We're the Windows Forms User Education team at Microsoft. We use this space to update folks on developments with Windows Forms docs, publishing doc updates before they're published. Feel free to ask us questions about WinForms, and we'll do our best to an

Implementing multi-column filtering on the IBindingListView

A couple of years ago, I a whitepaper about how to implement searching and sorting on the generic BindingList for data binding in Windows Forms. About the same time my colleague, Karl, wrote a whitepaper about adding an auto-filter capability to the Windows Forms DataGridView control. Karl and I had talked about bridging the gap by me writing another whitepaper about how to implement filtering on a generic BindingList by implementing the IBindingListView. Well , it took a year or so but I did write the code and the whitepaper. During the process, I made one blog post about it, but continued to improve the code. Another few months have gone by, and the whitepaper is still not published, although I do have the paper and the code available on the Microsoft Download site. The download includes projects for Visual Studio 2005 and 2008 in both VB.NET and C#. The VS 2008 versions include some unit tests as well. I am a big fan of unit testing, even though I am by no means an expert. I found the unit tests very helpful and my editor suggested I publish them because you might find them helpful as well. In addition, I’ve included projects that demonstrate the filtered list working the the auto-filtered DataGridView.

When I started on this project I honestly did not understand how complex it was. When you implement filtering one of the first things you need to decide is what sort of filtering expressions your code will accept. I opted for a sub-set of the DataView.RowFilter format guidelines. I limited my operators to <,> and = to keep things simple. My idea was to give you a jumping off point for more complex filtering. In addition, I opted to only filter types that implement IComparable. Attempts to filter other types will result in an exception. A complication is that I started with a list that already supports single-column sorting. This means that the sorting and filtering implementations need to play nice together. For example, a sorted collection that is later filtered should remain in the same sorted order, or if a list is sorted and filtered, and the sort is removed, it should remain filtered.

Following is some of the code from the paper with a brief explanation. If you want to stop reading this post and go straight to the paper and code, you can find it here.

The are only three APIs exposed by IBindingListView that you need to implement for filtering:

SupportsFiltering property

Filter property

RemoveFilter method

SupportsFiltering is simple. For my implementation this always return true. The get for the Filter property is simple as well. I simply return the value of the backing private field. The filteret is where a lot of heavy lifting happens. When the Filter property is set, the filter is applied to the collection and only items meeting the filter criteria are returned. This means you need to do a lot of work in the set:

Cache the existing collection in case the filter is removed

Check for null or empty string; this means the filter has been removed and you need to restore the original collection

Parse the filter expression to determine if its in the correct format

Apply the filter, and return on list values that meet the filter criteria

Reapply the sort if necessary

These steps are further complicated by multi-column filtering.

Here’s the code from my Filter set:

set { if (filterValue == value) return;

// If the value is not null or empty, but doesn’t // match expected format, throw an exception. if (!string.IsNullOrEmpty(value) && !Regex.IsMatch(value, BuildRegExForFilterFormat(), RegexOptions.Singleline)) throw new ArgumentException(“Filter is not in ” + “the format: propName[<>=]’value’.”);

// Check to see if the filter was set previously. // Also, check if current filter is a subset of // the previous filter. if (!String.IsNullOrEmpty(filterValue) && !value.Contains(filterValue)) ResetList();

As I said previously, there’s a lot happening here. First I check to see if the filter value has changed, if not, I simply return. Then I check for the correct filter format by calling BuildRegExForFilterFormat. Following is the code for this method:

//Add optional space followed by optional quote and // any character followed by the optional quote. regex.Append(@”\s?’?.+’?”);

return regex.ToString(); }

If the filter is in the correct format I know I’m in business and turn off change notification so I can manipulate the contents of the list without notifying bound controls.

Next I check for null or an empty string. One of these values indicates the filter has been removed and I need to reset the contents of the list to the unfiltered content. Following is the code for the ResetList method. Once I restore the original list, I reapply the sort, if the list is sorted.

Next I split the filter at “AND” to separate the parts of a multi-column filter. I loop through each filter part and parse and apply the filter to the list. I do some checking to see if the current filter part is a subset of a previous filter indicating a portion of a multi-column filter is being removed. In this case, I reset the list before I apply the filter. I created the SingleFilterInfo and FilterOperator enums to aid in parsing and filtering. The actual parsing and filtering are done by the ParseFilter and ApplyFilter methods. Following is the code for these methods along with the enums and supporting methods.

// Check to see if the property type we are filtering by implements // the IComparable interface. Type interfaceType = TypeDescriptor.GetProperties(typeof(T))[filterParts.PropName] .PropertyType.GetInterface(“IComparable”);

There’s a lot in my paper that I am leaving out of this post. Hopefully, I’ve whet your appetite and you’ll download the code and paper. The paper is included as part of the zip file. You can get it all here: Implementing Filtering for Windows Forms Data Binding. Enjoy!