Saturday, 19 November 2011

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 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 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 view
 
public 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 CollectionView
 
 
ICollectionView _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>
 
 

No comments:

Post a Comment