Filtering in WPF DataGrid (SfDataGrid)

10 Nov 202324 minutes to read

Filtering is the process of retrieving the values from the collection which satisfy the specified condition. In the SfDataGrid the filtering can be applied though the UI as well as the programmatic filters.

Programmatic filtering

The WPF DataGrid Filter allows you to filter the data programmatically in below ways,

  • Through View Predicate
  • Through Column Filter

View Filtering

View filtering can be achieved by setting SfDataGrid.View.Filter delegate. You can refresh the view by calling SfDataGrid.View.RefreshFilter method.

Here, FilterRecords delegate filters the data based on Country name. FilterRecords delegate is assigned to SfDataGrid.View.Filter predicate to filter Country column. After that, SfDataGrid.View.RefreshFilter method is called to refresh the records. If the record satisfies the filter conditions, true will be returned. Else false is returned.

public bool FilterRecords(object o)
{
    string filterText = "Germany";
    var item = o as OrderInfo;

    if (item != null)
    {

        if (item.Country.Equals(filterText))
            return true;
    }
    return false;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    dataGrid.View.Filter = FilterRecords;
    dataGrid.View.RefreshFilter();
}

NOTE

View filter is not supported when ItemsSource is DataTable.

Column Filtering

Column filtering is achieved by using GridColumn.FilterPredicates property and adding FilterPredicate to it.

Here, OrderID column is filtered for the data which has OrderID as 1005.

dataGrid.Columns["OrderID"].FilterPredicates.Add(new FilterPredicate() { FilterType = FilterType.Equals, FilterValue = "1005" });

Filter Behavior

The FilterBehavior property is used to specify whether to consider the FilterValue as the string or specific data type.

  • StringTyped - Records are filtered without considering the type and it takes FilterValue type as string.
  • StronglyTyped - Records are filtered by considering the FilterValue underlying type.

NOTE

When you use DataTable as items Source, IsCaseSensitive property in FilterPredicate is not applicable, since DataTable does not support CaseSensitive filtering.

Improving performance while adding multiple FilterPredicates to the column in loop

You can improve the performance of filtering by suspending the data operation while adding FilterPredicates to the column for bulk updates by calling SfDataGrid.View.BeginInit and SfDataGrid.View.EndInit method, before and after the data operation.

private void OnApplyFilterPredicate(object obj)
{
    var dataGrid = obj as SfDataGrid;            
    var gridColumn = dataGrid.Columns["EmployeeId"];
    dataGrid.View.BeginInit();
    foreach (var filterValue in FilterValues)
    {
        gridColumn.FilterPredicates.Add(new FilterPredicate()
        {
            FilterType = FilterType.Equals,
            FilterValue = filterValue,
            FilterBehavior = FilterBehavior.StronglyTyped,
            FilterMode = ColumnFilter.Value,
            PredicateType = PredicateType.Or,
            IsCaseSensitive = true
        });
    }            
    dataGrid.View.EndInit();
}

Clear Filtering

The WPF DataGrid (SfDataGrid) allows you to clear the filters by clearing the filter predicates. This is achieved by invoking the following methods.

this.dataGrid.ClearFilters();
this.dataGrid.ClearFilter("OrderID");
this.dataGrid.ClearFilter(this.dataGrid.Columns[0]);

Excel like UI Filtering

The WPF DataGrid (SfDataGrid) provides excel like filtering UI and also advanced filter UI to filter the data easily. UI filtering can be enabled by setting SfDataGrid.AllowFiltering property to true , where you can open filter UI by clicking the Filter icon in column header and filter the records.

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

You can enable/disable filtering for particular column by setting GridColumn.AllowFiltering property.

<syncfusion:GridTextColumn AllowFiltering="True" 
                           MappingName="OrderID" />
dataGrid.Columns["OrderID"].AllowFiltering = true;

NOTE

  1. GridColumn.AllowFiltering has higher priority than SfDataGrid.AllowFiltering property.
  2. UI filtering is not supported when using on-demand paging by setting UseOnDemandPaging to true.

Built-in UI Views

SfDataGrid filter UI comprises of two different UIs.

  • Checkbox Filter UI - Provides excel like filter interface with list of check box’s.

  • Advanced Filter UI - Provides advanced filter options to filter the data.

