HypernovaDesktop Components · docs
Binarymission
WPF › Virtualizing Collection View › VirtualizingCollectionView

VirtualizingCollectionView

class.NET 6 / 8 / 10.NET Framework 4.8Binarymission.WPF.Data.DataExtensions

A collection view that hands grouped data to a virtualising panel as one flat list, with each group's header as a row of its own – so that BinaryVirtualizingWrapPanel can show thousands of items in groups and still create only the rows in view.

public sealed class VirtualizingCollectionView : ListCollectionView

Inherits from / implements: ListCollectionView

Remarks

Use it in place of ListCollectionView (or CollectionViewSource) whenever grouped data is shown in a BinaryVirtualizingWrapPanel with IsDataSourceGrouped set. Sorting, filtering and group descriptions work as on any ListCollectionView.

How it works

An ordinary grouped view hands an items control its groups, and the control builds one group item per group, each with a panel of its own – which a virtualising panel cannot see through. This view reports Groups as null, so the items control treats it as a flat list, and puts the groups into that list itself: Count is the items plus one row per group, GetItemAt answers a CollectionViewGroup for a header row and the item for any other, and IndexOf is its inverse. The real groups are still built underneath, and GroupsCount says how many there are.

Header rows

A header row reaches the items control as an ordinary item whose data is the CollectionViewGroup, with its Name and ItemCount to bind to. Draw it with an implicit DataTemplate for that type. Its container is an ordinary container, too: to style header rows differently, set CheckForVirtualizedGroupHeadersProperty on the containers and trigger on IsCurrentItemAVirtualizedGroupHeaderProperty. Only the top-level groups get header rows; with more than one group description, the inner levels have none.

Changes to the list

Every index the view raises in its change notifications counts the header rows, so the items control's generator stays in step. An addition is raised at its row, preceded by its group's header when the addition created the group. A removal, move or replacement in a grouped view can empty a group or move an item between groups, so it is raised as a reset; ungrouped, those pass through unchanged.

Examples

Products grouped by category, header rows styled apart and kept out of selection.

using System.ComponentModel;
using System.Windows.Data;
using Binarymission.WPF.Data.DataExtensions;

var view = new VirtualizingCollectionView(products);    // products: ObservableCollection<Product>
view.SortDescriptions.Add(new SortDescription(nameof(Product.Category), ListSortDirection.Ascending));
view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(Product.Category)));
catalogue.ItemsSource = view;
<ListBox x:Name="catalogue"
         xmlns:data="clr-namespace:Binarymission.WPF.Data.DataExtensions;assembly=Binarymission.WPF.Data.DataExtensions"
         xmlns:vwp="clr-namespace:Binarymission.WPF.Controls.Panels;assembly=Binarymission.WPF.Controls.VirtualizingWrapPanel">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type CollectionViewGroup}">
            <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" />
        </DataTemplate>
    </ListBox.Resources>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="data:VirtualizingCollectionView.CheckForVirtualizedGroupHeaders" Value="True" />
            <Style.Triggers>
                <Trigger Property="data:VirtualizingCollectionView.IsCurrentItemAVirtualizedGroupHeader" Value="True">
                    <Setter Property="Focusable" Value="False" />
                    <Setter Property="IsHitTestVisible" Value="False" />
                    <Setter Property="Background" Value="#FFE8EEF7" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <vwp:BinaryVirtualizingWrapPanel ItemDimension="180,120" IsDataSourceGrouped="True" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

See also

Members at a glance

Constructors
VirtualizingCollectionViewCreates a view over a list. Add sort and group descriptions to it afterwards, as to any ListCollectionView.
Properties
CountGets the number of rows in the view: the items, plus one header row for each top-level group when the view is grouped.
GroupsGets null, always, so that an items control lays the view out as one flat list – with the headers among the rows – instead of generating a group item per group.
GroupsCountGets the number of top-level groups – the number of header rows among the view's rows.
Methods
GetCheckForVirtualizedGroupHeadersGets whether an element is having its header-row flag kept up to date.
GetIsCurrentItemAVirtualizedGroupHeaderGets whether an item container currently holds a group's header row.
GetItemAtGets the row at a position in the view: a group's header or an item.
IndexOfGets the row at which an item, or a group's header, appears – the inverse of GetItemAt.
OnCollectionChangedRaises the view's change notification with its positions counted in rows, header rows included, as the items control reads them.
SetCheckForVirtualizedGroupHeadersSets whether an element has its header-row flag, IsCurrentItemAVirtualizedGroupHeaderProperty, kept up to date.
SetIsCurrentItemAVirtualizedGroupHeaderSets the header-row flag on an element. The view sets it itself on checked containers; there is normally no reason to call this.
SetIsCurrentItemAVirtualizedGroupHeaderPropertySets the header-row flag on an element: the same as SetIsCurrentItemAVirtualizedGroupHeader, kept for code written against this name.
Fields
CheckForVirtualizedGroupHeadersPropertyIdentifies the CheckForVirtualizedGroupHeaders attached property, which asks the view to keep IsCurrentItemAVirtualizedGroupHeaderProperty up to date on an item container.
IsCurrentItemAVirtualizedGroupHeaderPropertyIdentifies the IsCurrentItemAVirtualizedGroupHeader attached property: true on an item container whose data is a group's header row.

