ICollectionView explained

Lately I have been getting a lot of questions on ICollectionView so I decided to write a very small post on how it works and some tips and tricks you might find useful when using it. ICollectionViews are used for filtering, sorting, grouping and also selecting items in list controls (selecting applies only for controls that inherit from the Selector base class such as ListBox, ComboBox etc..).

This is the MSDN documentation description of a ICollectionView :

“You can think of a collection view as a layer on top of a binding source collection that allows you to navigate and display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself”

And yes I agree, it is a very powerful mechanism and helps a lot when you are separating “concerns” by using patterns such as M-V-VM.

So let’s start from the basics, how can I get an ICollectionView out of my collection?

   1: List<string> myData = new List<string>();
   2: ICollectionView view = CollectionViewSource.GetDefaultView(myData);

That was easy wasn’t it! Now that you have the ICollectionView at hand, what can you do with it?

Filtering

Let’s start by doing some filtering. Basically you can set a predicate to the Filter  property and this will cause the list to be filtered by that predicate. Something like this

   1: myDataView.Filter = delegate(object item)
   2: {
   3:     return item.Contains(SearchText);
   4: };

As you can see it is very easy to implement. This will take care of filtering items as they are added (if the collection implements the INotifyCollectionChanged).
(TIP) You can also do Filtering on more than one predicate. Have a look at how Beatriz Costa does this.

Sorting

You can also do sorting of data by using the ICollectionView. This can be done in a super easy way by using SortDescriptions.

   1: myDataView.SortDescriptions.Add(new SortDescription("ID", direction));

Where ‘ID’ is the name of the property which you want to sort on. (TIP) If you are going to apply more than one SortDescription at once use the DeferRefresh method so that you only apply a sort once. DeferRefresh will return an IDisposable object and once you call dispose the Refresh happens. So you can use it like so…

   1: using (myDataView.DeferRefresh()) // we use the DeferRefresh so that we refresh only once
   2: {
   3:     myDataView.SortDescriptions.Clear();
   4:     if (SortById)
   5:         myDataView.SortDescriptions.Add(new SortDescription("ID", direction));
   6:     if (SortByName)
   7:         myDataView.SortDescriptions.Add(new SortDescription("Name", direction));
   8: }

(TIP) This seems like a very nice way how to implement sorting but there is a very big performance penalty here. The SortDescriptions  are slow as an old granny (it uses reflection). If you are dealing with large data sets you should NOT be using this technique for sorting. Yet do not despair because there is an alternative; You can also do sorting by using CustomSort property on the ListCollectionView (this is the type of CollectionView that gets created when using a collection that implements an IList). Have a look here http://ligao101.wordpress.com/2007/07/31/a-much-faster-sorting-for-listview-in-wpf/.
(TIP) If you need to do sorting of ListViews so that when the user clicks on the ListView Column header it sorts, then you might want to have a look at this: https://marlongrech.wordpress.com/2008/04/20/part1-avaloncontrolslibrary-datagridview/ (See the Section, “Sorting the data in the DataGridView (and also ListView)” )

Grouping

This is yet another cool feature of the ICollectionView. Basically you can group data together by using the GroupDescriptions property of the ICollectionView. You can add a PropertyGroupDescription to the GroupDescriptions property and you are all set. Besides this you can also modify how the group looks by using a GroupStyle (which is applicable for all class that derive from ItemsControl). Here is some code for you to chew…

   1: C# code
   2: myDataView.GroupDescriptions.Add(new PropertyGroupDescription("Location"));
   3:
   4: XAML
   5: <ListView.GroupStyle>
   6:     <GroupStyle>
   7:         <GroupStyle.HeaderTemplate>
   8:             <DataTemplate>
   9:                 <TextBlock Text="{Binding Name}"/>
  10:             </DataTemplate>
  11:         </GroupStyle.HeaderTemplate>
  12:     </GroupStyle>
  13: </ListView.GroupStyle>

(TIP) If you want some custom grouping you should have a look at: http://www.beacosta.com/blog/?p=19. Basically you can use an IValueConverter to do your custom logic; let’s say you have an Age property and you want to do a group of under 16 and over 16. You can do that logic in the converter 🙂

(TIP) Grouping will disable the Virtualization support by default. So grouping for large data sets could be really expensive, think it twice before doing it…

Selection

One of my favorite features of the CollectionView is the support for Selection tracking, this really helps a lot when you are in the ViewModel and need to know what is happening in the UI (Typical example for this is, a Master-Detail screen). Basically if the ItemsControl( this is the base class for all controls that show lists or better that have the famous ItemsSource property ) you are using inherits from the Selector control (such as Listbox, Listview, ComboBox etc…) you can switch on the feature to keep track of the Selections. You do this by setting the IsSynchronizedWithCurrentItem property to True. This is very important! if you do not set this property the CollectionView selection features are not gonna work!

   1: <ListView ItemsSource="{Binding MyData}" IsSynchronizedWithCurrentItem="True">

(TIP) Before going to the details of what you can do with this let me point out one really cool thing. If you set the IsSynchronizedWithCurrentItem property to true on 2 ListBoxes(or any other control that derives from Selector) the 2 controls will keep the selection in sync 🙂

Now let’s have a look at what you can do with this. Lets start by seeing how we can check what is the currently selected item. Thanks to the pretty API the ICollectionView exposes all you have to do is call the property CurrentItem of the ICollectionView

   1: myDataView.CurrentItem

The API also exposes an event that is fired when ever the current item changes so you can react when the current item changes

   1: //when the current selected changes store it in the CurrentSelectedPerson
   2: myDataView.CurrentChanged += delegate
   3: {
   4:     //stores the current selected person
   5:     CurrentSelectedPerson = (Person)myDataView.CurrentItem;
   6: };

You can also change the selected item by using one of the following methods

– MoveCurrentToFirst
– MoveCurrentToLast
– MoveCurrentToNext
– MoveCurrentToPrevious
– MoveCurrentTo(x) (where x is the index you want to navigate to)

You can do some neat things with this. In the Demo app that I provide for download in this post you can see how I leverage RoutedCommands with the CanExecute mechanism to enable and disable the options for the selection navigation.

(TIP) If you are doing a method to check if the user can select the last item/next item and you are using filtering on the list always count the items in the CollectionView and not in the original list, like the following

   1: public bool CanSelectLastItem()
   2: {
   3:     //Please note that this is done in such a way and not in myData.Count because if there is filtering being applied we need to make sure that the count of items is correct!
   4:     return myDataView.CurrentPosition < myDataView.Cast<Person>().Count() - 1;
   5: }

Conclusion

ICollectionView is really a great tool especially when you are trying to separate the UI code from the business logic by using patterns such as M-V-VM. In the Demo application I compiled for this post you can see how to implement a nice ViewModel and use the ICollectionView to do all sort of stuff for the UI without having any problems because you do not have a reference to the UI objects. Having said all this sometimes complex scenarios make us move away from the ICollectionView… Still I believe that ICollectionViews finds a place in nearly all projects because they make your life much easier 🙂

Download Demo Application

kick it on DotNetKicks.com