2012-09-11 46 views
2

在我的Metro應用程序中,我有一個包含一定數量項目(比如說25)的數據源。我有一個ListView呈現這些項目。我的問題是,ListView的大小允許它顯示6.5項,這樣它顯示的最後一項就被減半了。如果分辨率發生變化,它可能會顯示4個項目,或8.2個項目,或其他任何內容。我想要的是,ListView精確顯示了適合控件高度的項目數量,而不是剪裁最後一個項目。WPF/Metro風格:讓ListView只顯示完整的項目

現在,我看到了兩個可能的半解決方案,其中沒有一個是最佳:

  1. 設置的ListView的高度,以一個固定的高度是項目大小的倍數。這不會隨着分辨率的變化而擴大。

  2. 限制數據源中的項目數。這也沒有擴展。

所以我的問題是,如何才能得到ListView控件只顯示完整的項目(其中所有邊緣都視/列表視圖中的項目),並隱藏其他人呢?

在此先感謝!

+1

我覺得選項#1,你提到的可以做一些額外的代碼來處理規模分辨率/方向/視圖狀態(填充,捕捉等)的變化。 – Krishna

+1

這裏要做的一件簡單的事情是,在運行時獲取列表的ActualHeight,然後獲取單個列表項目的ActualHeight,然後可以計算可用空間中可容納多少項目並更改列表的高度。我不確定這是否是最好的解決方案,但我確實使用過它並且對我來說工作得很好。 –

+0

@NovitchiS謝謝,這似乎是目前最簡單的方法...如果你發佈這個答案我會接受這個作爲我的問題的答案。 – Hallgeir

回答

2

我最終的解決方案是結合@NovitchiS和@JesuX的建議。

我創建了一個堆棧面板覆蓋,並監聽LayoutUpdated事件。我的最終解決方案:

class HeightLimitedStackPanel : StackPanel 
{ 
    public HeightLimitedStackPanel() : base() 
    { 
     this.LayoutUpdated += OnLayoutUpdated; 
    } 

    double GetSizeOfVisibleChildren(double parentHeight) 
    { 
     double currentSize = 0; 
     bool hasBreaked = false; 
     for (int i = 0; i < Children.Count; i++) 
     { 
      var child = Children[i]; 
      if (currentSize + child.DesiredSize.Height > parentHeight) 
      { 
       hasBreaked = true; 
       break; 
      } 
      currentSize += child.DesiredSize.Height; 
     } 
     if (hasBreaked) return currentSize; 

     return parentHeight; 
    } 

    double ParentHeight 
    { 
     get 
     { 
      ItemsPresenter parent = VisualTreeHelper.GetParent(this) as ItemsPresenter; 
      if (parent == null) 
       return 0; 

      return parent.ActualHeight; 
     } 
    } 

    double previousHeight = 0; 
    int previousChildCount = 0; 
    protected void OnLayoutUpdated(object sender, object e) 
    { 
     double height = ParentHeight; 
     if (height == previousHeight && previousChildCount == Children.Count) return; 
     previousHeight = height; 
     previousChildCount = Children.Count; 

     this.Height = GetSizeOfVisibleChildren(height); 
    } 
} 
3

的ListView自ItemsControl繼承, 所以一個更優化的解決方案包括在ItemsPanel

像這樣(對不起,我沒有試圖編譯)注入自定義面板(由自定義剪輯顯示重寫度量):

protected override Size MeasureOverride(Size constraint) 
{ 
if (this.VisualChildrenCount <= 0) 
    return base.MeasureOverride(constraint); 
var size = ne Size(constraint.Width,0); 
for(int i = 0; i < this.visualChildrenCount; i++) 
{ 
    child.Measure(size); 
    if(size.height + child.desiredSize > constraint.height) 
    break; 
    size.Height += child.DesiredSize; 
} 
return size; 
} 
+1

謝謝,但它似乎約束的高度設置爲無窮大,並且孩子的DesiredSize高度設置爲0後測量... – Hallgeir

0

@JesuX的答案是更好的方法 - 如果做得對。下面ListView子類正常工作對我來說:

public sealed class IntegralItemsListView : ListView 
{ 
    protected override Size MeasureOverride(Size availableSize) 
    { 
     Size size = base.MeasureOverride(availableSize); 
     double height = 0; 
     if (Items != null) 
     { 
      for (int i = 0; i < Items.Count; ++i) 
      { 
       UIElement itemContainer = (UIElement)ContainerFromIndex(i); 
       if (itemContainer == null) 
       { 
        break; 
       } 

       itemContainer.Measure(availableSize); 
       double childHeight = itemContainer.DesiredSize.Height; 
       if (height + childHeight > size.Height) 
       { 
        break; 
       } 

       height += childHeight; 
      } 
     } 

     size.Height = height; 
     return size; 
    } 
} 

一個警告 - 如果你撲通的IntegralItemsListViewGrid,它將有

VerticalAlignment="Stretch" 

默認情況下,這違背了這個類的目的。

另外:如果項目是高度統一的,該方法可以明顯被簡化:

protected override Size MeasureOverride(Size availableSize) 
{ 
    Size size = base.MeasureOverride(availableSize); 
    size.Height = (int)(size.Height/ItemHeight) * ItemHeight; 
    return size; 
}