Data filtering actually controls visibility of individual grid rows basing on rules set by the programmer. These rules should be convenient and easy to use, should not depend on data sorting or grouping and should be equally applied to static and real-time data. Data filtering should not depend on hierarchy level or on data being expanded or collapsed by parent rows.

Wpf GridControl column filters

This topic contains the following sections.

Filtering methods

The grid provides three complementary methods of data filtering.

  • Row..::..Filtered is the easiest method of displaying or hiding data. However, it is doesn't support even-driven data model and therefore it is not useful for more complex applications. With this method developers need to control row visibility manually and to consider that data can be sorted or grouped and may have complex hierarchical structure.
  • IFilter interface - the most efficient and convenient filtering method. Developers have to inherit the IFilter interface and to implement the IsFiltered method. This method uses row as argument for which the grid determines whether filtering is required.

    This method is called in the following cases:

  • Column filters

Programmatic filter

C# Copy imageCopy
//Quote class implementing INotifyPropertyChanged interface 
class Quote : INotifyPropertyChanged 
{ 
    private readonly int _quantity; 
    private double _price; 

    public Quote(int quantity, double price) 
    { 
        _quantity = quantity; 
        _price = price; 
    } 

    public int Quantity { get { return _quantity; } } 
    public double Price 
    { 
        get { return _price; } 
        set 
        { 
            if (_price != value) 
            { 
                _price = value; 
                if(PropertyChanged != null) 
                { 
                    PropertyChanged(this, new PropertyChangedEventArgs("Price")); 
                } 
            } 
        } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

//Set some filter: 
grid.Filter = new Filter(delegate(Row row) 
{ 
    double price = (double) row["Price"].Value; 
    return price < 1000; 
}); 

//Populate grid with some values: 
List<Quote> collection = new List<Quote>(); 
for (int i = 0; i < 5; ++i) 
{ 
    collection.Add(new Quote(i, 1000 + 100 * i)); 
} 
grid.ItemsSource = collection; 

//All 5 rows are displayed right now. Change price of a quote: 
collection[4].Price = 900; 

//The row becomes invisible. Wpf GridControl displays only 4 rows

Column filters

Column filters provide user with visual controls to manage row filtering. Dapfor.Wpf.dll library provides a set of filters that can be used in most projects. These filters are provided in form of DataTemplate elements stored in Dapfor.Wpf.Filters..::..FilterFactory

Developers can set filters with xaml code:

XAML Copy imageCopy
<Window x:Class="SomeApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        xmlns:my="clr-namespace:Dapfor.Wpf.Controls;assembly=Dapfor.Wpf"
        xmlns:filters="clr-namespace:Dapfor.Wpf.Filters;assembly=Dapfor.Wpf">


  <my:GridControl>
        <my:GridControl.Headers>
            <my:Header>
                <my:Header.Columns>
                    <my:Column Id="Status" Title="Status" FilterTemplate="{x:Static filters:FilterFactory.CheckBoxFilter}" FilterAlignment="Stretch"/>
                    <my:Column Id="Established" Title="Established" FilterTemplate="{x:Static filters:FilterFactory.DateMultiFilter}"/>
                </my:Header.Columns>
            </my:Header>
        </my:GridControl.Headers>
    </my:GridControl>
</Window>

It can also be done programmatically:

C# Copy imageCopy
Column column = gridControl1.Headers[0]["Established"];
column.FilterTemplate = FilterFactory.WildcardFilter;

When a filter template is set for a column, the grid automatically displays filter icon in its right part. When a user clicks the icon, the grid automatically opens a popup window and places the specified template to this window. The grid provides Columnobject to the template for data content. You can set the initial popup position with Column..::..FilterAlignment property.

Column also provides the Column..::..CloseFilter()()()() method to hide a control when it is no longer necessary. Filter is set when the Column..::..Filter property is called and the IFilterobject is transferred to this property. The grid verifies filtering conditions for each row and hides or shows it as necessary. Users may change filtering conditions many times, and a new object has to be created for every such change. IFilter. If you need to store filter values between calls, you can use the Column..::..Tag property or take the values from a previously installed filter.

Custom column filters

Now we will show you how to create a custom filter for Doubletype data.

Wpf GridControl Custom column filter

First of all, we create a CustomControl that will be displayed in a popup window:

XAML Copy imageCopy
<UserControl x:Class="Dapfor.Wpf.Grid.Demo.Examples.Basics.CustomFilters.SalesFilter"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             x:Name="ThisControl">
    <Border BorderThickness=".5">
        <Slider x:Name="Slider" Height="23" HorizontalAlignment="Stretch" Value="100" Maximum="100" />
    </Border>
</UserControl>

To transfer a column to the control in xaml code, we add a DependencyProperty of the Columntype:

C# Copy imageCopy
public partial class SalesFilter : UserControl
{
    public SalesFilter()
    {
        InitializeComponent();
    }

    #region ColumnProperty
    public static readonly DependencyProperty ColumnProperty = DependencyProperty.Register("Column", 
                                                                                            typeof (Column), 
                                                                                            typeof (SalesFilter), 
                                                                                            new FrameworkPropertyMetadata(null, OnChangeColumn));
    private static void OnChangeColumn(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
    }
    public Column Column
    {
        get { return (Column) GetValue(ColumnProperty); }
        set { SetValue(ColumnProperty, value); }
    }
    #endregion
}

After that we can use binding to set control background and foreground so that it does not differ too much from the current grid style. We can also add an event handler for the slider value change event. Full xaml code of our control now may look as follows:

XAML Copy imageCopy
<UserControl x:Class="Dapfor.Wpf.Grid.Demo.Examples.Basics.CustomFilters.SalesFilter"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             x:Name="ThisControl">
    <Border Background="{Binding ElementName=ThisControl, Path=Column.Header.Grid.Background}" BorderBrush="{Binding ElementName=ThisControl, Path=Column.Header.Grid.AppearanceColumnBackground}" BorderThickness=".5">
        <Slider x:Name="Slider" Height="23" HorizontalAlignment="Stretch" Value="100" Maximum="100" ValueChanged="OnSliderValueChanged" />
    </Border>
</UserControl>

Sorting conditions change when the slider position changes. We will create rules displaying only rows with values not exceeding the slider value.

C# Copy imageCopy
//Our filtration rules
private class FilterRules : IFilter
{
    private readonly Column _column;
    private readonly double _value;

    //The second parameter indicates the current value of our slider
    public FilterRules(Column column, double value)
    {
        _column = column;
        _value = value;
    }

    //This method is called each time when the GridControl needs to show or hide the row
    public bool IsFiltered(Row row)
    {
        var cell = row[_column.Id];
        var v = cell != null ? cell.Value : null;
        return (v as double?) > _value;
    }
}

public partial class SalesFilter : UserControl
{
   ...

    //Event handler is called when slider's value is changed
    private void OnSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        //Set a new filter
        Column.Filter = new FilterRules(Column, e.NewValue);
    }
}

It would be great to hide the control when user presses the Esc key. We will add a corresponding event handler and use the Column..::..CloseFilter()()()()method.

C# Copy imageCopy
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
    base.OnPreviewKeyDown(e);
    if (e.Key == Key.Escape)
    {
        Column.CloseFilter();
    }
}