By default, both Checkbox Filter and Advanced Filter are loaded while opening the filter pop-up. You can switch between AdvancedFilter and CheckboxFilter by using AdvancedFilter button in the UI View.

SfDataGrid with Checkbox Filter View:

WPF DataGrid with CheckBox Filter

SfDataGrid with Advanced Filter View:

WPF DataGrid with Advanced Filter

Choose between built-in UI Views

The WPF DataGrid (SfDataGrid) lets you to customize the UI Views displayed for particular column or grid using FilterMode property in GridFilterControl.

Below are the options,

  1. CheckboxFilter – Displays only Checkbox filter View.
  2. AdvancedFilter – Displays only Advanced filter View.
  3. Both – Displays both filters Views.

Changing filter UI View for Grid

Filter UI view can be changed for all the columns in grid by changing FilterMode in GridFilterControl by writing style and assign it to SfDataGrid.FilterPopupStyle.

<Style x:Key="filterControlStyle" TargetType="syncfusion:GridFilterControl">
    <Setter Property="FilterMode" Value="AdvancedFilter" />
</Style>

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AllowFiltering="True"
                       AutoGenerateColumns="True"
                       FilterPopupStyle="{StaticResource filterControlStyle}"
                       ItemsSource="{Binding Orders}"/>

Changing filter UI View for columns

Filter UI view can be changed for the particular column by changing FilterMode in GridFilterControl by writing style and assign it to GridColumn.FilterPopupStyle.

<Style x:Key="filterControlStyle" TargetType="syncfusion:GridFilterControl">
       <Setter Property="FilterMode" Value="AdvancedFilter" />
</Style>

<syncfusion:GridTextColumn MappingName="OrderID"
                           FilterPopupStyle="{StaticResource filterControlStyle}"  />

Changing filter UI View programmatically

You can change FilterMode programmatically by using FilterItemsPopulating event.

this.dataGrid.FilterItemsPopulating += dataGrid_FilterItemsPopulating;

void dataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.Grid.GridFilterItemsPopulatingEventArgs e)
{

     if (e.Column.MappingName == "OrderID")
           e.FilterControl.FilterMode = FilterMode.AdvancedFilter;
}

Setting Default Filter popup style for particular column

You can skip the GridFilterControl styling for particular column from SfDataGrid.FilterPopupStyle by setting GridColumn.FilterPopupStyle to null.

<Window.Resources>
    <Style x:Key="filterControlStyle" TargetType="syncfusion:GridFilterControl">
        <Setter Property="FilterMode" Value="AdvancedFilter" />
    </Style>
</Window.Resources> 

<syncfusion:SfDataGrid Name="dataGrid"
                       AllowFiltering="True"
                       FilterPopupStyle="{StaticResource filterControlStyle}"
                       ItemsSource="{Binding OrderList}">
    <syncfusion:SfDataGrid.Columns>
        <syncfusion:GridTextColumn FilterPopupStyle="{x:Null}" MappingName="OrderID" />          
    </syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>
this.dataGrid.Columns["OrderID"].FilterPopupStyle = null;

Here, advanced filter will be loaded for all the columns in grid except OrderID column since GridColumn.FilterPopupStyle is set as null for OrderID column. So both checkbox filter and advanced filter (default style) will be loaded for OrderID column.

Advanced Filter UI

Advanced filter UI provides multiple filter options to filter the data easily. Filter menu options are loaded based on Advanced filter type by automatically detecting the underlying date type.

Below are the built-in filter types supported.

  • Text Filters – Loads various menu options to filter the display text effectively.
  • Number Filters – Loads various menu options to filter the numeric data.
  • Date Filters – Loads various menu options and DatePicker to filter DateTime type column.
Text Filters Number Filters Date Filters
When the string value is bounded to the

GridColumn

or the items source is

dynamic

,then TextFilters are loaded in

AdvancedFilterControl

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

GridColumn

then Number Filters are loaded in

AdvancedFilterControl

.
When the DateTime type value is bound to the

GridColumn

, then Date Filters are loaded in

AdvancedFilterControl

