I have been working on a new component that allows you to filter any list of items shown in a WPF user interface. My new PropertyFilterGroupView is definitely still a work-in-progress, so please do not drop this control into a production WPF application just yet! If you are interested in this, please leave me a comment with constructive criticism, feature requests, bug reports, etc. I’m interested to see what people have to say about it. The source code is available at the end of this blog post.
Binding to a collection of objects really means binding to an ICollectionView implementation that wraps the underlying collection. One property exposed by ICollectionView is Filter, of type Predicate<object>. When a filter is applied, all controls bound to the view will display only the items that passed through the filter, or were “filtered in.” PropertyFilterGroupView is a very fancy and elaborate way to assign the Filter property of an ICollectionView.
Below is a screenshot of the demo application when it first loads up. The control in the Expander up top is the PropertyFilterGroupView control. The ListBox and ListView seen beneath it are bound to the ICollectionView that the PropertyFilterGroupView affects. When the app first loads up all items in both lists are visible, as seen below:
After applying a few filters, the two lists display a subset of the original set of items. The items still seen in the lists are the ones that satisfy all of the filters applied. The modified UI looks like this:
In this demo app, the list of items shown in the UI is a collection of Person objects. The Person class exposes some properties, as seen below:
Now that you know what shape the data objects have, let’s see how to configure the PropertyFilterGroupView control so that it allows the user to filter a list of Person objects. The following XAML is from the Window1.xaml file in the demo project:
Each property on the Person object that the user should be able to filter on has a PropertyFilter object added to the filter group. PropertyFilter is a class I made which represents a filter that determines whether a certain property on an object satisfies a criterion. The user can select the filter’s criterion via a ComboBox in the PropertyFilterViewGroup control.
When you create a PropertyFilter, you must set the PropertyName and PropertyType properties to valid values. If you do not set them, or set them to invalid values, an exception will be thrown when that filter is used. The PropertyName property allows you to indicate which property to filter on, and PropertyType must be set to the data type of that property. If you want the property’s name to be more user-friendly, set the DisplayName property to a more readable name.
Look at the last PropertyFilter object again, which filters on the MaritalStatusID property. That filter has two additional properties set on it: PropertyConverter and DisplayType. The former is set to the IValueConverter used by the ListView to translate an integer to a value from the MaritalStatus enum. The ListView uses that converter in this column:
The PropertyFilter that filters the MaritalStatusID property needs access to that value converter so that it, too, can transform the integer value into the corresponding enum value. Without that in place, the user would not be able to filter on the text in the Marital Status column, but only the underlying numeric identifiers.
The DisplayType property is set on that PropertyFilter so that it can intelligently decide what the available filtering criteria are for that property, such as ‘Starts With’ and ‘Is Less Than’. The list of filtering criteria for, say, a numeric type is not the same as for text, which is not the same for Booleans. To ensure that the filtering criteria makes sense for a property whose value is passed through a value converter, you must indicate what type the output value should be treated as for filtering purposes. I chose String as the DisplayType for the MaritalStatusID filter, because enum values are easy to compare via their textual representations.
The PropertyFilterGroupView control also has its CollectionView property set. It is bound to the inherited DataContext. To ensure that this property is able to pick up a reference to a CollectionView, I created a CollectionViewSource as the Window’s DataContext. That XAML is below:
As I mentioned before, this project is a work-in-progress. There are some big pieces of the puzzle missing. For example, I do not yet support filtering on properties of type DateTime. However, with that said, I encourage you to try this component out and see what you think. I’m open for suggestions…
Download the source code here: PropertyFiltering Source Code . Be sure to change the file extension from .DOC to .ZIP and then decompress the file.