HypernovaDesktop Components · docs
Binarymission
WPF › Virtualizing Wrap Panel › BinaryVirtualizingWrapPanel

BinaryVirtualizingWrapPanel

class.NET 6 / 8 / 10.NET Framework 4.8Binarymission.WPF.Controls.Panels

A wrap panel for an items control that creates only the items in view: items flow along a line and wrap onto the next, as in a WrapPanel, but a ListBox of fifty thousand tiles scrolls as lightly as one of fifty.

public partial class BinaryVirtualizingWrapPanel : VirtualizingPanel, IScrollInfo, ISupportTransactionalUpdates

Inherits from / implements: VirtualizingPanel, IScrollInfo, ISupportTransactionalUpdates

Remarks

Use it as the ItemsPanel of a ListBox – or any items control whose template has a ScrollViewer with content scrolling on – wherever a long list reads better as tiles than as rows: photo galleries, product catalogues, file browsers, dashboards of cards.

How it virtualises

The panel is its own scroll owner (IScrollInfo). On each measure it works out from the offset and the viewport which items are in view, has containers created for those alone, and keeps or removes the ones that have left the view as VirtualizationSchemeOption says. Everything else exists only as data, in ItemsControl.ItemsSource.

Two layouts

By default each item gets a cell of ItemDimension, which you must set. A line holds as many cells as fit (see ItemPositioningMode) and an item's place is arithmetic on its index, so a jump to any item is cheap. With IsVariableItemSizeEnabled or IsDataSourceGrouped, a flow layout gives each item its own measured size instead.

Scrolling and grouping

Orientation gives rows that scroll down (the default) or columns that scroll across, a line – one item – at a time; ScrollToTheDesiredChild takes the user to any item. For groups, bind the items control to a VirtualizingCollectionView, which presents each group's header as a row of the flat list, and set IsDataSourceGrouped: the headers are laid out as GroupVirtualizationMode says and virtualised with the items.

Things to know

Examples

A product catalogue as 180 by 140 tiles; Products holds objects with Name and Price.

<ListBox xmlns:vwp="clr-namespace:Binarymission.WPF.Controls.Panels;assembly=Binarymission.WPF.Controls.VirtualizingWrapPanel"
         ItemsSource="{Binding Products}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <vwp:BinaryVirtualizingWrapPanel ItemDimension="180,140" ItemPositioningMode="Safe" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="8">
                <TextBlock Text="{Binding Name}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" />
                <TextBlock Text="{Binding Price, StringFormat=C}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

See also

Members at a glance