.
WPF DataGrid displays Text Filter WPF DataGrid displays Number Filter WPF DataGrid displays Date Filter
Filter menu options
  1. Equals
  2. Does Not Equal
  3. Begins With
  4. Does Not Begin With
  5. Ends With
  6. Does Not End With
  7. Contains
  8. Does Not Contain
  9. Empty
  10. Not Empty
  11. Null
  12. Not Null
Filter menu options
  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
Filter menu options
  1. Equals
  2. Does Not Equal
  3. Before
  4. Before Or Equal
  5. After
  6. After Or Equal
  7. Null
  8. Not Null

NOTE

  1. Null and Not Null options are available only when AllowBlankFilters is set to True.
  2. If the column is GridUnboundColumn or GridMaskColumn, then Text Filters will be loaded.

Changing Advanced Filter type

FilterBehavior determines the Advanced filter type loaded in GridFilterControl. By using FilterBehavior, you can change Advanced filter type.

  • StringTyped - TextFilters will be loaded in AdvancedFilterControl.
  • Strongly Typed – Advanced filter type is automatically detected based on underlying data type.
<syncfusion:SfDataGrid.Columns>
    <syncfusion:GridTextColumn MappingName="OrderID" FilterBehavior="StringTyped"/>
</syncfusion:SfDataGrid.Columns>
dataGrid.Columns["OrderID"].FilterBehavior = FilterBehavior.StringTyped;

Advanced filter type can be changed programmatically by using FilterItemsPopulating event also.

this.dataGrid.FilterItemsPopulating += dataGrid_FilterItemsPopulating;

void dataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.Grid.GridFilterItemsPopulatingEventArgs e)
{

    if (e.Column.MappingName != "OrderID")
        return;
    e.FilterControl.AdvancedFilterType = AdvancedFilterType.TextFilter;
    e.FilterControl.SetColumnDataType(typeof(string));
    e.FilterControl.AscendingSortString = GridResourceWrapper.SortStringAscending;
    e.FilterControl.DescendingSortString = GridResourceWrapper.SortStringDescending;
}

Case Sensitive

By default, casing is not considered while filtering. Because, filter predicates will be created with IsCaseSensitive as false. If you want to filter the records with IsCaseSensitive as true, you need to click case sensitive button present in Advanced Filter.

NOTE

When you use DataTable as items Source, CaseSensitive button will not be available in Filter popup as DataTable does not support CaseSensitive filtering.

Performance tips

GridFilterControl’s loading performance can be increased by setting FilterMode as AdvancedFilter and CanGenerateUniqueItems as False. Because a textbox is loaded instead of AdvancedFilter ComboBox that allows you to manually enter text for filtering.

<Style TargetType="syncfusion:AdvancedFilterControl">
    <Setter Property="CanGenerateUniqueItems" Value="False" />
</Style>

<Style x:Key="filterControlStyle" TargetType="syncfusion:GridFilterControl">
    <Setter Property="FilterMode" Value="AdvancedFilter" />
</Style>

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AllowFiltering="True"
                       AutoGenerateColumns="True"
                       FilterPopupStyle="{StaticResource filterControlStyle}"
                       ItemsSource="{Binding Orders}"/>

Improving Performance while Applying Filter in WPF DataGrid

By default, CanGenerateUniqueItems is true. So all the unique items in the column are loaded in the AdvancedFilter ComboBox that allows you to select the value easily from the combo box and filter it.

Filtering null values

To filter the null values, you need to set AllowBlankFilters property as True. So null values will be included in filter items list. If you want to exclude the null values from filter items list, you need to set AllowBlankFilters as False.

<syncfusion:GridTextColumn AllowBlankFilters="False" MappingName="Country" />
dataGrid.Columns["Country"].AllowBlankFilters = false;

Checkbox Filter with AllowBlankFilters as True

Filter Null Values using CheckBox Filter in WPF DataGrid

Advanced Filter with AllowBlankFilters as True

Filter Null Values using Advanced Filter in WPF DataGrid

Instant Filtering

By default, filters are applied to the columns when OK button is clicked in UI filtering. If you want to update the filters immediately whenever update in filter popup, you need to set ImmediateUpdateColumnFilter as True.

<syncfusion:GridTextColumn ImmediateUpdateColumnFilter="True" MappingName="OrderID" />
dataGrid.Columns["OrderID"].ImmediateUpdateColumnFilter = true;

