3

在我的 Metro 应用程序中,我有一个包含一定数量项目(比如 25 个)的数据源。我有一个显示这些项目的 ListView。我的问题是 ListView 的大小允许它显示 6.5 个项目,因此它显示的最后一个项目被切成两半。如果分辨率发生变化,它可能会显示 4 个项目,或 8.2 个项目,或其他任何内容。我想要的是 ListView 准确显示适合控件高度的项目数,而不是剪裁最后一个项目。

现在,我看到了两种可能的半解决方案,但都不是最优的:

  1. 将 ListView 的高度设置为项目大小的倍数的固定高度。这不会随着分辨率的变化而缩放。

  2. 限制数据源中的项目数。这也不能扩展。

所以我的问题是,我怎样才能让 ListView 只显示完整的项目(所有边缘都在视口/列表视图内的项目),并隐藏其余的?

4

3 回答 3

4

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;
}
于 2012-09-11T08:24:08.273 回答
3

我的最终解决方案是结合@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);
    }
}
于 2012-09-14T07:24:18.123 回答
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;
    }
}

一个警告——如果你把 anIntegralItemsListView放入 aGrid中,它将有

VerticalAlignment="Stretch"

默认情况下,这违背了此类的目的。

另外:如果项目的高度一致,则该方法显然可以简化:

protected override Size MeasureOverride(Size availableSize)
{
    Size size = base.MeasureOverride(availableSize);
    size.Height = (int)(size.Height / ItemHeight) * ItemHeight;
    return size;
}
于 2016-01-20T13:30:52.837 回答