|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
![]() IntroductionMicrosoft Excel has a very useful auto-filter function. With this function enabled on a column, the user is able to select filters from a drop-down list, which when selected, are applied to the column. This article describes how to add similar filter functionality to the WPF ListView control. BackgroundBefore the arrival of WPF control customisation was a tricky and somewhat messy task. With WinForms, a small amount of customisation can be performed if the control exposes events allowing the developer to tweak certain aspects of style prior to rendering, with other controls the Paint method can be overriden to perform additional rendering after the control has renderred itself. However, any significant changes in how the control appeared will probably require the developer to take responsibility for rendering the entire control (owner draw). In summary, control customisation with WinForms feels like hacking!
Step One: Where to StartI wanted this control to not only provide filter functionality, but also to allow the user to sort by clicking on the column heading. Rather than re-inventing the wheel, I found a sortable ListView on Joel Rumerman's Blog which provided a suitable starting point for my control. The image below shows this control in action, with the list sorted by Forename: I created a sub-class of SortableListView, FilterableListView as the starting point for this control. Step Two: Adding the ControlsUnfortunately the background section to this article paints a rosy picture of WPF which is not altogether true. Some controls are harder to customise than others, with the ListView being one of the trickier ones. <ListView>
There are a number of great Blog posts which describe how to style the various visual aspects of the ListView, including Styling The ListView, which describes how to style the ItemContainer which (as you might expect!) contains the items in the list, the CellTemplate for each GridViewColumn, the ColumnHeaderContainerStyle, and more. The basic principle being that the ListView exposes the templates and styles of the visual elements which it constructs as properties of the ListView itslef. The hard part is knowing which of this properties to use in order to achieve the effect you are after. To achieve the above it is the contents of the column header which we need to modify rather than the visual style of the entire header itself. Therefore the datatemplate of the column header is the correct place to add these controls. The XAML below illustrates how this is done. <DataTemplate x:Key="FilterGridHeaderTemplate">
If you have read the blog post referenced above, you will know that each GridViewColumn has a HeaderTemplate property which can be used to provide a datatemplate for the colum header. We could simply specify that when a developer uses the FilterableListView that they set the HeaderTemplate for each column accordingly, however it makes the control much more useable if the above template automatically becomes the datatemplate for the column header. protected override void OnInitialized(EventArgs e)Step Three: Displaying the PopupWhen placing components within a Window, event handling is a straightforward process of connecting events to handlers in the code-behind. However, in this case our data template is specified within a resource dictionary. It might seem surprising at first, but there is no way that you can directly connect the click event to the code! The problem is described in more detail in the following Blog post, Commands and Controls in WPF, with the solution being to use commands, giving us much looser coupling between the view (XAML) and the controller (code-behind). In listing 2 you can see that the filter button issues a ShowFilter command, this is a RoutedEvent that will tunnel and bubble through the visual tree. The FilterableListView has a command binding that allows it to handle this event.
The easiest way of discovering this information is to navigate the visual tree, first up to the column header, to obtain the name of the property to filter, then down from the header to locate the associated popup window. The VisualTreeHelper class provides a number of static utility methods for navigating the tree, which Andrew Whiddett has wrapped up in a WPFHelper class which enhances this functionality allowing you to search the visual tree for classes that match certain criteria (name, type, etc…) public FilterableListView()
Step Four: Applying the FilterThe final step is simply to handle the SelectionChange event from the filter ListView, constructing the appropriate filters and applying them to the Items in the list. When using a FilterableList view which has a filter set on one of the columns and you wish to apply a filter to one of the other columns, you would expect that the drop down only displays the items that are present as a result of the first filter. In other words, the filters are AND’ed together. Interestingly, this functionality comes for free! This is because we populated the drop-down filter list by iterating over the Items property of out ListView. The Items derives from System.Windows.Data.CollectionView which, as the name suggests, is a view on the bound data which is the effect of applying grouping, sorting, filtering etc… // construct a filter and apply it
Using the Control in PracticeUsing the FilterableListView is as simple as using a regular ListView. The only difference is the addition of an extra property SortPropertyName, which is the property which is sorted / filtered for a specific column. <slogic:FilterableListView ItemsSource="{Binding}">
The FilterableListView does however suffer from the same problem as the ListView. The control is composed of a number of other controls which the developer does not explicitly construct within their XAML code. FilterableListView uses the same approach as ListView, it exposes the properties of the visual elements it includes to the developer via dependency properties. The FilterButtonActive and FilterButtonInactive properties can be used to style the drop-down button (Note, I have not exposed all the potential properties that one might be interested in, I leave this as an exercise for the reader!).
ConclusionThis article demonstrates how to enhance the functionality of the provided ListView control to add an auto-filter to the column headings. The WPF ListView control feels a little inaccessible at first, however it is not much harder to customise that the other controls within the WPF libraries. History08/07/2008 - Initial article upload.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||