Constructors
BinaryVirtualizingWrapPanelCreates a panel that lays items out in rows and keeps every container it creates.
Properties
AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSourceGets or sets whether the panel refreshes the bound collection view whenever items are added, removed, moved or replaced.
CanHorizontallyScrollGets or sets whether the panel may scroll horizontally.
CanVerticallyScrollGets or sets whether the panel may scroll vertically.
CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRowGets or sets the space between a group's header and its first line of items, when it is not a whole item's height.
CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBeginsGets or sets the height of the blank line between the end of one group and the next group's header, in StandaloneLine mode.
DesiredChildIndexScrollingIsDataContextSynchronizedGets or sets how the panel settles after a jump to DesiredChildIndexToScrollTo.
DesiredChildIndexToScrollToGets or sets the index of the item the panel should bring into view – a jump straight to it, however far down the list it is.
ExtentHeightGets the height of all the content, shown or not – what the vertical scroll bar spans.
ExtentWidthGets the width of all the content, shown or not – what the horizontal scroll bar spans.
GroupHeaderItemCustomHeightGets or sets the height of a group header line – the size the header row's data template is drawn at.
GroupVirtualizationModeGets or sets how a grouped source is laid out: each group's header in line with the items, or starting a line of its own, with or without a blank line before it.
HorizontalOffsetGets how far the content is scrolled to the right.
IsAnimationEnabledGets or sets whether the panel plays PanelAnimationEffect as it scrolls a page or a wheel notch, and when it is laid out again.
IsAnimationEnabledWhenScrollToASpecificChildIndexGets or sets whether the panel fades briefly out and in again when it jumps to an item.
IsDataSourceGroupedGets or sets whether the items control's source is a grouped VirtualizingCollectionView, so that the panel lays out group headers among the items.
IsDynamicConstructionCapabilityEnabledGets or sets whether the panel uses the items control's actual size as its viewport when its parent measures it with unlimited space.
IsExtraMarginAtStartupRequiredGets or sets whether the panel adds ten units to every side of RendertimeMargin.
IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabledGets or sets whether a whole item's height of space separates a group's header from its first line of items.
IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabledGets or sets whether, when the lines are too short to hold two items, the space under each group header shrinks from a whole item's height to CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow.
IsVariableItemSizeEnabledGets or sets whether items may each be a different size, rather than all sharing the cell of ItemDimension.
ItemDimensionGets or sets the size of the cell each item is laid out in – and, with variable sizes, the size assumed for an item not yet measured.
ItemPositioningModeGets or sets what the fixed-size layout does with space at the end of a line too narrow for a whole item: leave it empty, or fit one more item, slightly clipped.
OrientationGets or sets the direction items run along a line before wrapping onto the next.
PanelAnimationEffectGets or sets the transition played as the panel scrolls, while IsAnimationEnabled is on.
ReRenderVisibilityRangeMaxCountGets or sets how many items, from the target onward, a jump to an item is meant to prepare.
RendertimeMarginGets or sets the margin the panel gives itself when it is loaded: the space between the edge of the items control and the lines of items.
ScrollOwnerGets or sets the scroll viewer that shows the panel's scroll bars and drives its scrolling.
ShouldAdjustRightHorizontalAlignmentSetupConsideringScrollInfoGets or sets whether right alignment (ShouldRecomputeViewportExtentSpacingUponAlignment) leaves room for a vertical scroll bar.
ShouldAutoAdjustParentItemsControlInternalPaddingGets or sets whether the panel trims the owning items control's padding so that the first line of items sits snugly in its top-left corner.
ShouldRecomputeViewportExtentSpacingUponAlignmentGets or sets whether the panel places its lines of items as its FrameworkElement.HorizontalAlignment says – centred, or against the right – by moving the space left at the end of each line into its own margin.
ShouldRenderExtraLineBeforeHeaderGets or sets whether, with StandaloneLine grouping (see GroupVirtualizationMode), a blank line comes before each group's header.
ShouldRenderItemHeightSpacingAfterAGroupHasBeenRenderedGets or sets whether the blank line before a group header, in StandaloneLine mode, is a whole item high.
ShouldShareUnusedSpaceEquallyOnHorizontalAlignmentGets or sets whether centring, under ShouldRecomputeViewportExtentSpacingUponAlignment, splits the space left over exactly in half.
ShouldUseRenderTimeMarginValuesWhenRecomputingViewPortExtentSpacingAdjustmentGets or sets whether the margin set for ShouldRecomputeViewportExtentSpacingUponAlignment keeps the top and bottom of RendertimeMargin.
VerticalOffsetGets how far the content is scrolled down.
ViewportHeightGets the height of the part of the content on show.
ViewportWidthGets the width of the part of the content on show.
VirtualizationSchemeOptionGets or sets what the panel does with the containers of items scrolled out of view.
VirtualizationSchemeWhenScrollingToASpecificChildIndexGets or sets what the panel does with the containers left out of view by a jump to an item.
Methods
ArrangeOverridePlaces each created item in its cell, or at its place in the flow layout.
LineDownScrolls on by one line: one item's height laid out horizontally, one item's width laid out vertically.
LineLeftScrolls left by one item's width.
LineRightScrolls right by one item's width.
LineUpScrolls back by one line: one item's height laid out horizontally, one item's width laid out vertically.
MakeVisibleScrolls the least distance that brings part of an item into the viewport.
MeasureOverrideWorks out which items are in view, creates and measures their containers, and reports the panel's size.
MouseWheelDownScrolls on by one line for a notch of the mouse wheel, as LineDown does.
MouseWheelLeftScrolls left by one item's width – the step for horizontal wheel scrolling – as LineLeft does.
MouseWheelRightScrolls right by one item's width – the step for horizontal wheel scrolling – as LineRight does.
MouseWheelUpScrolls back by one line for a notch of the mouse wheel, as LineUp does.
OnIsItemsHostChangedLays the panel out again when it becomes the items control's items host, which on a window's first show happens after its first measure.
OnItemsChangedDrops the containers of items removed, moved or replaced, and – with AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource – refreshes the view.
OnKeyDownScrolls one line for the arrow keys – Up and Down call LineUp and LineDown, Left and Right call LineLeft and LineRight – after the base class has seen the key.
OnRenderSizeChangedLays the items out again for the panel's new size, keeping the item that was first in view (or DesiredChildIndexToScrollTo, when set) in view.
PageDownScrolls on by one viewport: its height laid out horizontally, its width laid out vertically.
PageLeftScrolls left by the width of the viewport.
PageRightScrolls right by the width of the viewport.
PageUpScrolls back by one viewport: its height laid out horizontally, its width laid out vertically.
RefreshRefreshes the items control's collection view and lays the panel out again from the first item – call it after committing edits to the source, so that what is on screen matches the data.
ScrollToTheDesiredChildTakes the user straight to an item, however far away it is, choosing for this jump alone whether the panel fades as it lands and what happens to the containers left behind.
SetHorizontalOffsetScrolls the content to a horizontal position.
SetVerticalOffsetScrolls the content to a vertical position.
Events
AboutToRenderChildItemsOccurs when the panel is about to create the items for a new stretch of the list, giving the first and last index about to be shown.
Fields
CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRowPropertyIdentifies the CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow dependency property.
CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBeginsPropertyIdentifies the CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBegins dependency property.
DesiredChildIndexScrollingIsDataContextSynchronizedPropertyIdentifies the DesiredChildIndexScrollingIsDataContextSynchronized dependency property.
DesiredChildIndexToScrollToPropertyIdentifies the DesiredChildIndexToScrollTo dependency property.
GroupHeaderItemCustomHeightPropertyIdentifies the GroupHeaderItemCustomHeight dependency property.
GroupVirtualizationModePropertyIdentifies the GroupVirtualizationMode dependency property.
IsAnimationEnabledPropertyIdentifies the IsAnimationEnabled dependency property.
IsAnimationEnabledWhenScrollToASpecificChildIndexPropertyIdentifies the IsAnimationEnabledWhenScrollToASpecificChildIndex dependency property.
IsDataSourceGroupedPropertyIdentifies the IsDataSourceGrouped dependency property.
IsDynamicConstructionCapabilityEnabledPropertyIdentifies the IsDynamicConstructionCapabilityEnabled dependency property.
IsExtraMarginAtStartupRequiredPropertyIdentifies the IsExtraMarginAtStartupRequired dependency property.
IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabledPropertyIdentifies the IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabled dependency property.
IsTrimExtraLinkbreakForFirstRowEnabledPropertyIdentifies the IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabled dependency property.
IsVariableItemSizeEnabledPropertyIdentifies the IsVariableItemSizeEnabled dependency property.
ItemDimensionPropertyIdentifies the ItemDimension dependency property.
ItemPositioningModePropertyIdentifies the ItemPositioningMode dependency property.
OrientationPropertyIdentifies the Orientation dependency property.
PanelAnimationEffectPropertyIdentifies the PanelAnimationEffect dependency property.
ReRenderVisibilityRangeMaxCountPropertyIdentifies the ReRenderVisibilityRangeMaxCount dependency property.
RendertimeMarginPropertyIdentifies the RendertimeMargin dependency property.
ShouldAutoAdjustParentItemsControlInternalPaddingPropertyIdentifies the ShouldAutoAdjustParentItemsControlInternalPadding dependency property.
ShouldRenderExtraLineBeforeHeaderPropertyIdentifies the ShouldRenderExtraLineBeforeHeader dependency property.
ShouldRenderItemHeightSpacingAfterAGroupHasBeenRenderedPropertyIdentifies the ShouldRenderItemHeightSpacingAfterAGroupHasBeenRendered dependency property.
VirtualizationSchemeOptionPropertyIdentifies the VirtualizationSchemeOption dependency property.
VirtualizationSchemeWhenScrollingToASpecificChildIndexPropertyIdentifies the VirtualizationSchemeWhenScrollingToASpecificChildIndex dependency property.

