virtualizingstackpanel如何计算子大小而不实例化实际项目?

时间:2013-10-15 23:54:34

标签: c# wpf virtualization

我正在按照此处提供的教程进行操作:

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

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

我认为他们需要一个度量传递才能知道项目容器的大小,但是如果没有首先实例化这些项目,他们怎么知道呢?

我的目标是创建一个包含treeviewitems的面板并对其进行虚拟化,但是遵循virtualizingtilepanel中的示例只允许我虚拟化顶级项目。

我需要更改计算哪些项目可见的方式,但我不知道在不知道大小和实际实例化项目的情况下它如何知道哪些项目可见。

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

2 个答案:

答案 0 :(得分:3)

VirtualizingStackPanel项目不直接基于内容大小,它根据索引处理子项 堆叠方向的范围完全由儿童的数量决定。 VirtualizingStackPanel在知道孩子的大小之前不知道孩子的大小。

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

有很多关于在WPF中创建VirtualizingPanel的博客,我使用它来创建VirtualizingWrapPanel,这让我对Virtualization的工作方式有了很好的理解

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

答案 1 :(得分:3)

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

第二种模式称为ScrollToPixels,在此模式下,VirtualizingStackPanel管理虚拟化项目和实现项目的列表。如果某个项目尚未实现,VirtualizingStackPanel将使用其MinHeight值,如果没有用户设置为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像素,这是microsoft的默认值。

bpw像素滚动就在wpf中,因为从.Net 3.5开始也是如此,例如看到TreeView。 :):)