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

About these ads

26 thoughts on “ICollectionView explained

  1. Pingback: Arjan`s World » LINKBLOG for November 22, 2008

  2. Pingback: 2008 November 24 - Links for today « My (almost) Daily Links

  3. Pingback: JAPF » Blog Archive » How insert Separator in a databound ComboBox

  4. Pingback: JAPF » Blog Archive » Why should I use Model-View-ViewModel pattern

  5. Pingback: Binding a Silverlight DataGrid to dynamic data via IDictionary - Colin Eberhardt’s Adventures in WPF

  6. Pingback: weboomania.com » Silverlight MVVM + WCF Ria Services: Una combinación poderosa

  7. That article was very interesting..
    Had almost everything i wanted.
    However if you have 5 items in the list, then how would i select the 3rd item and display that in the combo box??

  8. Pingback: The Elephant and the Silverlight | View Model Collection Properties for WCF RIA Services

  9. Hi Can i get something of this Equivalent in Silverlight 4.
    As i am trying to make a silverlight MVVM App. where i have a listbox filled with name of the employees and on selected employee in the listbox a seperate panel shows its details.
    But i cannot get on selected name change in listbox i want to fire the onPropertyChnage event.
    This can be easily done in WPF.
    collectionView = CollectionViewSource.GetDefaultView(this.employees);
    if(this.collectionView == null)
    throw new NullReferenceException(“collectionView”);

    // listen to the CurrentChanged event to be notified when the selection changes
    this.collectionView.CurrentChanged += new EventHandler(this.OnCollectionViewCurrentChanged);

    But Not in SIlverlight 4

    Regards
    Debashish

  10. Pingback: JAPF » Blog Archive » A WPF behavior to improve grouping in your ListBox

  11. Greetings I recently finished reading through your blog and I’m very impressed. I really do have a couple inquiries for you personally however. You think you’re thinking about doing a follow-up submitting about this? Will you be going to keep bringing up-to-date at the same time?

  12. - MoveCurrentTo(x) (where x is the index you want to navigate to)

    The above should be MoveCurrentToPosition(x)

    In MoveCurrentTo(x), x is the item you want to navigate to and not the index.

  13. Pingback: JAPF » Blog Archive » WPF databinding trick (part 1)

  14. Pingback: My Three WCF Paths to Silverlight Data Access | the rasx() context

  15. Pingback: Icollectionview grouping | Jimrunsdorf

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s