Constructors

BinaryVirtualizingWrapPanel

public BinaryVirtualizingWrapPanel()

Creates a panel that lays items out in rows and keeps every container it creates.

Remarks

The items control normally creates it from an ItemsPanelTemplate; set ItemDimension before it is shown.

Properties

AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource

public bool AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource

Gets or sets whether the panel refreshes the bound collection view whenever items are added, removed, moved or replaced.

Value
The default is false.
Remarks

Each change then does what Refresh does: refreshes the ICollectionView in ItemsControl.ItemsSource (nothing, when the source is not a collection view), measures again and scrolls back to the first item. For a list edited in batches, leave it off and call Refresh after the batch.

CanHorizontallyScroll

public bool CanHorizontallyScroll

Gets or sets whether the panel may scroll horizontally.

Value
Written by the ScrollOwner from its horizontal scroll bar setting; false until it does.
Remarks

While there is a scroll owner and this is false, SetHorizontalOffset is ignored. With no scroll owner, offsets are always applied.

CanVerticallyScroll

public bool CanVerticallyScroll

Gets or sets whether the panel may scroll vertically.

Value
Written by the ScrollOwner from its vertical scroll bar setting; false until it does.
Remarks

While there is a scroll owner and this is false, SetVerticalOffset is ignored. See CanHorizontallyScroll.

CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow

public double CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow

Gets or sets the space between a group's header and its first line of items, when it is not a whole item's height.

Value
In device-independent units (1/96 inch); the default is 8. A negative value counts as 0.
Remarks

CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBegins

public double CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBegins

Gets or sets the height of the blank line between the end of one group and the next group's header, in StandaloneLine mode.

Value
In device-independent units (1/96 inch); the default is 48. A negative value counts as 0.
Remarks

Used with GroupVirtualizationMode set to StandaloneLine, ShouldRenderExtraLineBeforeHeader on and ShouldRenderItemHeightSpacingAfterAGroupHasBeenRendered off.

A value set while IsDataSourceGrouped is true and ShouldRenderItemHeightSpacingAfterAGroupHasBeenRendered is false is held to at most 80 per cent of the height of ItemDimension (or to 2, when that height is zero or less). Set ItemDimension first.

DesiredChildIndexScrollingIsDataContextSynchronized

public bool DesiredChildIndexScrollingIsDataContextSynchronized

Gets or sets how the panel settles after a jump to DesiredChildIndexToScrollTo.

Value
The default is false.
Remarks

Despite its name, it does not follow the collection view's current item. After a jump the panel steps one line back, forward and back again so that the lines round the target are created. With this false, that happens only when no index, or index 0, is asked for; with it true, whenever the target is more than two lines from the end.

DesiredChildIndexToScrollTo

public int DesiredChildIndexToScrollTo

Gets or sets the index of the item the panel should bring into view – a jump straight to it, however far down the list it is.

Value
The zero-based index in the items control's Items; the default, -1, asks for no particular item.
Exceptions
System.InvalidOperationException
The value is changed while IsDataSourceGrouped is true: jumping to an index is not available with a grouped source.
Remarks

Setting an index of zero or more scrolls so that the line holding that item is near the top of the viewport, creating only the items around it. The value stays set, and the panel keeps that item in view when its size changes. While it is -1, the panel sets it itself after the viewport is resized, to the item that was first in view, so that the user keeps their place. Setting the value it already has does nothing – call ScrollToTheDesiredChild to repeat a jump. An index at or past the end of the list does not move the view to a new place.

Jumping relies on the fixed-size layout, where an item's place follows from its index. With IsVariableItemSizeEnabled on, the panel cannot work out where the item is and does not take the user to it.

Example

Keep the panel on the photo the view model has selected:

<vwp:BinaryVirtualizingWrapPanel ItemDimension="160,160"
                                 DesiredChildIndexToScrollTo="{Binding SelectedPhotoIndex}" />
See also

ExtentHeight

public double ExtentHeight

Gets the height of all the content, shown or not – what the vertical scroll bar spans.

Value
In device-independent units (1/96 inch). In the fixed-size layout, laid out horizontally, it is the number of lines times the height of ItemDimension.

ExtentWidth

public double ExtentWidth

Gets the width of all the content, shown or not – what the horizontal scroll bar spans.

Value
In device-independent units (1/96 inch). In the fixed-size layout, laid out horizontally, it is the width of one full line of items.

GroupHeaderItemCustomHeight

public double GroupHeaderItemCustomHeight

Gets or sets the height of a group header line – the size the header row's data template is drawn at.

Value
In device-independent units (1/96 inch); the default is 24. A negative value counts as 0.
Remarks

Applies in the LineBreak and StandaloneLine modes, where the header is as wide as the line; laid out vertically, it is the header's height at the top of its column. In Continuation mode a header takes one item cell instead.