Here, the OK and Cancel buttons are unavailable and Done button is available to just close the popup.

Checkbox Filter with ImmediateUpdateColumnFilter is True

CheckBox Filter with Immediate Filter in WPF DataGrid

Advanced Filter with ImmediateUpdateColumnFilter is True

Advanced Filter with Immediate Filter in WPF DataGrid

NOTE

In Checkbox Filter, the SelectAll option is not reflected in the filter updates if ImmediateUpdateColumnFilter is true.

Filtering based on DisplayText

In UI filtering, records are filtered based on actual value by default. If you want to filter the records based on DisplayText, you need to set ColumnFilter property as DisplayText.

<syncfusion:GridDateTimeColumn MappingName="OrderDate"  ColumnFilter="DisplayText"/>
dataGrid.Columns["OrderDate"].ColumnFilter = ColumnFilter.DisplayText;

Consider in the following dataGrid, first and second records have same display value for OrderDate column but both have different actual value (E.g. 2/10/2010 12:00:00 AM and 2/10/2010 6:30:00 PM).

Filter WPF DataGrid using Actual Value instead of Formatted String

By default, based on the actual value only filter will be applied. So it will consider both values as different. And while opening filter popup, both values will be displayed like below.

Filtering WPF DataGrid based on Formatted String

If you set ColumnFilter as DisplayText, display value only will be considered for filtering. So filter popup will be shown like below.

Filter based on actual value in WPF DataGrid

After filtering, both records having the same OrderDate display value will be displayed in view.

Filtered same Record Values in WPF DataGrid

Events

SfDataGrid provides the following events for filtering.

FilterChanging event

FilterChanging event is raised while applying filters to a particular column. You can use this event to change the FilterPredicates, FilterType and FilterBehavior.

this.dataGrid.FilterChanging += dataGrid_FilterChanging;

void dataGrid_FilterChanging(object sender, GridFilterEventArgs e)
{
}

FilterChanged event

FilterChanged event is raised after filter is applied. You can use this event to get filtered records.

this.dataGrid.FilterChanged += dataGrid_FilterChanged;

void dataGrid_FilterChanged(object sender, GridFilterEventArgs e)
{
}

FilterItemsPopulating event

FilterItemsPopulating event is raised while populating the filter list items in GridFilterControl. You can change GridFilterControl properties by using this event.

this.dataGrid.FilterItemsPopulating += dataGrid_FilterItemsPopulating;

void dataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.Grid.GridFilterItemsPopulatingEventArgs e)
{
}

FilterItemsPopulated event

FilterItemsPopulated event is raised after filter list items are populated. You can change GridFilterControl ItemSource by using this event.

this.dataGrid.FilterItemsPopulated += dataGrid_FilterItemsPopulated;

void dataGrid_FilterItemsPopulated(object sender, GridFilterItemsPopulatedEventArgs e)
{
}

Getting the filtered records

You can get the filtered records from View in FilterChanged event. When filter is applied, the filtered records are available in View.Records.

this.dataGrid.FilterChanged += dataGrid_FilterChanged;
void dataGrid_FilterChanged(object sender, GridFilterEventArgs e)
{

       //OrderInfo is Model Class 
       ObservableCollection<OrderInfo> order = new  ObservableCollection<OrderInfo>();

       // Get filtered records
       var records = (sender as SfDataGrid).View.Records;

       foreach (RecordEntry record in records)
            order.Add(record.Data as OrderInfo);
}

Show image in CheckBoxFilterControl instead of image path

By default, in SfDataGrid image path is shown inside the CheckBoxFilterControl instead of image but you can show the image in CheckBoxFilterControl by setting CheckBoxFilterControl.ItemTemplate as like below.

