Introduction
This article demonstrates how to implement multiple filters for a WPF DataGrid using the MVVM paradigm and can probably be easily modified to provide multi-filtering on other collection controls like a ListBox. There are articles in CodeProject and other sites which describe general purpose filtering or auto-filtering in a DataGrid
, but they use code behind rather than MVVM (see references). The goal of this article is to provide a simple example that can be adapted to meet your project needs with a minimum of refactoring. The impetus for this article was a requirement for custom filtering with multiple filters (the filter mechanism needed to include synonyms of selected filter words). The example present in this article does allow for custom filter logic.
This article is marked as intermediate article because it assumes the reader will have some experience with the MVVM design paradigm. Despite this the sample should be simple, readable code that could be easily followed by beginners too.
The sample code provides a single view with a DataGrid
that displays a collection of Thing
objects. A Thing
is a simple data class which contains multiple properties. The DropDownList
s at the top of the view each apply a filter when a value is selected. The filters can be applied in any order and when a filter is applied the red x button can be used to remove the filter. The Reset button removes all filters.
The code sample is implemented with .NET 3.5 and uses the following:
The required libraries are bundled in a solution folder and should not need to be installed separately. The solution has 3 projects but the two classes of interest are MainView.xaml and MainViewModel.cs, all others are there to support the example.
Using the code
The key to implementing multiple filters in WPF is to instantiate a CollectionViewSource instance rather than using the CollectionViewSource.GetDefaultView() static method. The difference between the two can be summed up as follows:
CollectionViewSource.GetDefaultView()
returns an ICollectionView which allows a single filter using the Filter
property. Each time this property is changed it resets the previous filter.
- Instantiating a
CollectionViewSource
allows for multiple filters by providing a Filter event. Multiple filter event handlers can subscribe to this event and each filter is applied in the order it is subscribed to the event. To remove a filter involves unsubscribing from the event.
Here are the key steps in the example each associated file:
- Load the data into the
MainViewModel
from the DataService
(the example just loads an xml file) to populate the Things
property
- Instantiate the
CollectionViewSource
object in the MainView
and bind it to the collection of Thing
objects which was loaded in step 1 (~ line 13 of MainView.xaml) (*)
<Window.Resources>
...
<CollectionViewSource Source="{Binding Things}" x:Key="X_CVS"/>
</Window.Resources>
- Bind the CollectionViewSource instance to the
DataGrid
in the MainView
(~ line 51 of MainView.xaml)
...
<Custom:DataGrid ItemSource="{Binding Source={StaticResource X_CVS}}"
Margin="8" Grid.Row="1" AutoGenerateColumns="True"
IsReadOnly="True"></Custom:DataGrid>
...
- A reference to the
CollectionViewSource
instance is passed to the MainViewModel
(~ line 18 of MainView.xaml.cs) where it is assigned to the local property CVS
(~ line 265 of MainViewModel.cs)
- When a target filter is specified in a drop down list, a filter event handler is subscribed to the filter event of the CollectionViewSource
- When a filter is removed, the corresponding event handler is un-subscribed from the Filter event of the CollectionViewSource
One thing to note about the filter methods as seen in the following method which filters each row based on the SelectedAuthor
property of the MainViewModel
:
private void FilterByAuthor(object sender, FilterEventArgs e)
{
var src = e.Item as Thing;
if (src == null)
e.Accepted = false;
else if (string.Compare(SelectedAuthor, src.Author) != 0)
e.Accepted = false;
}
A row in the grid does not match the filter and is hidden by setting e.Accepted =
false.
Note that e.Accepted
is not set to true if a match is found. Rather, only set e.Accepted
to false for items that do not match.
This enables filter results to be applied on top of existing filter results. If e.Accepted
is set to true, it may negate any previous filter results.
References
WPF’s CollectionViewSource by Bea Stollnitz is an article for multiple filters on a ListBox using
the CollectionViewSource
mechanism with code behind and was responsible for leading me down the path to this creating this example.
The following are several CodeProject articles with code-behind WPF DataGrid filtering:
History
18 Aug 2012: updated the example code to include wpftoolkit.dll in the solution folder.