VirtualizingStackPanel has two modes. One of them is called ScrollToContent, and in this mode VirtualizingStackPanel uses element indices. As an example, 10 elements are visible, and you have 1000 items, so the ScrollBar will be displayed small.
The second mode is called ScrollToPixels, and in this mode, VirtualizingStackPanel manages a list of items that are virtualized and that are implemented. If the item has not yet been implemented, VirtualizingStackPanel uses the MinHeight value, which if none of them is set by the user is 16 pixels for Microsoft. As an example, 10 elements are visible, and each element has a height of 20 pixels. This will be 200 pixels for the height of the viewport, but you will have a total of 1000 elements, so the size will then be 200 + (1000 - 10) * 16 = 16040 pixels. ScrollBar will also seem small and suitable.
As a result, VirtualizingStackPanel is a complicated thing, and it works great basically. It also allows you to virtualize vertically and horizontally, which is amazing. If you want to write your own VirtualizationStackPanel, I suggest you stop reinventing the wheel. You will end up with the same as in the code, since the guys from Microsoft did it because the wait time was when someone else developed VirtualizingStackPanel :)
I flipped VirtualizingStackPanel using the RedGate tool. Take a look at this:
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 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; }
If you mirror ScrollViewer, you will find the following:
internal const double _scrollLineDelta = 16.0;
As you can see, the size is guessed when the container is not available, which means its set is 16.0 pixels, which is the default for Microsoft.
Btw pixel scrolling has been there in wpf since the beginning, since .Net 3.5 just sees a TreeView, for example. :) :)