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

Jam*_*eet 6 c# wpf virtualization

我按照这里提供的教程:

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

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

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

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

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

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

dev*_*hog 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;
    }
Run Code Online (Sandbox Code Playgroud)

如果你反映 ScrollViewer 你会发现:

internal const double _scrollLineDelta = 16.0;   // Default physical amount to scroll with one Up/Down
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,自 .Net 3.5 开始,wpf 中就存在像素滚动,例如,参见 TreeView。:) :)