Saturday, August 11, 2012
While working on a recent WPF project I came across a situation where I need to filter a collection based on user input. Additionally this project used the MVVM methodology, so the desire was to have as much as possible in the view model, not in the XAML or in the code behind. When faced with this situation normally you would just take the filtering criteria and apply a LINQ query on the collection to return a new list to base your display on. However in this case the collection was an Observable one and I needed to edit the filtered result and maintain the notify change event. These requirements ruled out the new list technique, and lead me to discover the CollectionViewSource. I asked around the office, and not many people had seen its use in a view model setting, so I thought I would do a quick post to document it.
So fire up Visual Studio and start a new WPF project and let’s get started.
The XAML in the main window is pretty basic, just a simple two row grid. The top row has a stack panel with a label and a text box. The second row has a DataGrid to hold the collection.
You will notice that we are binding the text of the text box to the FilterText property, and telling it to trigger the PropertyChanged event whenever the contents of the text box changes.
The other key item is the ItemsSource of the DataGrid, it is bound to the PersonCollectionView.View . This will be our CollecitonViewSource, not the underlying Observable collection, so be sure to use the .View property.
Since we will be use a view model the code behind is very simple, just initialize the DataContext and we are done here.
Now to the interesting part, create a new class called PersonViewModel. You can create this any place you want, typically I create a folder called ViewModel to hold these classes. It’s not required, but if you do make sure you import the namespace in your code behind.
Start by defining a new Person class, with Name and Age properties. Since this will be an Observable collection inherit the INotifyPropertyChanged interface. Also provide a private backing variable for the age and call the OnPropertyChanged event for it. While this sample doesn’t really need change notification, this is the typical scenario we want to support.
Since we will be keying on the filter string to trigger everything we need to make the PersionViewModel class inherit the INotifyPropertyChanged interface. Additionally we need to add three properties, FilterText (string), PersonCollection (ObservalbeCollection
We also need to make sure we import the System.Collections.ObjectModel for the Observable collection, System.ComponentModel for INotifyPropertyChanged, and System.Windows.Data for the CollectionViewSource.
In the constructor of the view model we will call a single LoadData method. This would normally be a call to your service or DAL to get your data, but here we will simply create a collection of fake data.
After we create our Observable collection we instantiate our CollectionViewSource. The first part simply creates the object, the second line adds a filtering event handler, which we will write shortly. The third (optional) line applies a sorting to the view. The last line assigns the PersonCollection to the source of the View.
Now it’s time to implement the filter, so create a new PersonViewFilter method with two parameters, an object, and a FilterEventArgs.
This method will be called for each item in the Observable collection, and if the item is Accepted it will be included in the filtered view. Here we are checking the FilterText property, and if it is not null then we cast the item to our Person class. We can then check to see if the current item starts with the FilterText, if it does it is accepted. If the FilterText string is empty or null then all items are accepted.
The last bit of code to wire it all up is located in the FitlerText property.
We need to provide a private backer, and implement the OnPropertyChanged event, but additionally we also call the Refresh method on the CollectionViewSource. This triggers the filtering action listed above.
Putting all the pieces together the outline of our PersonViewModel class looks like this:
Now when we run the application we see that the list is filtered based on our input. So there you have it, quick, easy filtering of an Observable Collection. Enjoy!
Download Code: CollectionSample.zip