Filter Row in WPF DataGrid (SfDataGrid)

SfDataGrid provides built-in row (called FilterRow) to filter the records. You can enable the FilterRow by specifying the position where it should be displayed by setting SfDataGrid.FilterRowPosition property.

<syncfusion:SfDataGrid x:Name="dataGrid"
                       FilterRowPosition="FixedTop"
                       AutoGenerateColumns="True"
                       ItemsSource="{Binding Orders}"/>
this.dataGrid.FilterRowPosition = FilterRowPosition.FixedTop;

WPF DataGrid with filter row

You can get the row index of FilterRow by using the SfDataGrid.GetFilterRowIndex method.

int filterRowIndex = this.dataGrid.GetFilterRowIndex();

You can check whether the specified row index is FilterRow index, by using SfDataGrid.IsFilterRowIndex helper method.

bool isFilterRowIndex = this.dataGrid.IsFilterRowIndex(1);

NOTE

The above helper methods are available in Syncfusion.UI.Xaml.Grid.Helpers namespace

Built-in Editors

By default, FilterRow loads the editors based on underlying property type to filter the data easily. You can change the default editors by using GridColumn.FilterRowEditorType property.

<syncfusion:GridTextColumn MappingName="CustomerName"
                           FilterRowEditorType="MultiSelectComboBox"/>
this.dataGrid.Columns[2].FilterRowEditorType = "MultiSelectComboBox";

WPF DataGrid filter row cell with multi select combobox

Below are the built-in FilterRow editor types supported in SfDataGrid.

FilterRowEditor Type

Editor Control

Renderer

Description

TextBox TextBox

GridFilterRowTextBoxRenderer

Used for filtering the string values.
Numeric DoubleTextBox

GridFilterRowNumericRenderer

Used for filtering the numeric values.
ComboBox ComboBoxAdv

GridFilterRowComboBoxRenderer

Used for filtering the specific value from the drop down.
MultiSelectComboBox ComboBoxAdv

GridFilterRowMultiSelectRenderer

Used for filtering the multiple values from the drop down.
CheckBox CheckBox

GridFilterRowCheckBoxRenderer

Used for filtering the Boolean values.
DateTime DateTimeEdit

GridFilterRowDateTimeRenderer

Used for filtering the DateTime values.

Filter options

Based on the editor type, FilterRowCell displays the filter conditions in dropdown where you can easily switch between the conditions to filter the data. You can disable filter options by setting GridColumn.FilterRowOptionsVisibility property.

<syncfusion:GridNumericColumn MappingName="OrderID" 
                              FilterRowOptionsVisibility="Collapsed"
                              FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].FilterRowOptionsVisibility = System.Windows.Visibility.Collapsed;

WPF DataGrid filter row

Below are the filter conditions supported by different filter row editors in SfDataGrid.

Numeric Editor

TextBox Editor

DateTime Editor

CheckBox Editor

ComboBox, MultiSelectComboBox Editor

When integer, double, short, decimal, byte or long are bound to the 

GridColumn

, the Numeric editor type are loaded in

FilterRowCell

.
When string value is bounded to the

GridColumn

or the items is dynamic, then TextBox editor type are loaded in

FilterRowCell

.
When DateTime type is bounded to the

GridColumn

, then DateTime editor is loaded in

FilterRowCell

.
When Boolean type is bounded to the

GridColumn

, then CheckBox editor is loaded in

FilterRowCell

.
If we need the ComboBox and MultiSelectComboBox we have to set the

FilterRowEditorType

.
The default filter condition is Equals, the below filter conditions are available in numeric filter.
  1. Equals
  2. Does Not Equal
  3. Null
  4. Not Null
  5. Less Than
  6. Less Than or Equal
  7. Greater Than
  8. Greater Than or Equal
The default filter condition is Begins With, the below filter conditions are available in text filter.
  1. Equals
  2. Does Not Equal
  3. Null
  4. Not Null
  5. Begins With
  6. Does Not Begin With
  7. Ends With
  8. Does Not End With
  9. Contains
  10. Does Not Contain
  11. Empty
  12. Not Empty
The default filter condition is Equals, the below filter conditions are available in date time filter.
  1. Equals
  2. Does Not Equal
  3. Null
  4. Not Null
  5. Before
  6. Before or Equal
  7. After
  8. After or Equal
Always equals filter condition will be applied for filtering the CheckBox value. Always equals or not equals filter condition will be applied based on selected items count for filtering the items.

You can change the default FilterRow condition for a corresponding column by using GridColumn.FilterRowCondition property.