GroupVirtualizationMode

public GroupVirtualizationMode GroupVirtualizationMode

Gets or sets how a grouped source is laid out: each group's header in line with the items, or starting a line of its own, with or without a blank line before it.

Value
The default is LineBreak.
Remarks

Used while IsDataSourceGrouped is true.

  • Continuation — A header takes one item cell, and the groups run on along the same lines.
  • LineBreak — A header starts a line of its own, as long as the line and GroupHeaderItemCustomHeight high, and its items start on the next line. 8 units separate a group from the next header.
  • StandaloneLine — As LineBreak, with a blank line before each header – see ShouldRenderExtraLineBeforeHeader.

Laid out vertically, a header heads its column instead, GroupHeaderItemCustomHeight tall. A change lays the panel out again from the first item.

HorizontalOffset

public double HorizontalOffset

Gets how far the content is scrolled to the right.

Value
In device-independent units (1/96 inch) from the left of the content; 0 at the start.
See also

IsAnimationEnabled

public bool IsAnimationEnabled

Gets or sets whether the panel plays PanelAnimationEffect as it scrolls a page or a wheel notch, and when it is laid out again.

Value
true to play the effect; the default is false.
Remarks

The effect plays on PageUp, PageDown, PageLeft, PageRight, the mouse-wheel steps, LineLeft and LineRight, and when the panel is resized or refreshed – not on LineUp or LineDown, nor while the scroll bar's thumb is dragged.

Example
<vwp:BinaryVirtualizingWrapPanel ItemDimension="180,140"
                                 IsAnimationEnabled="True"
                                 PanelAnimationEffect="BlurToNormal" />
See also

IsAnimationEnabledWhenScrollToASpecificChildIndex

public bool IsAnimationEnabledWhenScrollToASpecificChildIndex

Gets or sets whether the panel fades briefly out and in again when it jumps to an item.

Value
true for the 300-millisecond fade; the default is false.
Remarks

ScrollToTheDesiredChild sets this for its own jump from its argument, and restores it.

IsDataSourceGrouped

public bool IsDataSourceGrouped

Gets or sets whether the items control's source is a grouped VirtualizingCollectionView, so that the panel lays out group headers among the items.

Value
true for a grouped source; the default is false.
Remarks

A VirtualizingCollectionView with group descriptions presents each group's header as a row of its own, ahead of the group's items, whose data is the CollectionViewGroup – its Name and ItemCount are there to bind to. With this set, the panel uses the flow layout, starts each group as GroupVirtualizationMode says, and virtualises headers and items together. Bind to an ordinary grouped view instead and WPF generates a group item per group, which this panel cannot virtualise.

While grouped, VirtualizationSchemeOption is held at VirtualizationScheme.LightWeightCache, and jumping to an index (DesiredChildIndexToScrollTo, ScrollToTheDesiredChild) is not available. A change lays the panel out again from the first item.

Example

A catalogue grouped by category. Two implicit data templates draw the rows: one for the header rows (a CollectionViewGroup) and one for the products.

<Window x:Class="Catalogue.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Catalogue"
        xmlns:vwp="clr-namespace:Binarymission.WPF.Controls.Panels;assembly=Binarymission.WPF.Controls.VirtualizingWrapPanel"
        Title="Catalogue" Width="960" Height="640">
    <ListBox x:Name="catalogue">
        <ListBox.Resources>
            <DataTemplate DataType="{x:Type CollectionViewGroup}">
                <TextBlock FontSize="16" FontWeight="Bold" VerticalAlignment="Center">
                    <Run Text="{Binding Name, Mode=OneWay}" />
                    (<Run Text="{Binding ItemCount, Mode=OneWay}" /> products)
                </TextBlock>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:Product}">
                <StackPanel Margin="8">
                    <TextBlock Text="{Binding Name}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" />
                    <TextBlock Text="{Binding Price, StringFormat=C}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.Resources>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <vwp:BinaryVirtualizingWrapPanel ItemDimension="180,120"
                                                 IsDataSourceGrouped="True"
                                                 GroupVirtualizationMode="LineBreak"
                                                 GroupHeaderItemCustomHeight="32" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Window>
using System.ComponentModel;
using System.Windows.Data;
using Binarymission.WPF.Data.DataExtensions;

// products is a List<Product>; Product has Name, Price and Category.
var view = new VirtualizingCollectionView(products);
view.SortDescriptions.Add(new SortDescription(nameof(Product.Category), ListSortDirection.Ascending));
view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Product.Category)));
catalogue.ItemsSource = view;
See also

IsDynamicConstructionCapabilityEnabled

public bool IsDynamicConstructionCapabilityEnabled

Gets or sets whether the panel uses the items control's actual size as its viewport when its parent measures it with unlimited space.

Value
true to fall back on the items control's size; the default is false.
Remarks

Measured with infinite width or height – inside a StackPanel, say – the panel has no viewport to virtualise against. With this on, it uses the items control's ActualWidth and ActualHeight instead, so give the items control a size of its own.

IsExtraMarginAtStartupRequired

public bool IsExtraMarginAtStartupRequired

Gets or sets whether the panel adds ten units to every side of RendertimeMargin.

Value
true for the larger margin; the default is false.
Remarks

Applied when the panel is loaded, and at once when this changes, which also scrolls to the first item.

IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabled

public bool IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabled

Gets or sets whether a whole item's height of space separates a group's header from its first line of items.

Value
true, the default, for one item's height (its width, laid out vertically); false for CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow.
Remarks

Applies in the LineBreak and StandaloneLine modes. When the lines are too short for two items and IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabled is set, the smaller custom spacing is used even with this on.

IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabled

public bool IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabled

Gets or sets whether, when the lines are too short to hold two items, the space under each group header shrinks from a whole item's height to CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow.

Value
true, the default, to shrink it; false to keep the whole item's height.
Remarks

A narrow panel showing one item per line would otherwise put a large gap under every header. It matters only while IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabled is on.

IsVariableItemSizeEnabled

public bool IsVariableItemSizeEnabled

Gets or sets whether items may each be a different size, rather than all sharing the cell of ItemDimension.

Value
false, the default, lays every item out in a cell of ItemDimension; true gives each item the size it asks for.
Remarks

With variable sizes, items flow along each line at their own width (their own height, laid out vertically) and wrap when the line is full; a line is as tall as its tallest item. An item is measured when it first comes near the view, and its size is remembered; until then ItemDimension is its estimate, so set that to a typical item's size and the scroll bar will be close from the start. Grouping works with variable sizes too.

ItemPositioningMode does not apply, and jumping to an index (DesiredChildIndexToScrollTo) does not take the user to the item. Turning this on or off forgets the sizes measured so far.

Example

Notes of different lengths, each as wide as its text needs, up to 320 units:

<ListBox ItemsSource="{Binding Notes}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <vwp:BinaryVirtualizingWrapPanel IsVariableItemSizeEnabled="True" ItemDimension="200,90" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border Padding="10" MaxWidth="320" Background="LightYellow">
                <TextBlock Text="{Binding Text}" TextWrapping="Wrap" />
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

ItemDimension

public Size ItemDimension

Gets or sets the size of the cell each item is laid out in – and, with variable sizes, the size assumed for an item not yet measured.

Value
Width and height in device-independent units (1/96 inch); the default, infinite both ways, means "not set".
Remarks

In the fixed-size layout every item is measured and arranged in a cell of this size – size it for the largest item – and the panel knows from an index alone where an item is, which is what lets it virtualise a huge list without measuring it. Left at the default, the fixed-size layout shows nothing; the flow layout estimates 100 by 100. A line of scrolling is one cell. A change lays the panel out again from the first item. In XAML: ItemDimension="200,160".

ItemPositioningMode

public ItemPositioningMode ItemPositioningMode

Gets or sets what the fixed-size layout does with space at the end of a line too narrow for a whole item: leave it empty, or fit one more item, slightly clipped.

Value
The default is Safe.
Remarks

Safe puts whole items only on a line, so every item seen is complete. Greedy takes one more item when the space left is at least 85 per cent of an item; that last item is clipped by at most fifteen per cent of its width (its height, laid out vertically).

It applies to the fixed-size layout. The flow layout, used with IsVariableItemSizeEnabled or IsDataSourceGrouped, wraps each item at its own size and does not read it. A change re-counts the lines and keeps the scroll position.

Orientation

public Orientation Orientation

Gets or sets the direction items run along a line before wrapping onto the next.

Value
Horizontal, the default: rows filled left to right, wrapping downward, scrolled vertically. Vertical: columns filled top to bottom, wrapping to the right, scrolled horizontally.
Remarks

A change scrolls the panel back to the first item.

PanelAnimationEffect

public AnimationEffect PanelAnimationEffect

Gets or sets the transition played as the panel scrolls, while IsAnimationEnabled is on.

Value
The default is AnimationEffect.None.
Remarks

Keep transitions for galleries and showcases: they play on every page and wheel step, and a blur over a large panel costs rendering time on every frame it plays.

ReRenderVisibilityRangeMaxCount

public int ReRenderVisibilityRangeMaxCount

Gets or sets how many items, from the target onward, a jump to an item is meant to prepare.

Value
The default is 100; between 15 and 175 is sensible.
Remarks

The items created after a jump are those in view at the new position; this value does not change them.

RendertimeMargin

public Thickness RendertimeMargin

Gets or sets the margin the panel gives itself when it is loaded: the space between the edge of the items control and the lines of items.

Value
In device-independent units (1/96 inch); the default is 10,0,0,10, 10 on the left and bottom.
Remarks

The panel copies this to its own FrameworkElement.Margin when it is loaded and when IsExtraMarginAtStartupRequired changes.

ScrollOwner

public ScrollViewer ScrollOwner

Gets or sets the scroll viewer that shows the panel's scroll bars and drives its scrolling.

Value
The ScrollViewer in the items control's template, set by that scroll viewer when it finds the panel; null until then.
Remarks

Part of IScrollInfo; there is normally no reason to set it yourself. The scroll viewer must have CanContentScroll on (a ListBox has) for it to hand scrolling to the panel.

ShouldAdjustRightHorizontalAlignmentSetupConsideringScrollInfo

public bool ShouldAdjustRightHorizontalAlignmentSetupConsideringScrollInfo

Gets or sets whether right alignment (ShouldRecomputeViewportExtentSpacingUponAlignment) leaves room for a vertical scroll bar.

Value
true, the default, to add VerticalScrollBarWidth to the left margin; false to put the lines hard against the right.

ShouldAutoAdjustParentItemsControlInternalPadding

public bool ShouldAutoAdjustParentItemsControlInternalPadding

Gets or sets whether the panel trims the owning items control's padding so that the first line of items sits snugly in its top-left corner.

Value
true to trim it; the default is false.
Remarks

When set, the owner's Padding loses three units on the left and nine at the top – once per owner, however often the panel is loaded – so the left-most item's border is not cut off and no gap shows above the first line. Setting it back gives them back. A change scrolls back to the first item.

ShouldRecomputeViewportExtentSpacingUponAlignment

public bool ShouldRecomputeViewportExtentSpacingUponAlignment

Gets or sets whether the panel places its lines of items as its FrameworkElement.HorizontalAlignment says – centred, or against the right – by moving the space left at the end of each line into its own margin.

Value
true to align the lines; the default, false, starts them at the left with RendertimeMargin.
Remarks

