Filtering in WinUI DataGrid

21 Jun 202224 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

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 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)
{
    if (sfDataGrid != null && sfDataGrid.View != null)
    {
    	sfDataGrid.View.Filter = FilterRecords;
    	sfDataGrid.View.RefreshFilter();
    }
}

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.

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

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 ObservableCollection<string> filterValues;

/// <summary>
/// Get or set the FilterValues
/// </summary>
public ObservableCollection<string> FilterValues
{
    get { return filterValues; }
    set { filterValues = value; }
}

private void ApplyFilterPredicate(object sender, RoutedEventArgs e)
{
    var gridColumn = this.sfDataGrid.Columns["OrderID"];
    sfDataGrid.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
        });
    }
    sfDataGrid.View.EndInit();
}

Clear Filtering

SfDataGrid allows you to clear the filters by clearing the filter predicates. This is achieved by invoking the following methods.

this.sfDataGrid.ClearFilters();
this.sfDataGrid.ClearFilter("OrderID");
this.sfDataGrid.ClearFilter(this.sfDataGrid.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.

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

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

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

NOTE

  1. GridColumn.AllowFiltering has higher priority than SfDataGrid.AllowFiltering property.

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 CheckboxFilter 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:

WinUI DataGrid with Filter like Excel

SfDataGrid with Advanced Filter View:

WinUI DataGrid with Advanced Filter

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,

  • CheckboxFilter – Displays only Checkbox filter View.
  • AdvancedFilter – Displays only Advanced filter View.
  • 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="dataGrid:GridFilterControl">
    <Setter Property="FilterMode" Value="AdvancedFilter" />
</Style>

<dataGrid:SfDataGrid x:Name="sfDataGrid"
                       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="dataGrid:GridFilterControl">
    <Setter Property="FilterMode" Value="AdvancedFilter" />
</Style>

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

Changing filter UI View programmatically

You can change FilterMode programmatically by using FilterItemsPopulating event.

this.sfDataGrid.FilterItemsPopulating += sfDataGrid_FilterItemsPopulating;

void sfDataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.DataGrid.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.

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

<dataGrid:SfDataGrid Name="sfDataGrid"
                       AllowFiltering="True"
                       FilterPopupStyle="{StaticResource filterControlStyle}"
                       ItemsSource="{Binding Orders}">
    <dataGrid:SfDataGrid.Columns>
        <dataGrid:GridTextColumn FilterPopupStyle="{x:Null}" MappingName="OrderID" />          
    </dataGrid:SfDataGrid.Columns>
</dataGrid:SfDataGrid>
this.sfDataGrid.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 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 NumberFilters are loaded in

AdvancedFilterControl

.
When the DateTimeOffset type value is bound to the

GridColumn

then DateFilters are loaded in

AdvancedFilterControl

.
WinUI DataGrid displays Text Filter WinUI DataGrid displays Number Filter WinUI 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, then TextFilters 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.
  • StronglyTyped – Advanced filter type is automatically detected based on underlying data type.
<dataGrid:SfDataGrid.Columns>
    <dataGrid:GridTextColumn MappingName="OrderID" FilterBehavior="StringTyped"/>
</dataGrid:SfDataGrid.Columns>
sfDataGrid.Columns["OrderID"].FilterBehavior = FilterBehavior.StringTyped;

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

this.sfDataGrid.FilterItemsPopulating += sfDataGrid_FilterItemsPopulating;

void sfDataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.DataGrid.GridFilterItemsPopulatingEventArgs e)
{
    if (e.Column.MappingName != "OrderID")
        return;
    e.FilterControl.AdvancedFilterType = AdvancedFilterType.TextFilter;
    e.FilterControl.SetColumnDataType(typeof(string));
    e.FilterControl.AscendingSortString = GridLocalizationResourceAccessor.Instance.GetLocalizedStringResource("SortStringAscending");
    e.FilterControl.DescendingSortString = GridLocalizationResourceAccessor.Instance.GetLocalizedStringResource("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.

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 x:Key="advancedFilterControlStyle" TargetType="dataGrid:AdvancedFilterControl">
    <Setter Property="CanGenerateUniqueItems" Value="False" />
</Style>
<Style x:Key="filterControlStyle" TargetType="dataGrid:GridFilterControl">
    <Setter Property="FilterMode" Value="AdvancedFilter" />
    <Setter Property="AdvancedFilterStyle" Value="{StaticResource advancedFilterControlStyle}" />
</Style>

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

Improve the 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.

<dataGrid:GridTextColumn AllowBlankFilters="True" MappingName="Country" />
sfDataGrid.Columns["Country"].AllowBlankFilters = true;

Checkbox Filter with AllowBlankFilters as True

Filter Null Values using CheckBox Filter in WinUI DataGrid

Advanced Filter with AllowBlankFilters as True

Filter Null Values using Advanced Filter in WinUI 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.

<dataGrid:GridTextColumn ImmediateUpdateColumnFilter="True" MappingName="OrderID" />
sfDataGrid.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 WinUI DataGrid

Advanced Filter with ImmediateUpdateColumnFilter is True

Advanced Filter with Immediate Filter in WinUI DataGrid

NOTE

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

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.sfDataGrid.FilterChanging += sfDataGrid_FilterChanging;

void sfDataGrid_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.sfDataGrid.FilterChanged += sfDataGrid_FilterChanged;

void sfDataGrid_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.sfDataGrid.FilterItemsPopulating += sfDataGrid_FilterItemsPopulating;

void sfDataGrid_FilterItemsPopulating(object sender, Syncfusion.UI.Xaml.DataGrid.GridFilterItemsPopulatingEventArgs e)
{
}

FilterItemsPopulated event

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

this.sfDataGrid.FilterItemsPopulated += sfDataGrid_FilterItemsPopulated;

void sfDataGrid_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.sfDataGrid.FilterChanged += sfDataGrid_FilterChanged;
void sfDataGrid_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.

<dataGrid:GridImageColumn  MappingName="Flag" HeaderText="Country" ImageHeight="50" ImageWidth="50" TextAlignment="Center">
    <dataGrid:GridImageColumn.FilterPopupStyle>
            <Style TargetType="dataGrid:GridFilterControl">
                <Setter Property="CheckboxFilterStyle">
                    <Setter.Value>
                        <Style TargetType="dataGrid:CheckboxFilterControl">
                            <Setter Property="Background" Value="{ThemeResource AcrylicBackgroundFillColorDefaultBrush}"/>
                            <Setter Property="ItemTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <CheckBox Margin="4"
                                                  HorizontalAlignment="Stretch"
                                                  HorizontalContentAlignment="Stretch"            
                                                  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}" HorizontalAlignment="Left" Height="25"/>
                                                </DataTemplate>
                                            </CheckBox.ContentTemplate>
                                        </CheckBox>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Style>
    </dataGrid:GridImageColumn.FilterPopupStyle>
</dataGrid:GridImageColumn>

Column Filter with Image in WinUI DataGrid

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.

<dataGrid:GridTextColumn MappingName="OrderID" FilterBehavior="StringTyped"/>

You can achieve this programmatically by using FilterItemsPopulating event also.

this.sfDataGrid.FilterItemsPopulating += sfDataGrid_FilterItemsPopulating;

void sfDdataGrid_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 = GridLocalizationResourceAccessor.Instance.GetLocalizedStringResource("SortStringAscending");
        e.FilterControl.DescendingSortString = GridLocalizationResourceAccessor.Instance.GetLocalizedStringResource("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.sfDataGrid.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.sfDataGrid.FilterItemsPopulated += sfDataGrid_FilterItemsPopulated;

void sfDataGrid_FilterItemsPopulated(object sender, GridFilterItemsPopulatedEventArgs e)
{
    if (e.Column.MappingName == "OrderID")
    {
        var itemsSource = e.ItemsSource as List<FilterElement>;

        //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.sfDataGrid.FilterChanging += sfDataGrid_FilterChanging;

void sfDataGrid_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="dataGrid:GridFilterControl" x:Key="gridFilterControlStyle">
    <Setter Property="SortOptionVisibility" Value="Collapsed"/>
</Style>

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

Collapse sort option from filter popup in WinUI DataGrid

Customizing Sort Options text

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

this.sfDataGrid.FilterItemsPopulating += sfDataGrid_FilterItemsPopulating;

void sfDataGrid_FilterItemsPopulating(object sender, GridFilterItemsPopulatingEventArgs e)
{
    if(e.Column.MappingName == "CustomerName")
    {
        e.FilterControl.AscendingSortString = "Sort Ascending";
        e.FilterControl.DescendingSortString = "Sort Descending";
    }           
}

Customized Sort Option Text from Filter Popup in WinUI DataGrid

Customize the FilterPopup size using GridFilterControl style

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

<Page.Resources>
    <Style TargetType="dataGrid:GridFilterControl">
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontWeight" Value="Normal" />
        <Setter Property="FilterPopupHeight" Value="620" />
    </Style>
</Page.Resources>

Customizing Filter Popup in WinUI 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.

xmlns:grid="using:Syncfusion.UI.Xaml.Grids"

<Page.Resources>
    <Style TargetType="grid:FilterToggleButton">
	    <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="grid:FilterToggleButton">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="FilterStates">
    
                                <VisualState x:Name="Filtered">
                                    <Storyboard BeginTime="0">
                                        <ObjectAnimationUsingKeyFrames BeginTime="0"
                                                               Duration="1"
                                                               Storyboard.TargetName="PART_FilterToggleButtonIndicator"
                                                               Storyboard.TargetProperty="Data">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="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" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ColorAnimation BeginTime="0"
                                                Duration="00:00:01"
                                                Storyboard.TargetName="PathFillColor"
                                                Storyboard.TargetProperty="Color"
                                                To="Red" />
                                    </Storyboard>
                                </VisualState>
    
                                <VisualState x:Name="UnFiltered">
                                    <Storyboard BeginTime="0">
                                        <ObjectAnimationUsingKeyFrames BeginTime="0"
                                                               Duration="1"
                                                               Storyboard.TargetName="PART_FilterToggleButtonIndicator"
                                                               Storyboard.TargetProperty="Data">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="M0,0 L118.49799,0 L72.811813,53.068943 L72.811813,116.02525 L46.897243,101.20534 L46.897243,51.835941 z" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ColorAnimation BeginTime="0"
                                                Duration="00:00:01"
                                                Storyboard.TargetName="PathFillColor"
                                                Storyboard.TargetProperty="Color"
                                                To="Gray" />
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
    
                        <Border Width="{TemplateBinding Width}"
                                Height="{TemplateBinding Height}"
                                Background="Transparent">
    
                            <Path Name="PART_FilterToggleButtonIndicator"
                                  Margin="{TemplateBinding Margin}"
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                  Data="M0,0 L118.49799,0 L72.811813,53.068943 L72.811813,116.02525 L46.897243,101.20534 L46.897243,51.835941 z"
                                  Stretch="Fill">
                                <Path.Fill>
                                    <SolidColorBrush x:Name="PathFillColor" Color="Gray" />
                                </Path.Fill>
                            </Path>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

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.