<syncfusion:GridNumericColumn MappingName="OrderID" 
                              FilterRowCondition="LessThanOrEqual"
                              FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].FilterRowCondition = FilterRowCondition.LessThanOrEqual;

WPF DataGrid shows filter row with numeric editor

Filtering null values

You can enable or disable filtering of null values by setting GridColumn.AllowBlankFilters property. The default value is true.
When null value filtering is enabled, the filter options loaded with two additional options (“Null” and “Not Null”) to filter the null values. ComboBox and MultiSelectComboBox editors, loads with “Blanks” item in drop down to filter the null values.

<syncfusion:GridNumericColumn MappingName="OrderID" 
                              AllowBlankFilters="False"
                              FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].AllowBlankFilters = false;

WPF DataGrid filter row handles null value

<syncfusion:GridTextColumn MappingName="CustomerName"
                           AllowBlankFilters="True"
                           FilterRowEditorType="MultiSelectComboBox"/>
this.dataGrid.Columns[2].AllowBlankFilters = true;

WPF DataGrid loads multi select combobox in filter row cell

Instant Filtering

By default, filters are applied to the columns when moving to other cells or pressing enter key. You can apply filter when typing or selecting in editor itself by setting GridColumn.ImmediateUpdateColumnFilter as true.

<syncfusion:GridTextColumn MappingName="CustomerName"
                           FilterRowEditorType="MultiSelectComboBox"
                           ImmediateUpdateColumnFilter="True"/>
this.dataGrid.Columns[2].ImmediateUpdateColumnFilter = true;

WPF DataGrid Filter Row

Disable filtering for a particular FilterRowCell

By default, you can filter the records by editing filter row cell. You can disable this editing by using CurrentCellBeginEdit event.

this.dataGrid.CurrentCellBeginEdit += dataGrid_CurrentCellBeginEdit;

void dataGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{

    //Cancel the editing for filter row cell in OrderID Column

    if (args.Column.MappingName == "OrderID" && dataGrid.IsFilterRowIndex(args.RowColumnIndex.RowIndex))
        args.Cancel = true;
}

You can collapse the FilterOption button using FilterRowOptionsVisibility property.

<syncfusion:GridNumericColumn MappingName="OrderID" 
                              FilterRowOptionsVisibility="Collapsed"/>

Styling

Filter row style

You can customize the style of filter row by writing style of TargetType FilterRowControl.

<Window.Resources>
    <Style TargetType="syncfusion:FilterRowControl">
        <Setter Property="Background" Value="BlanchedAlmond"/>
    </Style>
</Window.Resources>

WPF DataGrid filter row style

Filter row - cell style

You can customize the style of filter row cell by writing style of TargetType GridFilterRowCell.

<local:FilterRowCellStyleConverter x:Key="filterRowCellStyleConverter"/>
<Style TargetType="syncfusion:GridFilterRowCell">
    <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource filterRowCellStyleConverter}}"/>
</Style>
public class FilterRowCellStyleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var filterRowCell = value as GridFilterRowCell;
        if (filterRowCell == null) return null;

        if (filterRowCell.DataColumn.GridColumn.MappingName == "OrderID" ||
            filterRowCell.DataColumn.GridColumn.MappingName == "CustomerName")
            return new SolidColorBrush(Colors.LightBlue);

        return new SolidColorBrush(Colors.Honeydew);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}

WPF DataGrid filter row cell style

Customizing filter row cell

You can customize the filter row cell by overriding the GridFilterRowCell. You have to override the GetGridCell method in RowGenerator to load the customized GridFilterRowCell.

public class GridFilterRowCellExt : GridFilterRowCell
{

    public GridFilterRowCellExt()
        : base()
    { }
}

public class CustomRowGenerator : RowGenerator
{

    public CustomRowGenerator(SfDataGrid dataGrid)
        : base(dataGrid)
    {
    }

    /// <summary>
    /// Return the Custom FilterRowCell
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>GridCell</returns>

    protected override GridCell GetGridCell<T>()
    {

        //If the Cell is FilterRowCell return custom FilterRowCell

        if (typeof(T) == typeof(GridFilterRowCell))
            return new GridFilterRowCellExt();
        return base.GetGridCell<GridCell>();
    }
}

public MainWindow()
{
    InitializeComponent();
    this.dataGrid.RowGenerator = new CustomRowGenerator(this.dataGrid);
}

Customizing filter options for particular columns

By default, TextBox editor will display the string related conditions like Begins With, Does Not Begin With, Ends With, Does Not End With, Contains, Does Not Contain, Empty, Not Empty filter row conditions in drop down. The below code shows how to display the custom filter row conditions in TextBox editor by overriding the OpenFilterOptionPopup method in a GridFilterRowCell class.

