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。:) :)