Bookmark and Share Share...    Subscribe to this feed Feed   About Christian Moser  


How to Navigate, Group, Sort and Filter Data in WPF

What is a CollectionView?

Navigation

Filtering

Sorting

Grouping

How to create a CollectionView in XAML



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>
 
 




Last modified: 2009-10-13 09:25:17
Copyright (c) by Christian Moser, 2011.

 Comments on this article

Show all comments
tsahi
Commented on 29.September 2009
I think there's an error in the first C# code piece. You cannot declare the variable _customerView without it's type, and in the CustomerViewModel ctor, you probably didn't mean to redefine that variable locally.
Christian Moser
Commented on 2.October 2009
Hi Tsahi,
thanks for your feedback. It must have been already a bit late as I wrote this article :-) I correted the two things in the code snippets.

Greetings
Christian
jyanks
Commented on 29.October 2009
It'd be really helpful if you indicated which files each code snippet pertains to...
Tomas
Commented on 11.November 2009
Ehhm, where does the GetCustomers() method come from? Or am I to inexperienced to realize this...
Christian Moser
Commented on 11.November 2009
Hi Thomas,
the GetCustomer() is just a method on the ViewModel that returns a list of customers. I did not mentioned it in the snippet, because it's not relevant where the customers come from. It's just a data source. You can replace it by any kind of data source you like.

I hope this helps.
Christian
Nigel Stratton
Commented on 19.November 2009
Love the layout, concept and training, keep it up!

I think your snippet needs a couple of changes, adding "new EventHandler(" and the "void" return type.
_customerView.CurrentChanged += new EventHandler(CustomerSelectionChanged);

private void CustomerSelectionChanged(object sender, EventArgs e)
{
// React to the changed selection
}

Regards,
Nigel
Steven Jackson
Commented on 23.November 2009
Thanks for this article, time for me to refactor.
anon
Commented on 7.January 2010
But with out a definition for GetCustomers(), teh code can't be used! Its worthwhile you provide the whole code necessary when you write a tutorial for beginners so we dont get lost for hours! :)
Andy
Commented on 12.February 2010
"Commented on 7.January 2010
But with out a definition for GetCustomers(), teh code can't be used! Its worthwhile you provide the whole code necessary when you write a tutorial for beginners so we dont get lost for hours! :)"

Just write whatever function or data source you like since the only thing that matters is that it returns results in form GetCustomers() returns.
Thomas
Commented on 16.February 2010
Thanks for this article, and very nice site !

but i still have a question, how can you get the number of visible items in your filtered CollectionView ?
Mutia
Commented on 10.June 2010
I'm trying to bind and sort and filter images.. how can i posibly do this?
(http://mutiarar06.student.ipb.ac.id)
Tod
Commented on 8.July 2010
lol
Christian M
Commented on 23.August 2010
I apprieciate your feedback but I cant explain it anymore in depth because I just copy and pasted this from another website
alimbada
Commented on 1.November 2010
What is "customers" here? (last line in first example)
_customerView = CollectionViewSource.GetDefaultView(customers);

Or is that supposed to be "Customers"?
amir
Commented on 17.December 2010
hello
please help me?
this code for set group style in combo box. i can not set group style .until change combo box items then change text group!!!!

//class person
Public Class Person

Sub New(ByVal d As Integer, ByVal fm As String)
_id = d
_family = fm
End Sub

Private _family As String
Public Property Family() As String
Get
Return _family
End Get
Set(ByVal value As String)
_family = value
End Set
End Property

Private _id As Integer
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
End Class

//vb.net code

Dim lst As Person() = New Person() {New Person(1, "amir"), New Person(1, "ali"), New Person(2, "hasan"), New Person(3, "ahmad")}

Dim p As New ListCollectionView(lst)
p.Filter = AddressOf compare
p.GroupDescriptions.Add(New PropertyGroupDescription("ID"))
p.SortDescriptions.Add(New ComponentModel.SortDescription("ID", ComponentModel.ListSortDirection.Ascending))

cmb1.DisplayMemberPath = "Family"
cmb1.Items.Clear()
cmb1.ItemsSource = p

Dim g As New GroupStyle
g.HeaderTemplate = New DataTemplate
g.HeaderTemplate.VisualTree = New FrameworkElementFactory(GetType(TextBlock))
With g.HeaderTemplate.VisualTree
Dim b As New Binding("ID")
b.Source = p
.SetBinding(TextBlock.TextProperty, b)

.SetValue(TextBlock.BackgroundProperty, Brushes.Gray)
.SetValue(TextBlock.HeightProperty, 25.0)
.SetValue(TextBlock.ForegroundProperty, Brushes.White)
End With
cmb1.GroupStyle.Add(g)
Aarti Varadkar
Commented on 22.December 2010
Check out d link for ICollectionView in detail
http://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/
Aarti Varadkar
Commented on 22.December 2010
Check out d link for ICollectionView in detail
http://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/
Sanjay Patolia
Commented on 26.March 2011
I understood Itemsource property bindings but Why we use source property while we create collection view in View(XAML).
KC
Commented on 19.May 2011
&quot;I apprieciate your feedback but I cant explain it anymore in depth because I just copy and pasted this from another website&quot; - Chritian M

Nice!!!
john_stashek
Commented on 26.August 2011
[color=red]Hello, dear users.[/color]
My name is James. Today i was joined in your portal. There are very nice! :) I hope we will be friends.

Appreciate song, please: [url=http://baseofmp3.com/?q=%CC%E0%F5%E0%E1%E1%E0%F2]&ETH;&nbsp;&Ntilde;&ETH;&nbsp;&Acirc;&deg;&ETH;&iexcl;&acirc;&brvbar;&ETH;&nbsp;&Acirc;&deg;&ETH;&nbsp;&Acirc;&plusmn;&ETH;&nbsp;&Acirc;&plusmn;&ETH;&nbsp;&Acirc;&deg;&ETH;&iexcl;&acirc;[/url]
I like it.

[b] John[/b].

Name
E-Mail (optional)
Comment