We also need to preserve the current slider value for the case if user suddenly closes the control. We can do it by saving values in Column..::..Tag. Finally we will add the _suspendUpdates variable so that we don't have to recreate the IFilter object on control initialization. The full code of our control will now look as follows:

C# Copy imageCopy
public partial class SalesFilter : UserControl
{
    private bool _suspendUpdates;

    public SalesFilter()
    {
        InitializeComponent();
    }

    private void OnSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (!_suspendUpdates && Column != null)
        {
            Column.Tag = e.NewValue;
            Column.Filter = new FilterRules(Column, e.NewValue);
        }
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        base.OnPreviewKeyDown(e);
        if (e.Key == Key.Escape)
        {
            Column.CloseFilter();
        }
    }

    #region ColumnProperty
    public static readonly DependencyProperty ColumnProperty = DependencyProperty.Register("Column", typeof (Column), typeof (SalesFilter), new FrameworkPropertyMetadata(null, OnChangeColumn));
    private static void OnChangeColumn(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //Restore persisted data
        var control = (SalesFilter) d;
        if (control.Column != null && control.Column.Tag is double)
        {
            control._suspendUpdates = true;
            control.Slider.Value = (double) control.Column.Tag;
            control._suspendUpdates = false;
        }
    }
    public Column Column
    {
        get { return (Column) GetValue(ColumnProperty); }
        set { SetValue(ColumnProperty, value); }
    }
    #endregion
}

Setting our slider in grid column is quite easy. To do it, we will create DataTemplate resource and put our control inside it. Don't forget to transfer the produced column to the control:

XAML Copy imageCopy
<UserControl x:Class="SomeControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d">

  <UserControl.Resources>
      <!-- Sales templates -->
      <DataTemplate x:Key="SalesFilter">
          <customFilters:SalesFilter Column="{Binding}"/>
      </DataTemplate>
  </UserControl.Resources>  

</UserControl>

Finally we have to transfer our template to grid column:

XAML Copy imageCopy
<my:GridControl>
    <my:GridControl.Headers>
        <my:Header>
            <my:Header.Columns>
                <my:Column Id="sales" Title="Sales" FilterTemplate="{StaticResource SalesFilter}"/>
            </my:Header.Columns>
        </my:Header>
    </my:GridControl.Headers>
</my:GridControl>