WPF/Metro-style: Making ListView show only complete items

前端 未结 3 500
长情又很酷
长情又很酷 2020-12-21 08:29

In my Metro application, I have a data source containing a certain number of items (say 25). I have a ListView that presents those items. My problem is that the ListView hav

相关标签:
3条回答
  • 2020-12-21 09:19

    My final solution was to combine the suggestions of @NovitchiS and @JesuX.

    I created a stack panel override, and listened to the LayoutUpdated event. My final solution:

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-21 09:26

    ListView inherits from ItemsControl, so one more optimized solution consists in injecting custom panel (overriding measure by custom clipping display) in ItemsPanel

    something like this(sorry, i did not try to compile):

    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;
    }
    
    0 讨论(0)
  • 2020-12-21 09:34

    The answer from @JesuX is the better approach -- if done correctly. The following ListView subclass works fine for me:

    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;
        }
    }
    

    One caveat -- if you plop an IntegralItemsListView into a Grid, it will have

    VerticalAlignment="Stretch"
    

    by default, which defeats the purpose of this class.

    Also: If the items are of uniform height, the method can obviously be simplified:

    protected override Size MeasureOverride(Size availableSize)
    {
        Size size = base.MeasureOverride(availableSize);
        size.Height = (int)(size.Height / ItemHeight) * ItemHeight;
        return size;
    }
    
    0 讨论(0)
提交回复
热议问题