<syncfusion:GridTextColumn AllowEditing="False" HeaderText="Country" MappingName="ImageLink">
                    <syncfusion:GridTextColumn.FilterPopupStyle>
                        <Style TargetType="syncfusion:GridFilterControl">
                            <Setter Property="CheckboxFilterStyle">
                                <Setter.Value>
                                    <Style TargetType="syncfusion:CheckboxFilterControl">
                                        <Setter Property="Background" Value="Red"/>
                                        <Setter Property="ItemTemplate">
                                            <Setter.Value>
                                                <DataTemplate>
                                                    <CheckBox Margin="4"
            HorizontalAlignment="Stretch"
            HorizontalContentAlignment="Stretch"
            Focusable="False"
            Content="{Binding}"          
            FontWeight="{Binding FontWeight,RelativeSource={RelativeSource Self}}"
            Foreground="{Binding Foreground,RelativeSource={RelativeSource Self}}"
            IsChecked="{Binding IsSelected,
                                Mode=TwoWay}">
                                                        <CheckBox.ContentTemplate>
                                                            <DataTemplate>
                                                                <Image Source="{Binding Path=ActualValue, Converter={StaticResource stringToImageConverter}}"
                                                                       HorizontalAlignment="Left"
                                                                       Height="25"/>
                                                            </DataTemplate>
                                                        </CheckBox.ContentTemplate>
                                                    </CheckBox>
                                                </DataTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </syncfusion:GridTextColumn.FilterPopupStyle>
                    <syncfusion:GridTextColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <Image Source="{Binding Path=ImageLink,Converter={StaticResource stringToImageConverter}}"/>
                            </Grid>
                        </DataTemplate>
                    </syncfusion:GridTextColumn.CellTemplate>
</syncfusion:GridTextColumn>
public class StringToImageConverter : IValueConverter
{     

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string imagename = value as string;
        return new BitmapImage(new Uri(string.Format(@"..\..\Images\{0}", imagename), UriKind.Relative));
    }
        
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Filter with Image in WPF DataGrid Column

You can get the sample from here.

Apply ICollectionView.Filter and DataView.RowFilter on initial loading

By default, the default filter created by ICollectionView.Filter and DataView.RowFilter will not be applied to the data on initial loading. These filters can be applied on initial loading by enabling CanUseViewFilter property.

Functionality Customization

Loading the Text Filters for the column having Number or Date value as underlying type

If you want to use the Text Filters for the column that has number or date value as underlying type, you need to set FilterBehavior property of the GridColumn as StringTyped. This loads the Text Filters instead of Number or Date Filters.

<syncfusion:GridNumericColumn MappingName="OrderID" FilterBehavior="StringTyped"/>

You can achieve this programmatically by using FilterItemsPopulating event also.

this.dataGrid.FilterItemsPopulating += dataGrid_FilterItemsPopulating;

void dataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.Grid.GridFilterItemsPopulatingEventArgs e)
{

    if (e.Column.MappingName == "OrderID")
        e.FilterControl.AdvancedFilterType = AdvancedFilterType.TextFilter;
    e.FilterControl.SetColumnDataType(typeof(string));
    e.FilterControl.AscendingSortString =  GridResourceWrapper.SortStringAscending;
    e.FilterControl.DescendingSortString = GridResourceWrapper.SortStringDescending;
}

Changing AdvancedFilter type while loading dynamic ItemsSource

By default, TextFilters will be loaded for the columns if ItemsSource is dynamic. If you want to load Number Filters or Date Filters based on column values, you need to use ColumnMemberType property.

this.dataGrid.Columns["OrderID"].ColumnMemberType = typeof(double?);

You can achieve this by using FilterItemsPopulating event also. But in this case, Nullable type values will not be filtered in advanced filtering. So you need to set ColumnMemberType.

Customizing Excel like Filter ItemsSource

When you want to restrict some data from filtering, you need to customize the GridFilterControl ItemsSource by using FilterItemsPopulated event. Here,FilterElement which has ActualValue as 1005 is removed from itemsSource.

this.dataGrid.FilterItemsPopulated += dataGrid_FilterItemsPopulated;

void dataGrid_FilterItemsPopulated(object sender, GridFilterItemsPopulatedEventArgs e)
{

    if (e.Column.MappingName == "OrderID")
    {
        var itemsSource = e.ItemsSource as List&lt;FilterElement&gt;;

        //Get the FilterElement to Remove from itemsSource.
        var filterElement = itemsSource.FirstOrDefault(items => items.ActualValue.Equals(1005));

        //Remove the FilterElement from itemsSource.
        itemsSource.Remove(filterElement);               
    }
}

Likewise,FilterElement also can be changed.

Customizing Filter predicates

