What is a CollectionView?
WPF has a powerful data binding infrastructure. It allows you to bind almost any kind of collection directly to a view. But when it comes to sorting, filtering and grouping the support of the collections is rare. That's the point where the CollectionView comes into play. A collection view is a wrapper around a collection that provides the following additional features:
- Navigation
- Sorting
- Filtering
- Grouping
How to Create and Use a CollectionView
The following example shows you how to create a collection view and bind it to a ListBox<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ListBox ItemsSource={Binding Customers} /> </Window>
public class CustomerView { public CustomerView() { DataContext = new CustomerViewModel(); } } public class CustomerViewModel { private ICollectionView _customerView; public ICollectionView Customers { get { return _customerView; } } public CustomerViewModel() { IList<Customer> customers = GetCustomers(); _customerView = CollectionViewSource.GetDefaultView(customers); } }
Navigation
The collection view adds support for selection tracking. If you set the propertyIsSynchronizedWithCurrentItem
to True
on the view that the collection is bound to, it automatically synchronizes the current item of the CollectionView and the View.
<ListBox ItemsSource="{Binding Customers}" IsSynchronizedWithCurrentItem="True" />
SelectedItem
of the control, because it's implicity available over the CollectionView.IList<Customer> customers = GetCustomers(); ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers); _customerView.CurrentChanged = CustomerSelectionChanged; private CustomerSelectionChanged(object sender, EventArgs e) { // React to the changed selection }
MoveCurrentToFirst()
or MoveCurrentToLast()
methods on the CollectionView.Filtering
To filter a collection view you can define a callback method that determines if the item should be part of the view or not. That method should have the following signature:bool Filter(object item)
. Now set the delegate of that method to the Filter
property of the CollectionView and you're done.ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers); _customerView.Filter = CustomerFilter private bool CustomerFilter(object item) { Customer customer = item as Customer; return customer.Name.Contains( _filterString ); }
Refresh the filter
If you change the filter criteria and you want to refresh the view, you have to callRefresh()
on the collection viewpublic string FilterString { get { return _filterString; } set { _filterString = value; NotifyPropertyChanged("FilterString"); _customerView.Refresh(); } }
Sorting
Sorting data ascending or descending by one or multiple criterias is a common requirement for viewing data. The collection view makes it so easy to achieve this goal. Just add as manySortDescriptions
as you like to the CollectionViewICollectionView _customerView = CollectionViewSource.GetDefaultView(customers); _customerView.SortDescriptions.Add( new SortDescription("LastName", ListSortDirection.Ascending ); _customerView.SortDescriptions.Add( new SortDescription("FirstName", ListSortDirection.Ascending );
Fast Sorting
The sorting technique explained above is really simple, but also quite slow for a large amount of data, because it internally uses reflection. But there is an alternative, more performant way to do sorting by providing a custom sorter.ListCollectionView _customerView = CollectionViewSource.GetDefaultView(customers); as ListCollectionView; _customerView.CustomSort = new CustomerSorter(); public class CustomerSorter : IComparer { public int Compare(object x, object y) { Customer custX = x as Customer; Customer custY = y as Customer; return custX.Name.CompareTo(custY.Name); } }
Grouping
Grouping is another powerful feature of the CollectionView. You can define as many groups as you like by addingGroupDescriptions
to the collection view.Note: Grouping disables virtualization! This can bring huge performance issues on large data sets. So be careful when using it.
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers); _customerView.GroupDescriptions.Add(new PropertyGroupDescription("Country"));
GroupStyle
on the view.<ListBox ItemsSource="{Binding Customers}"> <ListBox.GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name}"/> </DataTemplate> </GroupStyle.HeaderTemplate> </ListBox.GroupStyle> </ListBox>
How to create a CollectionView in XAML
It's also possible to create a CollectionView completely in XAML<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Window.Resources> <CollectionViewSource Source="{Binding}" x:Key="customerView"> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="Country" /> </CollectionViewSource.GroupDescriptions> </CollectionViewSource> </Window.Resources> <ListBox ItemSource="{Binding Source={StaticResource customerView}}" /> </Window>