Virtualizing Collection View
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.
Assembly
| Assembly | Binarymission.WPF.Data.DataExtensions.dll |
| Root namespace | Binarymission.WPF.Data.DataExtensions |
| Target frameworks | .NET Framework 4.8, .NET 6 / 8 / 10 |
Overview
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.
Getting started
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>API reference
Binarymission.WPF.Data.DataExtensions
class VirtualizingCollectionView | 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. |