this.dataGrid.RowGenerator = new CustomRowGenerator(this.dataGrid);

public class GridFilterRowCellExt : GridFilterRowCell
{

    public GridFilterRowCellExt()
        : base()
    { }

    /// <summary>
    /// Opens the FilterOptionPopup with the FilterOptionList.
    /// </summary>
 
    public override void OpenFilterOptionPopup()
    {
        base.OpenFilterOptionPopup();
 
        if (this.DataColumn.GridColumn.MappingName != "CustomerID")
            return;
        var list = this.OptionsList();
 
        if (list.Count > 0)
            this.FilterOptionsList.ItemsSource = list;
    }

    /// <summary>
    /// Populates the FilterOption list which will loaded in FilterOptionPopup for ShipAddress.
    /// </summary>
    /// <returns></returns>
 
    private new ObservableCollection<string> OptionsList()
    {
        var list = new ObservableCollection<string>();
        list.Add("Contains");
        list.Add("Does not contain");
        list.Add("Match");
        list.Add("Does not match");
        list.Add("Like");
        list.Add("Not Like");
        return list;
    }
}

public class CustomRowGenerator : RowGenerator
{
    public CustomRowGenerator(SfDataGrid dataGrid)
        : base(dataGrid)
    {
    }

    /// <summary>
    /// Return the Custom FilterRowCell
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>GridCell</returns>
 
    protected override GridCell GetGridCell<T>()
    {
 
        //If the Cell is FilterRowCell return custom FilterRowCell
 
        if (typeof(T) == typeof(GridFilterRowCell))
            return new GridFilterRowCellExt();
        return base.GetGridCell<GridCell>();
    }
}

WPF DataGrid filter row cell customization

Customizing Filter row editors

Customizing the filter row renderer

SfDataGrid allows you to customize the filter row renderer behavior by overriding the corresponding renderer associated with the filter row cell. Each renderer have a set of virtual methods for handling the filter row behaviors. You can also create new renderers instead of overriding the existing renderer.
You can customize the default TextBox editor behavior by overriding GridFilterRowTextBoxRenderer class and add the custom renderer to FilterRowCellRenderers.

<syncfusion:GridTextColumn MappingName="CustomerName"
                                                     FilterRowEditorType="TextBoxExt"/>
public class GridFilterRowTextBoxRendererExt : GridFilterRowTextBoxRenderer
{

    public GridFilterRowTextBoxRendererExt()
        : base()
    {
    }
}
    
public MainWindow()
{
    InitializeComponent();
    this.dataGrid.FilterRowCellRenderers.Add("TextBoxExt", new GridFilterRowTextBoxRendererExt());
}

Filter based on numeric interval by using the multi select combobox filter

By default you can filter the multiple data in the column by using MultiSelectComboBox filter editor type, the below code shows how to filter the data based on range of numeric values by overriding the ProcessMultipleFilters method in GridFilterRowComboBoxRenderer class.

<syncfusion:GridNumericColumn MappingName="OrderID" 
                              FilterRowOptionsVisibility="Collapsed"
                              NumberDecimalDigits="0"
                              FilterRowEditorType="ComboBoxExt"/>
this.dataGrid.FilterRowCellRenderers.Add("ComboBoxExt", new GridFilterRowComboBoxRendererExt());