Only Center and Right are acted on, and only when the viewport is wider than ExtentWidth. The margin is worked out when this becomes true and each time the panel is loaded, not as the panel is resized. Setting it back to false restores RendertimeMargin. See also ShouldShareUnusedSpaceEquallyOnHorizontalAlignment.

ShouldRenderExtraLineBeforeHeader

public bool ShouldRenderExtraLineBeforeHeader

Gets or sets whether, with StandaloneLine grouping (see GroupVirtualizationMode), a blank line comes before each group's header.

Value
true, the default, for the blank line; false for the 8-unit gap of LineBreak.
Remarks

ShouldRenderItemHeightSpacingAfterAGroupHasBeenRendered

public bool ShouldRenderItemHeightSpacingAfterAGroupHasBeenRendered

Gets or sets whether the blank line before a group header, in StandaloneLine mode, is a whole item high.

Value
true for one item's height (its width, laid out vertically); the default, false, uses CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBegins.
Remarks

Applies only with GroupVirtualizationMode set to StandaloneLine and ShouldRenderExtraLineBeforeHeader on.

ShouldShareUnusedSpaceEquallyOnHorizontalAlignment

public bool ShouldShareUnusedSpaceEquallyOnHorizontalAlignment

Gets or sets whether centring, under ShouldRecomputeViewportExtentSpacingUponAlignment, splits the space left over exactly in half.

Value
true for equal halves; the default, false, moves the lines a further VerticalScrollBarWidth to the right.

ShouldUseRenderTimeMarginValuesWhenRecomputingViewPortExtentSpacingAdjustment

public bool ShouldUseRenderTimeMarginValuesWhenRecomputingViewPortExtentSpacingAdjustment

Gets or sets whether the margin set for ShouldRecomputeViewportExtentSpacingUponAlignment keeps the top and bottom of RendertimeMargin.

Value
true to keep them; the default, false, makes them zero.

VerticalOffset

public double VerticalOffset

Gets how far the content is scrolled down.

Value
In device-independent units (1/96 inch) from the top of the content; 0 at the start.
See also

ViewportHeight

public double ViewportHeight

Gets the height of the part of the content on show.

Value
In device-independent units (1/96 inch): the height the scroll viewer gives the panel.

ViewportWidth

public double ViewportWidth

Gets the width of the part of the content on show.

Value
In device-independent units (1/96 inch): the width the scroll viewer gives the panel.

VirtualizationSchemeOption

public VirtualizationScheme VirtualizationSchemeOption

Gets or sets what the panel does with the containers of items scrolled out of view.

Value
The default is VirtualizationScheme.LightWeightCache.
Remarks

While IsDataSourceGrouped is true, any other value is set back to VirtualizationScheme.LightWeightCache at once. A jump to an item uses VirtualizationSchemeWhenScrollingToASpecificChildIndex instead. A change scrolls to the first item.

VirtualizationSchemeWhenScrollingToASpecificChildIndex

public VirtualizationScheme VirtualizationSchemeWhenScrollingToASpecificChildIndex

Gets or sets what the panel does with the containers left out of view by a jump to an item.

Value
The default is VirtualizationScheme.DisposeOnLeavingViewPortBounds.
Remarks

Used for the layout passes of a jump only; ordinary scrolling uses VirtualizationSchemeOption. ScrollToTheDesiredChild sets it for its own jump and restores it.

Methods

ArrangeOverride

protected override Size ArrangeOverride(Size finalSize)

Places each created item in its cell, or at its place in the flow layout.

Parameters
finalSize
The space the panel has been given.
Returns

finalSize.

LineDown

public void LineDown()

Scrolls on by one line: one item's height laid out horizontally, one item's width laid out vertically.

Remarks

The Down arrow key and the vertical scroll bar's down arrow call it. It stops at the end of the content.

See also

LineLeft

public void LineLeft()

Scrolls left by one item's width.

Remarks

It moves the view only when the content is wider than the viewport – in practice, with Orientation set to Vertical. Plays PanelAnimationEffect when IsAnimationEnabled is on.

LineRight

public void LineRight()

Scrolls right by one item's width.

Remarks

It moves the view only when the content is wider than the viewport – in practice, with Orientation set to Vertical. Plays PanelAnimationEffect when IsAnimationEnabled is on.

LineUp

public void LineUp()

Scrolls back by one line: one item's height laid out horizontally, one item's width laid out vertically.

Remarks

The Up arrow key and the vertical scroll bar's up arrow call it. It stops at the start of the content.

See also

MakeVisible

public Rect MakeVisible(Visual visual, Rect rectangle)

Scrolls the least distance that brings part of an item into the viewport.

Parameters
visual
The element to bring into view: an item's container, or something inside one.
rectangle
The part of visual to show, in its own coordinates.
Returns

rectangle, unchanged.

Remarks

WPF calls this for FrameworkElement.BringIntoView, for keyboard focus moving to an item, and for ListBox.ScrollIntoView. It works in every layout, because it reads the item's arranged position. It can only reach an item whose container exists; to reach any item by index, use ScrollToTheDesiredChild. A visual that is not inside the panel is ignored.

MeasureOverride

protected override Size MeasureOverride(Size availableSize)

Works out which items are in view, creates and measures their containers, and reports the panel's size.

Parameters
availableSize
The space offered, normally the viewport. When it is infinite along an axis, see IsDynamicConstructionCapabilityEnabled.
Returns

