Filter Row in WPF DataGrid (SfDataGrid)
2 Aug 202324 minutes to read
SfDataGrid provides built-in row (called FilterRow) to filter the records. You can enable the FilterRow by specifying the position where it should be displayed by setting SfDataGrid.FilterRowPosition property.
<syncfusion:SfDataGrid x:Name="dataGrid"
FilterRowPosition="FixedTop"
AutoGenerateColumns="True"
ItemsSource="{Binding Orders}"/>
this.dataGrid.FilterRowPosition = FilterRowPosition.FixedTop;
You can get the row index of FilterRow by using the SfDataGrid.GetFilterRowIndex method.
int filterRowIndex = this.dataGrid.GetFilterRowIndex();
You can check whether the specified row index is FilterRow index, by using SfDataGrid.IsFilterRowIndex helper method.
bool isFilterRowIndex = this.dataGrid.IsFilterRowIndex(1);
NOTE
The above helper methods are available in Syncfusion.UI.Xaml.Grid.Helpers namespace
Built-in Editors
By default, FilterRow loads the editors based on underlying property type to filter the data easily. You can change the default editors by using GridColumn.FilterRowEditorType property.
<syncfusion:GridTextColumn MappingName="CustomerName"
FilterRowEditorType="MultiSelectComboBox"/>
this.dataGrid.Columns[2].FilterRowEditorType = "MultiSelectComboBox";
Below are the built-in FilterRow editor types supported in SfDataGrid.
FilterRowEditor Type |
Editor Control |
Renderer |
Description |
TextBox | TextBox | Used for filtering the string values. | |
Numeric | DoubleTextBox | Used for filtering the numeric values. | |
ComboBox | ComboBoxAdv | Used for filtering the specific value from the drop down. | |
MultiSelectComboBox | ComboBoxAdv | Used for filtering the multiple values from the drop down. | |
CheckBox | CheckBox | Used for filtering the Boolean values. | |
DateTime | DateTimeEdit | Used for filtering the DateTime values. |
Filter options
Based on the editor type, FilterRowCell displays the filter conditions in dropdown where you can easily switch between the conditions to filter the data. You can disable filter options by setting GridColumn.FilterRowOptionsVisibility property.
<syncfusion:GridNumericColumn MappingName="OrderID"
FilterRowOptionsVisibility="Collapsed"
FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].FilterRowOptionsVisibility = System.Windows.Visibility.Collapsed;
Below are the filter conditions supported by different filter row editors in SfDataGrid.
Numeric Editor |
TextBox Editor |
DateTime Editor |
CheckBox Editor |
ComboBox, MultiSelectComboBox Editor |
When integer, double, short, decimal, byte or long are bound to the
, the Numeric editor type are loaded in
.
|
When string value is bounded to the
or the items is dynamic, then TextBox editor type are loaded in
.
|
When DateTime type is bounded to the
, then DateTime editor is loaded in
.
|
When Boolean type is bounded to the
, then CheckBox editor is loaded in
.
|
If we need the ComboBox and MultiSelectComboBox we have to set the
.
|
The default filter condition is Equals, the below filter conditions are available in numeric filter.
|
The default filter condition is Begins With, the below filter conditions are available in text filter.
|
The default filter condition is Equals, the below filter conditions are available in date time filter.
|
Always equals filter condition will be applied for filtering the CheckBox value. | Always equals or not equals filter condition will be applied based on selected items count for filtering the items. |
You can change the default FilterRow condition for a corresponding column by using GridColumn.FilterRowCondition property.
<syncfusion:GridNumericColumn MappingName="OrderID"
FilterRowCondition="LessThanOrEqual"
FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].FilterRowCondition = FilterRowCondition.LessThanOrEqual;
Filtering null values
You can enable or disable filtering of null values by setting GridColumn.AllowBlankFilters property. The default value is true
.
When null value filtering is enabled, the filter options loaded with two additional options (“Null” and “Not Null”) to filter the null values. ComboBox and MultiSelectComboBox editors, loads with “Blanks” item in drop down to filter the null values.
<syncfusion:GridNumericColumn MappingName="OrderID"
AllowBlankFilters="False"
FilterRowEditorType="Numeric"/>
this.dataGrid.Columns[0].AllowBlankFilters = false;
<syncfusion:GridTextColumn MappingName="CustomerName"
AllowBlankFilters="True"
FilterRowEditorType="MultiSelectComboBox"/>
this.dataGrid.Columns[2].AllowBlankFilters = true;
Instant Filtering
By default, filters are applied to the columns when moving to other cells or pressing enter key. You can apply filter when typing or selecting in editor itself by setting GridColumn.ImmediateUpdateColumnFilter as true
.
<syncfusion:GridTextColumn MappingName="CustomerName"
FilterRowEditorType="MultiSelectComboBox"
ImmediateUpdateColumnFilter="True"/>
this.dataGrid.Columns[2].ImmediateUpdateColumnFilter = true;
Disable filtering for a particular FilterRowCell
By default, you can filter the records by editing filter row cell. You can disable this editing by using CurrentCellBeginEdit event.
this.dataGrid.CurrentCellBeginEdit += dataGrid_CurrentCellBeginEdit;
void dataGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{
//Cancel the editing for filter row cell in OrderID Column
if (args.Column.MappingName == "OrderID" && dataGrid.IsFilterRowIndex(args.RowColumnIndex.RowIndex))
args.Cancel = true;
}
You can collapse the FilterOption button using FilterRowOptionsVisibility
property.
<syncfusion:GridNumericColumn MappingName="OrderID"
FilterRowOptionsVisibility="Collapsed"/>
Styling
Filter row style
You can customize the style of filter row by writing style of TargetType FilterRowControl.
<Window.Resources>
<Style TargetType="syncfusion:FilterRowControl">
<Setter Property="Background" Value="BlanchedAlmond"/>
</Style>
</Window.Resources>
Filter row - cell style
You can customize the style of filter row cell by writing style of TargetType GridFilterRowCell.
<local:FilterRowCellStyleConverter x:Key="filterRowCellStyleConverter"/>
<Style TargetType="syncfusion:GridFilterRowCell">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource filterRowCellStyleConverter}}"/>
</Style>
public class FilterRowCellStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var filterRowCell = value as GridFilterRowCell;
if (filterRowCell == null) return null;
if (filterRowCell.DataColumn.GridColumn.MappingName == "OrderID" ||
filterRowCell.DataColumn.GridColumn.MappingName == "CustomerName")
return new SolidColorBrush(Colors.LightBlue);
return new SolidColorBrush(Colors.Honeydew);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Customizing filter row cell
You can customize the filter row cell by overriding the GridFilterRowCell. You have to override the GetGridCell method in RowGenerator to load the customized GridFilterRowCell
.
public class GridFilterRowCellExt : GridFilterRowCell
{
public GridFilterRowCellExt()
: base()
{ }
}
public class CustomRowGenerator : RowGenerator
{
public CustomRowGenerator(SfDataGrid dataGrid)
: base(dataGrid)
{
}
/// <summary>
/// Return the Custom FilterRowCell
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>GridCell</returns>
protected override GridCell GetGridCell<T>()
{
//If the Cell is FilterRowCell return custom FilterRowCell
if (typeof(T) == typeof(GridFilterRowCell))
return new GridFilterRowCellExt();
return base.GetGridCell<GridCell>();
}
}
public MainWindow()
{
InitializeComponent();
this.dataGrid.RowGenerator = new CustomRowGenerator(this.dataGrid);
}
Customizing filter options for particular columns
By default, TextBox editor will display the string related conditions like Begins With, Does Not Begin With, Ends With, Does Not End With, Contains, Does Not Contain, Empty, Not Empty filter row conditions in drop down. The below code shows how to display the custom filter row conditions in TextBox editor by overriding the OpenFilterOptionPopup method in a GridFilterRowCell class.
this.dataGrid.RowGenerator = new CustomRowGenerator(this.dataGrid);
public class GridFilterRowCellExt : GridFilterRowCell
{
public GridFilterRowCellExt()
: base()
{ }
/// <summary>
/// Opens the FilterOptionPopup with the FilterOptionList.
/// </summary>
public override void OpenFilterOptionPopup()
{
base.OpenFilterOptionPopup();
if (this.DataColumn.GridColumn.MappingName != "CustomerID")
return;
var list = this.OptionsList();
if (list.Count > 0)
this.FilterOptionsList.ItemsSource = list;
}
/// <summary>
/// Populates the FilterOption list which will loaded in FilterOptionPopup for ShipAddress.
/// </summary>
/// <returns></returns>
private new ObservableCollection<string> OptionsList()
{
var list = new ObservableCollection<string>();
list.Add("Contains");
list.Add("Does not contain");
list.Add("Match");
list.Add("Does not match");
list.Add("Like");
list.Add("Not Like");
return list;
}
}
public class CustomRowGenerator : RowGenerator
{
public CustomRowGenerator(SfDataGrid dataGrid)
: base(dataGrid)
{
}
/// <summary>
/// Return the Custom FilterRowCell
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>GridCell</returns>
protected override GridCell GetGridCell<T>()
{
//If the Cell is FilterRowCell return custom FilterRowCell
if (typeof(T) == typeof(GridFilterRowCell))
return new GridFilterRowCellExt();
return base.GetGridCell<GridCell>();
}
}
Customizing Filter row editors
Customizing the filter row renderer
SfDataGrid allows you to customize the filter row renderer behavior by overriding the corresponding renderer associated with the filter row cell. Each renderer have a set of virtual methods for handling the filter row behaviors. You can also create new renderers instead of overriding the existing renderer.
You can customize the default TextBox editor behavior by overriding GridFilterRowTextBoxRenderer
class and add the custom renderer to FilterRowCellRenderers.
<syncfusion:GridTextColumn MappingName="CustomerName"
FilterRowEditorType="TextBoxExt"/>
public class GridFilterRowTextBoxRendererExt : GridFilterRowTextBoxRenderer
{
public GridFilterRowTextBoxRendererExt()
: base()
{
}
}
public MainWindow()
{
InitializeComponent();
this.dataGrid.FilterRowCellRenderers.Add("TextBoxExt", new GridFilterRowTextBoxRendererExt());
}
Filter based on numeric interval by using the multi select combobox filter
By default you can filter the multiple data in the column by using MultiSelectComboBox filter editor type, the below code shows how to filter the data based on range of numeric values by overriding the ProcessMultipleFilters method in GridFilterRowComboBoxRenderer class.
<syncfusion:GridNumericColumn MappingName="OrderID"
FilterRowOptionsVisibility="Collapsed"
NumberDecimalDigits="0"
FilterRowEditorType="ComboBoxExt"/>
this.dataGrid.FilterRowCellRenderers.Add("ComboBoxExt", new GridFilterRowComboBoxRendererExt());
public class GridFilterRowComboBoxRendererExt : GridFilterRowComboBoxRenderer, INotifyPropertyChanged
{
private List<string> numericComboBoxItems;
public GridFilterRowComboBoxRendererExt()
: base()
{
SetNumericComboBoxItemsList();
}
/// <summary>
/// Generate the Items for NumericComboBox
/// </summary>
/// <returns></returns>
public void SetNumericComboBoxItemsList()
{
numericComboBoxItems = new List<string>();
numericComboBoxItems.Add("Between 1001 and 1004");
numericComboBoxItems.Add("Between 1005 and 1009");
numericComboBoxItems.Add("Between 1010 and 1014");
numericComboBoxItems.Add("Between 1015 and 1020");
numericComboBoxItems.Add(">1020");
}
/// <summary>
/// InitializeEditBinding based on our item, set the SelectedItem and set the ItemSource.
/// </summary>
/// <param name="uiElement">Corresponding UIElement</param>
/// <param name="dataColumn">Corresponding Column</param>
protected override void InitializeEditBinding(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement, DataColumnBase dataColumn)
{
ObservableCollection<object> selItems = new ObservableCollection<object>();
//Generate the items for FilterRow
uiElement.ItemsSource = numericComboBoxItems;
if (dataColumn.GridColumn.FilteredFrom == FilteredFrom.FilterRow && dataColumn.GridColumn.FilterPredicates.Count > 0)
{
if (numericComboBoxItems != null)
{
numericComboBoxItems.ForEach(element =>
{
//Check if the filter is already applied or not, if applied means again add the filter
bool needToAdd = false;
switch (element)
{
case "Between 1001 and 1004":
needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1001");
break;
case "Between 1005 and 1009":
needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1005");
break;
case "Between 1010 and 1014":
needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1010");
break;
case "Between 1015 and 1020":
needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1015");
break;
case ">1020":
needToAdd = this.NeedToAdd(dataColumn.GridColumn.FilterPredicates, "1020");
break;
}
if (needToAdd)
selItems.Add(element);
});
}
}
if (selItems.Count > 0)
uiElement.SelectedItems = selItems;
else if (uiElement.SelectedItems != null)
uiElement.SelectedItems = null;
uiElement.AllowMultiSelect = true;
uiElement.AllowSelectAll = true;
uiElement.EnableOKCancel = true;
uiElement.IsEditable = false;
}
/// <summary>
/// Check whether the column having a FilterPredicate or not
/// </summary>
/// <param name="filterPredicate">FilterPredicates for a column</param>
/// <param name="filterValue">FilterValue for a column</param>
/// <returns></returns>
private bool NeedToAdd(ObservableCollection<FilterPredicate> filterPredicate, string filterValue)
{
bool needToAdd = false;
foreach (var item in filterPredicate)
{
if ((item as FilterPredicate).FilterValue.ToString() == filterValue)
{
needToAdd = true;
break;
}
}
return needToAdd;
}
/// <summary>
/// Generate the FilterPredicates and apply the filter for a corresponding column
/// </summary>
/// <param name="filterValues">Corresponding Filter Value</param>
/// <param name="totalItems">Corresponding Filter Items</param>
public override void ProcessMultipleFilters(List<object> filterValues, List<object> totalItems)
{
var selectedItems = filterValues.Cast<string>().ToList();
var total = totalItems.Cast<string>().ToList();
if (selectedItems == null || total == null || filterValues == null)
return;
if (selectedItems.Count == total.Count)
{
this.ApplyFilters(null, string.Empty);
this.IsValueChanged = false;
return;
}
var filterPredicates = new List<FilterPredicate>();
if (filterValues.Count > 0)
{
selectedItems.ForEach(item =>
{
switch (item)
{
case "Between 1001 and 1004":
filterPredicates.Add(GetFilterPredicates((int)1001, FilterType.GreaterThan, PredicateType.OrElse));
filterPredicates.Add(GetFilterPredicates((int)1004, FilterType.LessThan, PredicateType.And));
break;
case "Between 1005 and 1009":
filterPredicates.Add(GetFilterPredicates((int)1005, FilterType.GreaterThan, PredicateType.OrElse));
filterPredicates.Add(GetFilterPredicates((int)1009, FilterType.LessThan, PredicateType.And));
break;
case "Between 1010 and 1014":
filterPredicates.Add(GetFilterPredicates((int)1010, FilterType.GreaterThan, PredicateType.OrElse));
filterPredicates.Add(GetFilterPredicates((int)1014, FilterType.LessThan, PredicateType.And));
break;
case "Between 1015 and 1020":
filterPredicates.Add(GetFilterPredicates((int)1015, FilterType.GreaterThan, PredicateType.OrElse));
filterPredicates.Add(GetFilterPredicates((int)1020, FilterType.LessThan, PredicateType.And));
break;
case ">1020":
filterPredicates.Add(GetFilterPredicates((int)1020, FilterType.GreaterThan, PredicateType.Or));
break;
}
});
}
string _filterText = string.Empty;
//Creates the FilterText
if (filterPredicates.Count > 0)
{
var selectItems = ((IList)filterValues).Cast<string>().ToList();
for (int i = 0; i < selectedItems.Count; i++)
{
_filterText += selectedItems[i];
if (i != selectedItems.Count - 1)
_filterText += " - ";
}
}
if (filterPredicates != null)
this.ApplyFilters(filterPredicates, _filterText);
this.IsValueChanged = false;
}
/// <summary>
/// Generate the Filter Predicates for Strongly Typed
/// </summary>
/// <param name="value"></param>
/// <param name="filterType"></param>
/// <param name="predType"></param>
/// <returns></returns>
private FilterPredicate GetFilterPredicates(object value, FilterType filterType, PredicateType predType)
{
return new FilterPredicate()
{
FilterBehavior = FilterBehavior.StronglyTyped,
FilterType = filterType,
FilterValue = value,
IsCaseSensitive = false,
PredicateType = predType
};
}
/// <summary>
/// Generate the Filter Predicates for String Typed
/// </summary>
/// <param name="value"></param>
/// <param name="filterType"></param>
/// <param name="predType"></param>
/// <returns></returns>
private FilterPredicate GetStringFilterPredicates(object value, FilterType filterType, PredicateType predType)
{
return new FilterPredicate()
{
FilterBehavior = FilterBehavior.StringTyped,
FilterType = filterType,
FilterValue = value,
IsCaseSensitive = false,
PredicateType = predType
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
Numeric filter row conditions for string typed column
By default, TextBox filter is loaded when underlying property type is string which is bound to a column. The below code shows how to apply the numeric filter row conditions for that particular column using converters in ValueBinding property.
You can filter the decimal value in a filter row cell by overriding the OnInitializeEditElement method in GridFilterRowNumericRenderer class.
<syncfusion:GridTextColumn MappingName="Freight"
UseBindingValue="True"
ValueBinding="{Binding Path=Freight, Converter={StaticResource stringToNumericConverter}}"/>
public class StringToNumericConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return double.Parse(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
//Remove the existing renderer
if (dataGrid.FilterRowCellRenderers.ContainsKey("Numeric"))
dataGrid.FilterRowCellRenderers.Remove("Numeric");
//Add the new custom renderer
dataGrid.FilterRowCellRenderers.Add("Numeric", new GridFilterRowNumericRendererExt());
public class GridFilterRowNumericRendererExt : GridFilterRowNumericRenderer
{
public override void OnInitializeEditElement(DataColumnBase dataColumn, DoubleTextBox uiElement, object dataContext)
{
base.OnInitializeEditElement(dataColumn, uiElement, dataContext);
//Set the NumericDecimalDigits
uiElement.NumberDecimalDigits = 2;
}
}
Customizing GridFilterRowMultiSelectRenderer
By default, in SfDataGrid ComboBox is loaded while enter into edit mode in FilterRow but you can customize the GridFilterRowMultiSelectRenderer for display the combobox while FilterRow loading itself.
<Syncfusion:GridTextColumn MappingName="OrderID" HeaderText="OrderID" FilterRowEditorType="MultiSelectComboBox"/>
sfgrid.FilterRowCellRenderers.Remove("MultiSelectComboBox");
sfgrid.FilterRowCellRenderers.Add("MultiSelectComboBox", new GridMultiSelectComboBoxRendererExt());
public class GridMultiSelectComboBoxRendererExt: GridFilterRowMultiSelectRenderer
{
public GridMultiSelectComboBoxRendererExt():base()
{
SupportsRenderOptimization = false;
IsEditable = false;
}
protected override void OnWireEditUIElement(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement)
{
base.OnWireEditUIElement(uiElement);
uiElement.PreviewMouseDown += uiElement_PreviewMouseDown;
}
void uiElement_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (this.HasCurrentCellState && this.CurrentCellRendererElement == sender)
return;
else
{
var position = e.GetPosition(DataGrid.GetVisualContainer());
var rowColumnIndex = DataGrid.GetVisualContainer().PointToCellRowColumnIndex(position, false);
DataGrid.SelectionController.MoveCurrentCell(rowColumnIndex, true);
}
}
protected override void OnUnwireEditUIElement(Syncfusion.Windows.Tools.Controls.ComboBoxAdv uiElement)
{
base.OnUnwireEditUIElement(uiElement);
uiElement.PreviewMouseDown -= uiElement_PreviewMouseDown;
}
}
You can get the sample from here.
See Also
How to load the symbols in FilterRow and perform actions based on that ?