If you want to customize the filter predicates, you need to use FilterChanging event. Here, FilterValue is changed according to some conditions.

this.dataGrid.FilterChanging += dataGrid_FilterChanging;

void dataGrid_FilterChanging(object sender, GridFilterEventArgs e)
{

    if (e.FilterPredicates == null || e.Column.MappingName != "CustomerID" ||         e.FilterPredicates.Count == 0)
        return;           

    if (e.FilterPredicates[0].FilterValue.Equals("ALFKI"))
        e.FilterPredicates[0].FilterValue = "ANATR";          
}

Appearance customization

GridFilterControl is derived from ContentControl and has its own structure. This structure is customized using the properties SfDataGrid.FilterPopupStyle and SfDataGrid.FilterPopupTemplate for all the columns in grid.

When you need to change the appearance of the GridFilterControl for a particular column, GridColumn.FilterPopupStyle and GridColumn.FilterPopupTemplate properties are used.

Collapsing Sort Options in GridFilterControl

Sort Options can be collapsed by setting SortOptionVisibility property in GridFilterControl.

<Style TargetType="syncfusion:GridFilterControl" x:Key="gridFilterControlStyle">
    <Setter Property="SortOptionVisibility" Value="Collapsed"/>
</Style>

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AllowFiltering="True"
                       AutoGenerateColumns="True"
                       FilterPopupStyle="{StaticResource  gridFilterControlStyle}"
                       ItemsSource="{Binding Orders}"/>

Collapse sort option from filter popup in WPF DataGrid

Customizing Sort Options text

Sort Options text can be customized by using AscendingSortString and DescendingSortString properties in GridFilterControl.

this.dataGrid.FilterItemsPopulating += DataGrid_FilterItemsPopulating;

void DataGrid_FilterItemsPopulating(object sender, GridFilterItemsPopulatingEventArgs e)
{

    if(e.Column.MappingName=="CustomerName")
    {
        e.FilterControl.AscendingSortString = "Sort Ascending";
        e.FilterControl.DescendingSortString = "Sort Descending";
    }           
}

Customize the FilterPopup size using GridFilterControl style

You can customize the FilterPopup size using FilterPopupHeight property by writing style of TargetType as GridFilterControl.

<Window.Resources>
        <Style TargetType="Syncfusion:GridFilterControl">
            <Setter Property="FontSize" Value="14" />
            <Setter Property="FontWeight" Value="Normal" />
            <Setter Property="FilterPopupHeight" Value="632"/>
        </Style>
</Window.Resources>

Customizing Sort Option Text from Filter Popup in WPF DataGrid

Changing filter icon style after applying filters

You can change the filter icon style by editing the FilterToggleButton style. In FilterToggleButton style, you can see Filtered and UnFiltered VisualStates. In that, you can change PathFillColor for FilterToggleButton.

