Filtering is an important element that simplifies user navigation over large data volumes by displaying only the requested data. The main requirements to filtering are:

  • Convenient API for implementing simple filters and composite filters that filter data by different criteria at different hierarchy levels.
  • Filtered rows should preserve their state (position, expansion, selection, place in hierarchy and existence of children) so that on change of filtering criteria they could be displayed in positions where they were previously hidden.
  • High filtering performance to prevent GUI slowdown when large data volumes are filtered.
  • Filtering of dynamic data (i.e. data objects) should be checked for compliance with filtering criteria upon every change. It’s best to implement it through notifications from INotifyPropertyChanged and IBindingList interfaces.
  • Data filtering should work for classical data presentation (with or without hierarchy) and grouping. Groups that contain only filtered rows should be filtered out as well. Programming API should remain unchanged and should be as simple to implement as possible.
  • Data filtering should be thread-safe without regard to threads used by data objects.
  • Data filtering should work without regard to the method of object placement in the grid. By calling GridControl.Rows.Add(Object) method or a binging list, the grid follows all requirements and provides a simple and convenient interface.
Wpf GridControl custom filters

The grid provides a simple IFilter interface with a single IFilter..::..IsFiltered(Row) method for filter implementation. This method is called every time when new data is added to the grid or when the grid gets a notification from INotifyPropertyChanged or IBindingList interfaces or when Row..::..Update()()()() method is called. This implementation works independently of hierarchy or data grouping and provides the programmer with a simple and convenient interface.

C# Copy imageCopy
public class Product : INotifyPropertyChanged
{
    private double price;

    public double Price
    {
        get { return price; }
        set
        {
            //If the price is not the same, change it and notify about price changing
            if (price != value)
            {
                price = value;
                //The event can be raised from any thread. The grid will synchronize thread with GUI without blocking the calling thread.
                //While painting, sorting or filtering the grid can ask this object in the GUI (!) thread to return the price value.
                if(PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Price"));
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public void CreateFilter(GridControl grid)
{
    //Set a filter that hides all rows, that contain products with price less than 10
    grid.Filter = new Filter(delegate(Row row)
    {
        //There are three ways to get price: 
        //1. From the Cell through the Value property, which returns a double value: row["Price"].Value
        //2. Through the IDataAccessor and IDataField: row.DataAccessor["Price"].Value
        //3. From the data object itself: ((Product)row.DataObject).Price

        if ((double)row["Price"].Value < 10)
        {
            //Filter the row
            return true;
        }

        //The row is not filtered
        return false;
    });
}

//Populate the grid
public void PopulateGrid(GridControl grid)
{
    Product product1 = new Product();
    Product product2 = new Product();

    grid.Rows.Add(product1);
    grid.Rows.Add(product2);

    //Update the product's price
    //Data objects will notify the GridControl, and it will display only the product2. 
    //The product1 will be hidden
    product1.Price = 9;
    product2.Price = 11;
}