How to Navigate, Group, Sort and Filter Data in WPF
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 awrapper 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 property
IsSynchronizedWithCurrentItem
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" />
If you are using a MVVM (Model-View-ViewModel) pattern, you don't have to extra wire-up the
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 }
You can also manually control the selection from the ViewModel by calling the
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 call
Refresh()
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 many
SortDescriptions
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 adding
GroupDescriptions
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"));
To make the grouping visible in the view you have to define a special
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>
Comments
Post a Comment