<Style TargetType="syncfusion:FilterToggleButton">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="syncfusion:FilterToggleButton">
                <Grid SnapsToDevicePixels="True">
                    <VisualStateManager.VisualStateGroups>
                    
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="MouseOver" />
                            <VisualState x:Name="Pressed" />
                            <VisualState x:Name="Disabled" />
                        </VisualStateGroup>
                        
                        <VisualStateGroup x:Name="FilterStates">
                        
                            <VisualState x:Name="Filtered">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_FilterToggleButtonIndicator" 
                                                                   Storyboard.TargetProperty="Data">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Geometry>M2.1299944,9.9798575L55.945994,9.9798575 35.197562,34.081179 35.197562,
                                                          62.672859 23.428433,55.942383 23.428433,33.52121z M1.3001332,0L56.635813,
                                                          0C57.355887,0,57.935946,0.5891428,57.935946,1.3080959L57.935946,
                                                          2.8258877C57.935946,3.5448422,57.355887,4.133985,56.635813,4.133985L1.3001332,
                                                          4.133985C0.58005941,4.133985,-2.3841858E-07,3.5448422,0,2.8258877L0,
                                                          1.3080959C-2.3841858E-07,0.5891428,0.58005941,0,1.3001332,0z
                                                </Geometry>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ColorAnimation Duration="0:0:0:1"                                                       
                                                    Storyboard.TargetName="PathFillColor"
                                                    Storyboard.TargetProperty="Color"
                                                    To="Red" />
                                </Storyboard>
                            </VisualState>
                            
                            <VisualState x:Name="UnFiltered">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_FilterToggleButtonIndicator" 
                                                                   Storyboard.TargetProperty="Data">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Geometry>F1M-2124.61,-1263.65L-2131.54,-1263.72 -2145.51,-1263.84 -2152.41,
                                                          -1263.9C-2155.99,-1263.93,-2157.48,-1262.16,-2155.7,-1259.96L-2152.05,
                                                          -1255.43C-2150.28,-1253.23,-2147.38,-1249.62,-2145.61,-1247.42L-2143.25,
                                                          -1244.5 -2143.25,-1230.24C-2143.25,-1229.23,-2142.43,-1228.42,-2141.42,
                                                          -1228.42L-2135.64,-1228.42C-2134.63,-1228.42,-2133.81,-1229.23,-2133.81,
                                                          -1230.24L-2133.81,-1244.78 -2131.7,-1247.3C-2129.89,-1249.47,-2126.93,-1253.02,
                                                          -2125.12,-1255.18L-2121.39,-1259.65C-2119.57,-1261.82,-2121.02,-1263.62,-2124.61,-1263.65z
                                                </Geometry>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ColorAnimation Duration="0:0:0:1"                                                       
                                                    Storyboard.TargetName="PathFillColor"
                                                    Storyboard.TargetProperty="Color"
                                                    To="Gray" />
                                </Storyboard>
                            </VisualState>
                            
                        </VisualStateGroup>
                        
                    </VisualStateManager.VisualStateGroups>
                    
                    <Border Width="{TemplateBinding Width}"
                            Height="{TemplateBinding Height}"
                            Background="{TemplateBinding Background}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                            
                        <Path Name="PART_FilterToggleButtonIndicator"
                              Margin="3"
                              Data="F1M-2124.61,-1263.65L-2131.54,-1263.72 -2145.51,-1263.84 -2152.41,
                                    -1263.9C-2155.99,-1263.93,-2157.48,-1262.16,-2155.7,
                                    -1259.96L-2152.05,-1255.43C-2150.28,-1253.23,-2147.38,
                                    -1249.62,-2145.61,-1247.42L-2143.25,-1244.5 -2143.25,
                                    -1230.24C-2143.25,-1229.23,-2142.43,-1228.42,-2141.42,
                                    -1228.42L-2135.64,-1228.42C-2134.63,-1228.42,-2133.81,
                                    -1229.23,-2133.81,-1230.24L-2133.81,-1244.78 -2131.7,
                                    -1247.3C-2129.89,-1249.47,-2126.93,-1253.02,-2125.12,
                                    -1255.18L-2121.39,-1259.65C-2119.57,-1261.82,-2121.02,
                                    -1263.62,-2124.61,-1263.65z"
                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                              Stretch="Fill">
                            <Path.Fill>
                                <SolidColorBrush x:Name="PathFillColor" 
                                                 Color="Gray" />
                            </Path.Fill>
                        </Path>
                        
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

When you apply above style to FilterToggleButton, FilterIcon changes from Default to Gray and to Red when filtering is applied. When you clear it, it changes from Red to Gray and to default style.

See Also

How to display values with underscore in check boxes of the filter control?

How to serialize the filtered values based on FilterMode of the column

How to apply search and filter for one column in SfDataGrid?

How to customize the Filtering and Sorting icons in the SfDataGrid ?

How to change the Filter Predicate showing in CheckBoxFilter UI ?

How to show filter status message in SfDataGrid?

How to localize the filter values in GridCheckBoxColumn ?

How to load NumberFilters in AdvanceFilters using Dynamic Collection?

How to search and select record in SfDataGrid?

How to skip the frozen row data from filtering in the SfDataGrid?

How to filter the records based on display text in the SfDataGrid?

How to change the position of FilterToggleButton and SortIcon in header cell of SfDataGrid?

How to Save and Reload the filters in SfDataGrid?

How to Customize the Excel like Filtering Items Source in SfDataGrid?

How to clear the filtering for all columns using HeaderContextMenu?

How to change the FilterToggleButton color while filtering?

How to access the filtered records from SfDataGrid?