Constructors

VirtualizingCollectionView

public VirtualizingCollectionView(IList list) : base(list)

Creates a view over a list. Add sort and group descriptions to it afterwards, as to any ListCollectionView.

Parameters
list
The items. A list that raises change notifications, such as an ObservableCollection, keeps the view – and the panel – up to date as it changes.

Properties

Count

public override int Count

Gets the number of rows in the view: the items, plus one header row for each top-level group when the view is grouped.

Value
The items that pass the filter, plus GroupsCount.

Groups

public override ReadOnlyObservableCollection<object> Groups

Gets null, always, so that an items control lays the view out as one flat list – with the headers among the rows – instead of generating a group item per group.

Value
null. GroupsCount says how many groups there are.

GroupsCount

public int GroupsCount

Gets the number of top-level groups – the number of header rows among the view's rows.

Value
0 when the view has no group descriptions.

Methods

GetCheckForVirtualizedGroupHeaders static

public static bool GetCheckForVirtualizedGroupHeaders(DependencyObject obj)

Gets whether an element is having its header-row flag kept up to date.

Parameters
obj
The item container.
Returns

GetIsCurrentItemAVirtualizedGroupHeader static

public static bool GetIsCurrentItemAVirtualizedGroupHeader(DependencyObject obj)

Gets whether an item container currently holds a group's header row.

Parameters
obj
The item container.
Returns

true while it holds a header row (and is being checked).

GetItemAt

public override object GetItemAt(int index)

Gets the row at a position in the view: a group's header or an item.

Parameters
index
The zero-based row, counting one header row before each top-level group's items.
Returns

The CollectionViewGroup when the row is a group's header; otherwise the item. Ungrouped, simply the item at index.

Exceptions
System.ArgumentOutOfRangeException
index is negative, or not less than Count.

IndexOf

public override int IndexOf(object item)

Gets the row at which an item, or a group's header, appears – the inverse of GetItemAt.

Parameters
item
An item, or a CollectionViewGroup for its header row.
Returns

The zero-based row, counting one header row before each top-level group's items; -1 when the item, or the group, is not in the view.

OnCollectionChanged

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)

Raises the view's change notification with its positions counted in rows, header rows included, as the items control reads them.

Parameters
args
The change, with positions counted in items.
Remarks

An addition is raised at its row, preceded by the addition of its group's header row when the addition created the group. While grouped, a removal, move or replacement is raised as a reset; ungrouped, it passes through unchanged.

SetCheckForVirtualizedGroupHeaders static

public static void SetCheckForVirtualizedGroupHeaders(DependencyObject obj, bool value)

Sets whether an element has its header-row flag, IsCurrentItemAVirtualizedGroupHeaderProperty, kept up to date.

Parameters
obj
The item container – a FrameworkElement; other elements are ignored.
value
true to keep the flag up to date; false to stop and clear it.

SetIsCurrentItemAVirtualizedGroupHeader static

public static void SetIsCurrentItemAVirtualizedGroupHeader(DependencyObject obj, bool value)

Sets the header-row flag on an element. The view sets it itself on checked containers; there is normally no reason to call this.

Parameters
obj
The element.
value
The flag.

SetIsCurrentItemAVirtualizedGroupHeaderProperty static

public static void SetIsCurrentItemAVirtualizedGroupHeaderProperty(DependencyObject obj, bool value)

Sets the header-row flag on an element: the same as SetIsCurrentItemAVirtualizedGroupHeader, kept for code written against this name.

Parameters
obj
The element.
value
The flag.

Fields

CheckForVirtualizedGroupHeadersProperty static

public static readonly DependencyProperty CheckForVirtualizedGroupHeadersProperty

Identifies the CheckForVirtualizedGroupHeaders attached property, which asks the view to keep IsCurrentItemAVirtualizedGroupHeaderProperty up to date on an item container.

Remarks

Set it to true on the item containers, normally with a setter in the items control's ItemContainerStyle. From then on the container follows its data context as it is created and reused, and IsCurrentItemAVirtualizedGroupHeader is true exactly while it holds a header row. The default is false; setting it back clears the flag.

IsCurrentItemAVirtualizedGroupHeaderProperty static

public static readonly DependencyProperty IsCurrentItemAVirtualizedGroupHeaderProperty

Identifies the IsCurrentItemAVirtualizedGroupHeader attached property: true on an item container whose data is a group's header row.

Remarks

Kept up to date only on containers with CheckForVirtualizedGroupHeadersProperty set; elsewhere it stays false, its default. Trigger on it in the ItemContainerStyle to style header rows apart.