DataGrid Filtering

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

DataGrid 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 callingSfDataGrid.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

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

  • 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.

Clear Filtering

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

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:

SfDataGrid with Checkbox Filter View in WPF

SfDataGrid with Advanced Filter View:

SfDataGrid with Advanced Filter View in WPF

Choose between built-in UI Views

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

.
Filter menu options
  1. Equals
  2. Does Not Equal
  3. Begins With
  4. Ends With
  5. Contains
  6. Does Not Contain
  7. Empty
  8. Not Empty
  9. Null
  10. 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}"/>

Improve the performance while applying the filter in SfDataGrid WPF

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 the NULL values by using the CheckBox Filter view in SfDataGrid WPF

Advanced Filter with AllowBlankFilters as True

Filter the NULL values by using the Advanced Filter view in SfDataGrid WPF

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

Apply the immediate filter in CheckBox Filter view

Advanced Filter with ImmediateUpdateColumnFilter is True

Apply the immediate filter in Advanced Filter view

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).

Apply the filter based on the underlying value in SfDataGrid WPF

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.

Apply the filter based on the display text in SfDataGrid WPF

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

Apply the filter based on the actual value in SfDataGrid WPF

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

Filter the same values records in SfDataGrid WPF

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;
    }
}

Apply the column filter for image in wpf datagrid

You can get the sample from here.

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}"/>

Hide the SortOptions from Filter popup in SfDataGrid WPF

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>

Customize the FilterPopup in SfDataGrid WPF

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.