public class GridFilterRowComboBoxRendererExt : GridFilterRowComboBoxRenderer, INotifyPropertyChanged
    {
        private List<string> numericComboBoxItems;

 
        public GridFilterRowComboBoxRendererExt()
            : base()
        {
            SetNumericComboBoxItemsList();
        }
             
        /// <summary>
        /// Generate the Items for NumericComboBox
        /// </summary>
        /// <returns></returns>
 
        public void SetNumericComboBoxItemsList()
        {
            numericComboBoxItems = new List<string>();
            numericComboBoxItems.Add("Between 1001 and 1004");
            numericComboBoxItems.Add("Between 1005 and 1009");
            numericComboBoxItems.Add("Between 1010 and 1014");
            numericComboBoxItems.Add("Between 1015 and 1020");
            numericComboBoxItems.Add(">1020");
        }

        /// <summary>
        /// InitializeEditBinding based on our item, set the SelectedItem and set the ItemSource.
        /// </summary>
        /// <param name="uiElement">Corresponding UIElement</param>
        /// <param name="dataColumn">Corresponding Column</param>

        protected override void InitializeEditBinding(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement, DataColumnBase dataColumn)
        {
            ObservableCollection<object> selItems = new ObservableCollection<object>();

                //Generate the items for FilterRow                 
                uiElement.ItemsSource = numericComboBoxItems;

                if (dataColumn.GridColumn.FilteredFrom == FilteredFrom.FilterRow && dataColumn.GridColumn.FilterPredicates.Count > 0)
                {

                    if (numericComboBoxItems != null)
                    {
                        numericComboBoxItems.ForEach(element =>
                        {

                            //Check if the filter is already applied or not, if applied means again add the filter
                            bool needToAdd = false;

                            switch (element)
                            {

                                case "Between 1001 and 1004":
                                    needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1001");
                                    break;

                                case "Between 1005 and 1009":
                                    needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1005");
                                    break;

                                case "Between 1010 and 1014":
                                    needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1010");
                                    break;

                                case "Between 1015 and 1020":
                                    needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1015");
                                    break;

                                case ">1020":
                                    needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1020");
                                    break;
                            }

                            if (needToAdd)
                                selItems.Add(element);
                        });
                    }
                }
          
            if (selItems.Count > 0)
                uiElement.SelectedItems = selItems;

            else if (uiElement.SelectedItems != null)
                uiElement.SelectedItems = null;
            uiElement.AllowMultiSelect = true;
            uiElement.AllowSelectAll = true;
            uiElement.EnableOKCancel = true;
            uiElement.IsEditable = false;
        }

        /// <summary>
        /// Check whether the column having a FilterPredicate or not
        /// </summary>
        /// <param name="filterPredicate">FilterPredicates for a column</param>
        /// <param name="filterValue">FilterValue for a column</param>
        /// <returns></returns>

        private bool NeedToAdd(ObservableCollection<FilterPredicate> filterPredicate, string filterValue)
        {
            bool needToAdd = false;

            foreach (var item in filterPredicate)
            {

                if ((item as FilterPredicate).FilterValue.ToString() == filterValue)
                {
                    needToAdd = true;
                    break;
                }
            }
            return needToAdd;
        }

        /// <summary>
        /// Generate the FilterPredicates and apply the filter for a corresponding column
        /// </summary>
        /// <param name="filterValues">Corresponding Filter Value</param>
        /// <param name="totalItems">Corresponding Filter Items</param>

        public override void ProcessMultipleFilters(List<object> filterValues, List<object> totalItems)
        {
            var selectedItems = filterValues.Cast<string>().ToList();
            var total = totalItems.Cast<string>().ToList();

            if (selectedItems == null || total == null || filterValues == null)
                return;

            if (selectedItems.Count == total.Count)
            {
                this.ApplyFilters(null, string.Empty);
                this.IsValueChanged = false;
                return;
            }
            var filterPredicates = new List<FilterPredicate>();

            if (filterValues.Count > 0)
            {
                selectedItems.ForEach(item =>
                {

                    switch (item)
                    {

                        case "Between 1001 and 1004":
                            filterPredicates.Add(GetFilterPredicates((int)1001, FilterType.GreaterThan, PredicateType.OrElse));
                            filterPredicates.Add(GetFilterPredicates((int)1004, FilterType.LessThan, PredicateType.And));
                            break;

                        case "Between 1005 and 1009":
                            filterPredicates.Add(GetFilterPredicates((int)1005, FilterType.GreaterThan, PredicateType.OrElse));
                            filterPredicates.Add(GetFilterPredicates((int)1009, FilterType.LessThan, PredicateType.And));
                            break;

                        case "Between 1010 and 1014":
                            filterPredicates.Add(GetFilterPredicates((int)1010, FilterType.GreaterThan, PredicateType.OrElse));
                            filterPredicates.Add(GetFilterPredicates((int)1014, FilterType.LessThan, PredicateType.And));
                            break;

                        case "Between 1015 and 1020":
                            filterPredicates.Add(GetFilterPredicates((int)1015, FilterType.GreaterThan, PredicateType.OrElse));
                            filterPredicates.Add(GetFilterPredicates((int)1020, FilterType.LessThan, PredicateType.And));
                            break;

                        case ">1020":
                            filterPredicates.Add(GetFilterPredicates((int)1020, FilterType.GreaterThan, PredicateType.Or));
                            break;
                    }
                });
            }
            string _filterText = string.Empty;
  
            //Creates the FilterText
  
            if (filterPredicates.Count > 0)
            {
                var selectItems = ((IList)filterValues).Cast<string>().ToList();
  
                for (int i = 0; i < selectedItems.Count; i++)
                {
                    _filterText += selectedItems[i];
  
                    if (i != selectedItems.Count - 1)
                        _filterText += " - ";
                }
             }
  
            if (filterPredicates != null)
                this.ApplyFilters(filterPredicates, _filterText);
            this.IsValueChanged = false;
        }

        /// <summary>
        /// Generate the Filter Predicates for Strongly Typed
        /// </summary>
        /// <param name="value"></param>
        /// <param name="filterType"></param>
        /// <param name="predType"></param>
        /// <returns></returns>
 
        private FilterPredicate GetFilterPredicates(object value, FilterType filterType, PredicateType predType)
        {
            return new FilterPredicate()
            {
                FilterBehavior = FilterBehavior.StronglyTyped,
                FilterType = filterType,
                FilterValue = value,
                IsCaseSensitive = false,
                PredicateType = predType
            };
        }
        /// <summary>
        /// Generate the Filter Predicates for String Typed
        /// </summary>
        /// <param name="value"></param>
        /// <param name="filterType"></param>
        /// <param name="predType"></param>
        /// <returns></returns>

        private FilterPredicate GetStringFilterPredicates(object value, FilterType filterType, PredicateType predType)
        {
            return new FilterPredicate()
            {
                FilterBehavior = FilterBehavior.StringTyped,
                FilterType = filterType,
                FilterValue = value,
                IsCaseSensitive = false,
                PredicateType = predType
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(String prop)
        {

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
        }
    }

Displaying filter row MultiSelectComboBox editor customization in WPF Sfdatagrid

Numeric filter row conditions for string typed column

By default, TextBox filter is loaded when underlying property type is string which is bound to a column. The below code shows how to apply the numeric filter row conditions for that particular column using converters in ValueBinding property.
You can filter the decimal value in a filter row cell by overriding the OnInitializeEditElement method in GridFilterRowNumericRenderer class.

<syncfusion:GridTextColumn MappingName="Freight"
                           UseBindingValue="True" 
                           ValueBinding="{Binding Path=Freight, Converter={StaticResource stringToNumericConverter}}"/>
public class StringToNumericConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return double.Parse(value.ToString());
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

//Remove the existing renderer

if (dataGrid.FilterRowCellRenderers.ContainsKey("Numeric"))
    dataGrid.FilterRowCellRenderers.Remove("Numeric");

//Add the new custom renderer
dataGrid.FilterRowCellRenderers.Add("Numeric", new GridFilterRowNumericRendererExt());

public class GridFilterRowNumericRendererExt : GridFilterRowNumericRenderer
{

    public override void OnInitializeEditElement(DataColumnBase dataColumn, DoubleTextBox uiElement, object dataContext)
    {
        base.OnInitializeEditElement(dataColumn, uiElement, dataContext);

        //Set the NumericDecimalDigits
        uiElement.NumberDecimalDigits = 2;
    }
}

Customizing GridFilterRowMultiSelectRenderer

By default, in SfDataGrid ComboBox is loaded while enter into edit mode in FilterRow but you can customize the [GridFilterRowMultiSelectRenderer] (https://help.syncfusion.com/cr/wpf/Syncfusion.UI.Xaml.Grid.RowFilter.GridFilterRowMultiSelectRenderer.html) for display the combobox while FilterRow loading itself.

<Syncfusion:GridTextColumn MappingName="OrderID" HeaderText="OrderID" FilterRowEditorType="MultiSelectComboBox"/>
sfgrid.FilterRowCellRenderers.Remove("MultiSelectComboBox");
sfgrid.FilterRowCellRenderers.Add("MultiSelectComboBox", new GridMultiSelectComboBoxRendererExt());

public class GridMultiSelectComboBoxRendererExt: GridFilterRowMultiSelectRenderer
{

    public GridMultiSelectComboBoxRendererExt():base()
    {
        SupportsRenderOptimization = false;
        IsEditable = false;
    }

    protected override void OnWireEditUIElement(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement)
    {
        base.OnWireEditUIElement(uiElement);
        uiElement.PreviewMouseDown += uiElement_PreviewMouseDown;
    }

    void uiElement_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {

        if (this.HasCurrentCellState && this.CurrentCellRendererElement == sender)
            return;

        else
        {
            var position = e.GetPosition(DataGrid.GetVisualContainer());
            var rowColumnIndex = DataGrid.GetVisualContainer().PointToCellRowColumnIndex(position, false);
            DataGrid.SelectionController.MoveCurrentCell(rowColumnIndex, true);
        }
    }

    protected override void OnUnwireEditUIElement(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement)
    {
        base.OnUnwireEditUIElement(uiElement);
        uiElement.PreviewMouseDown -= uiElement_PreviewMouseDown;
    }
}

WPF DataGrid filter row

You can get the sample from here.

See Also

How to load the symbols in FilterRow and perform actions based on that ?

How to show filter status message in SfDataGrid?