The whole of the space offered (or the items control's size used in its place).

MouseWheelDown

public void MouseWheelDown()

Scrolls on by one line for a notch of the mouse wheel, as LineDown does.

Remarks

MouseWheelLeft

public void MouseWheelLeft()

Scrolls left by one item's width – the step for horizontal wheel scrolling – as LineLeft does.

MouseWheelRight

public void MouseWheelRight()

Scrolls right by one item's width – the step for horizontal wheel scrolling – as LineRight does.

MouseWheelUp

public void MouseWheelUp()

Scrolls back by one line for a notch of the mouse wheel, as LineUp does.

Remarks

OnIsItemsHostChanged

protected override void OnIsItemsHostChanged(bool oldIsItemsHost, bool newIsItemsHost)

Lays the panel out again when it becomes the items control's items host, which on a window's first show happens after its first measure.

Parameters
oldIsItemsHost
Whether the panel was the items host before.
newIsItemsHost
Whether it is now.

OnItemsChanged

protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)

Drops the containers of items removed, moved or replaced, and – with AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource – refreshes the view.

Parameters
sender
The items control's generator.
args
What changed, and where.

OnKeyDown

protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)

Scrolls one line for the arrow keys – Up and Down call LineUp and LineDown, Left and Right call LineLeft and LineRight – after the base class has seen the key.

Parameters
e
The key pressed. It is not marked handled, so the items control still sees it.

OnRenderSizeChanged

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)

Lays the items out again for the panel's new size, keeping the item that was first in view (or DesiredChildIndexToScrollTo, when set) in view.

Parameters
sizeInfo
The old and new size.

PageDown

public void PageDown()

Scrolls on by one viewport: its height laid out horizontally, its width laid out vertically.

Remarks

PageLeft

public void PageLeft()

Scrolls left by the width of the viewport.

Remarks

PageRight

public void PageRight()

Scrolls right by the width of the viewport.

Remarks

PageUp

public void PageUp()

Scrolls back by one viewport: its height laid out horizontally, its width laid out vertically.

Remarks

Refresh

public void Refresh()

Refreshes the items control's collection view and lays the panel out again from the first item – call it after committing edits to the source, so that what is on screen matches the data.

Remarks

When the items control's ItemsControl.ItemsSource is an ICollectionView (a VirtualizingCollectionView or a ListCollectionView, say), the view is refreshed – re-sorted, re-filtered and regrouped - the panel is measured again and both offsets go back to 0. With any other source it does nothing.

This is the panel's implementation of ISupportTransactionalUpdates.Refresh. AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource calls it on every change instead.

Example
// Mark a batch of orders as shipped through the editable view, then bring the tiles up to date.
var view = (IEditableCollectionView)ordersList.ItemsSource;
foreach (Order order in selectedOrders)
{
    view.EditItem(order);
    order.Status = "Shipped";
    view.CommitEdit();
}

panel.Refresh();

ScrollToTheDesiredChild

public void ScrollToTheDesiredChild(int desiredChildIndexToScrollTo, bool performAnimation, VirtualizationScheme virtualizationScheme)

Takes the user straight to an item, however far away it is, choosing for this jump alone whether the panel fades as it lands and what happens to the containers left behind.

Parameters
desiredChildIndexToScrollTo
The zero-based index of the item in the items control's Items. -1 asks for no particular item; an index at or past the end does not move the view to a new place.
performAnimation
true to fade the panel out and back in (300 milliseconds) as it lands; false to jump without it.
virtualizationScheme
What to do with the containers of the items left out of view by this jump. VirtualizationScheme.DisposeOnLeavingViewPortBounds keeps memory flat after a long jump.
Exceptions
System.InvalidOperationException
IsDataSourceGrouped is true: jumping to an index is not available with a grouped source.
Remarks

The jump is made at once, by setting DesiredChildIndexToScrollTo and laying the panel out. IsAnimationEnabledWhenScrollToASpecificChildIndex and VirtualizationSchemeWhenScrollingToASpecificChildIndex are set from the arguments for the jump and restored afterwards. Unlike setting DesiredChildIndexToScrollTo, calling this with the same index again jumps again – after the user has scrolled away, say.

Example

An order board: the panel is kept when it loads (it is created by the items control, from the template), and a "Go to order" button takes the clerk to the order typed in.

<ListBox x:Name="ordersList" ItemsSource="{Binding Orders}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <vwp:BinaryVirtualizingWrapPanel ItemDimension="220,110" Loaded="OnPanelLoaded" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>
using Binarymission.WPF.Controls.Panels;

private BinaryVirtualizingWrapPanel _panel;

private void OnPanelLoaded(object sender, RoutedEventArgs e)
{
    _panel = (BinaryVirtualizingWrapPanel)sender;
}

private void OnGoToOrderClick(object sender, RoutedEventArgs e)
{
    int index = _orders.FindIndex(order => order.Number == orderNumberBox.Text);
    if (index >= 0 && _panel != null)
    {
        _panel.ScrollToTheDesiredChild(index, true,
            BinaryVirtualizingWrapPanel.VirtualizationScheme.DisposeOnLeavingViewPortBounds);
    }
}
See also

SetHorizontalOffset

public void SetHorizontalOffset(double offset)

Scrolls the content to a horizontal position.

Parameters
offset
The distance from the left of the content, in device-independent units (1/96 inch). It is held between 0 and ExtentWidth less ViewportWidth; when the content is no wider than the viewport, it is 0.
Remarks

Ignored while there is a ScrollOwner and CanHorizontallyScroll is false. The items now in view are created on the next layout pass.

SetVerticalOffset

public void SetVerticalOffset(double offset)

Scrolls the content to a vertical position.

Parameters
offset
The distance from the top of the content, in device-independent units (1/96 inch). It is held between 0 and ExtentHeight less ViewportHeight; when the content is no taller than the viewport, it is 0.
Remarks

Ignored while there is a ScrollOwner and CanVerticallyScroll is false. The items now in view are created on the next layout pass.

Example
// Back to the top of the gallery.
panel.SetVerticalOffset(0);

Events

