6

我正在关注此处介绍的教程:

http://blogs.msdn.com/b/dancre/archive/tags/virtualizingtilepanel/

并且在他们的实现中,他们在 virtualizingtilepanel 上有一个依赖属性,可以跟踪孩子的大小。但是,WPF 库的 virtualizingstackpanel 不需要我将子元素的大小设置为属性。但是,我不明白虚拟化堆栈面板如何在不实例化项目的情况下计算面板中的哪些项目是可见的。

我认为他们需要一个度量通道来了解项目容器的大小,但是如果不先实例化这些项目,他们怎么能知道呢?

我的目标是创建一个面板来包含 treeviewitems 并虚拟化它们,但是按照 virtualizingtilepanel 中的示例只能让我虚拟化顶级项目。

我需要改变它计算哪些项目可见的方式,但我不知道它如何知道哪些项目是可见的而不知道大小并实际实例化这些项目。

编辑:等等,也许它会立即实例化进入 treeviewitem 的对象并使用它们来计算大小?

4

2 回答 2

3

VirtualizingStackPanel 项目不直接基于内容大小,它根据索引处理子项。
堆叠方向的范围完全由子项的数量决定。VirtualizingStackPanel 不知道一个孩子的大小,直到它意识到那个孩子。

随着视口滚动,下一个项目被实现,这就是为什么你不能使用虚拟化平滑滚动(除了添加像素滚动的.Net4.5)

有一个很棒的 4 部分博客关于VirtualizingPanel在 WPF 中创建一个,我用它来创建一个VirtualizingWrapPanel,它让我对如何Virtualization工作有了很好的理解

http://blogs.msdn.com/b/dancre/archive/2006/02/06/implementing-a-virtualized-panel-in-wpf-avalon.aspx

于 2013-10-16T00:15:10.503 回答
3

VirtualizingStackPanel 有两种模式。一种称为 ScrollToContent,在这种模式下,VirtualizingStackPanel 使用项目索引。作为示例可见的是 10 个项目,并且您有 1000 个项目,因此 ScrollBar 将显示得很小。

第二种模式称为 ScrollToPixels,在这种模式下,VirtualizingStackPanel 管理一个列表,其中列出了哪些项目被虚拟化,哪些项目被实现。如果项目尚未实现,则 VirtualizingStackPanel 将使用其 MinHeight 值,如果没有,则由用户为 microsoft 设置 16 像素。作为示例可见的是 10 个项目,每个项目的高度为 20 像素。视口高度为 200 像素,但您总共有 1000 个项目,因此范围将是 200 + (1000 - 10 ) * 16 = 16040 像素。ScrollBar 也会显得很小且比例适当。

最后,VirtualizingStackPanel 是一件复杂的事情,而且大部分时间都很好用。它还允许垂直和水平虚拟化,这很棒。如果您想编写自己的 VirtualizingStackPanel,我建议您停止重新发明轮子。你最终会在代码中做和微软人一样的事情,所以当其他人已经开发了 VirtualizingStackPanel 时为什么要浪费时间:)

我用 RedGate 工具反射了 VirtualizingStackPanel。看看这个:

    private Size ContainerSizeForItem(ItemsControl itemsControl, object item, int index, out UIElement container)
    {
        Size containerSize;
        container = index >= 0 ? ((ItemContainerGenerator)Generator).ContainerFromIndex(index) as UIElement : null;

        if (container != null)
        {
            containerSize = container.DesiredSize;
        }
        else
        {
            // It's virtualized; grab the height off the item if available.
            object value = itemsControl.ReadItemValue(item, _desiredSizeStorageIndex);
            if (value != null)
            {
                containerSize = (Size)value;
            }
            else
            {
                //
                // No stored container height; simply guess.
                //
                containerSize = new Size();


                if (Orientation == Orientation.Horizontal)
                {
                    containerSize.Width = ContainerStackingSizeEstimate(itemsControl, /*isHorizontal = */ true);
                    containerSize.Height = DesiredSize.Height;
                }
                else
                {
                    containerSize.Height = ContainerStackingSizeEstimate(itemsControl, /*isHorizontal = */ false);
                    containerSize.Width = DesiredSize.Width;
                }
            }
        }

        return containerSize;
    }

    private double ContainerStackingSizeEstimate(IProvideStackingSize estimate, bool isHorizontal)
    {
        double stackingSize = 0d;

        if (estimate != null)
        {
            stackingSize = estimate.EstimatedContainerSize(isHorizontal);
        }

        if (stackingSize <= 0d || DoubleUtil.IsNaN(stackingSize))
        {
            stackingSize = ScrollViewer._scrollLineDelta;
        }

        return stackingSize;
    }

如果您反映 ScrollViewer,您会发现:

internal const double _scrollLineDelta = 16.0;   // Default physical amount to scroll with one Up/Down

正如您所看到的,当容器不可用时会猜测大小,这意味着它设置为 16.0 像素,这是微软的默认值。

顺便说一句,从 .Net 3.5 开始,wpf 中就存在像素滚动,例如,仅查看 TreeView。:) :)

于 2013-10-16T06:45:07.703 回答