AboutToRenderChildItems

public event EventHandler<AboutToRenderChildItemsEventArgs> AboutToRenderChildItems

Occurs when the panel is about to create the items for a new stretch of the list, giving the first and last index about to be shown.

Remarks

Raised from the measure pass of the fixed-size layout whenever the first index in view changes – not during a jump to an item, and not in the flow layout (variable sizes or grouping). Use it to fetch what those items need, or to show where in the list the user is. Changing ItemDimension from the handler lays the panel out again from the first item.

See also

Fields

CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRowProperty static

public static readonly DependencyProperty CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRowProperty

Identifies the CustomExtraSpacingBetweenGroupHeaderRowAndDataItemRow dependency property.

CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBeginsProperty static

public static readonly DependencyProperty CustomExtraSpacingBetweenGroupRenderingCompletionAndGroupHeaderRowRenderingBeginsProperty

DesiredChildIndexScrollingIsDataContextSynchronizedProperty static

public static readonly DependencyProperty DesiredChildIndexScrollingIsDataContextSynchronizedProperty

Identifies the DesiredChildIndexScrollingIsDataContextSynchronized dependency property.

DesiredChildIndexToScrollToProperty static

public static readonly DependencyProperty DesiredChildIndexToScrollToProperty

Identifies the DesiredChildIndexToScrollTo dependency property.

GroupHeaderItemCustomHeightProperty static

public static readonly DependencyProperty GroupHeaderItemCustomHeightProperty

Identifies the GroupHeaderItemCustomHeight dependency property.

GroupVirtualizationModeProperty static

public static readonly DependencyProperty GroupVirtualizationModeProperty

Identifies the GroupVirtualizationMode dependency property.

IsAnimationEnabledProperty static

public static readonly DependencyProperty IsAnimationEnabledProperty

Identifies the IsAnimationEnabled dependency property.

IsAnimationEnabledWhenScrollToASpecificChildIndexProperty static

public static readonly DependencyProperty IsAnimationEnabledWhenScrollToASpecificChildIndexProperty

Identifies the IsAnimationEnabledWhenScrollToASpecificChildIndex dependency property.

IsDataSourceGroupedProperty static

public static readonly DependencyProperty IsDataSourceGroupedProperty

Identifies the IsDataSourceGrouped dependency property.

IsDynamicConstructionCapabilityEnabledProperty static

public static readonly DependencyProperty IsDynamicConstructionCapabilityEnabledProperty

Identifies the IsDynamicConstructionCapabilityEnabled dependency property.

IsExtraMarginAtStartupRequiredProperty static

public static readonly DependencyProperty IsExtraMarginAtStartupRequiredProperty = DependencyProperty.Register("IsExtraMarginAtStartupRequired", typeof(bool), typeof(BinaryVirtualizingWrapPanel), new UIPropertyMetadata(false, (o, e)

Identifies the IsExtraMarginAtStartupRequired dependency property.

IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabledProperty static

public static readonly DependencyProperty IsRenderItemDimensionHeightSpacingBetweenGroupHeaderAndDataItemRowEnabledProperty

IsTrimExtraLinkbreakForFirstRowEnabledProperty static

public static readonly DependencyProperty IsTrimExtraLinkbreakForFirstRowEnabledProperty

Identifies the IsTrimExtraLinkbreakForLeadingRowWithOneChildEnabled dependency property.

IsVariableItemSizeEnabledProperty static

public static readonly DependencyProperty IsVariableItemSizeEnabledProperty

Identifies the IsVariableItemSizeEnabled dependency property.

ItemDimensionProperty static

public static readonly DependencyProperty ItemDimensionProperty

Identifies the ItemDimension dependency property.

ItemPositioningModeProperty static

public static DependencyProperty ItemPositioningModeProperty

Identifies the ItemPositioningMode dependency property.

OrientationProperty static

public static readonly DependencyProperty OrientationProperty

Identifies the Orientation dependency property.

PanelAnimationEffectProperty static

public static readonly DependencyProperty PanelAnimationEffectProperty

Identifies the PanelAnimationEffect dependency property.

ReRenderVisibilityRangeMaxCountProperty static

public static readonly DependencyProperty ReRenderVisibilityRangeMaxCountProperty

Identifies the ReRenderVisibilityRangeMaxCount dependency property.

RendertimeMarginProperty static

public static readonly DependencyProperty RendertimeMarginProperty

Identifies the RendertimeMargin dependency property.

ShouldAutoAdjustParentItemsControlInternalPaddingProperty static

public static readonly DependencyProperty ShouldAutoAdjustParentItemsControlInternalPaddingProperty = DependencyProperty.Register("ShouldAutoAdjustParentItemsControlInternalPadding", typeof(bool), typeof(BinaryVirtualizingWrapPanel), new UIPropertyMetadata(false, (o, e)

Identifies the ShouldAutoAdjustParentItemsControlInternalPadding dependency property.

ShouldRenderExtraLineBeforeHeaderProperty static

public static DependencyProperty ShouldRenderExtraLineBeforeHeaderProperty

Identifies the ShouldRenderExtraLineBeforeHeader dependency property.

ShouldRenderItemHeightSpacingAfterAGroupHasBeenRenderedProperty static

public static readonly DependencyProperty ShouldRenderItemHeightSpacingAfterAGroupHasBeenRenderedProperty

VirtualizationSchemeOptionProperty static

public static readonly DependencyProperty VirtualizationSchemeOptionProperty

Identifies the VirtualizationSchemeOption dependency property.

VirtualizationSchemeWhenScrollingToASpecificChildIndexProperty static

public static readonly DependencyProperty VirtualizationSchemeWhenScrollingToASpecificChildIndexProperty

Identifies the VirtualizationSchemeWhenScrollingToASpecificChildIndex dependency property.