DxGridDataColumn.FilterMenuTemplate Property
Specifies a template used to display the column’s filter menu.
Namespace: DevExpress.Blazor
Assembly:
DevExpress.Blazor.v23.1.dll
NuGet Package:
DevExpress.Blazor
Declaration
[Parameter]
public RenderFragment<GridDataColumnFilterMenuTemplateContext> FilterMenuTemplate { get; set; }
Property Value
Use the FilterMenuTemplate property to define a template for the column filter menu.
The FilterMenuTemplate accepts a GridDataColumnFilterMenuTemplateContext object as the context parameter. Use the parameter’s FilterCriteria property to specify the filter criteria the filter menu applies to the data column. Call the parameter’s GetDataItemsAsync() method to obtain a list of default filter menu items.
Note
The GetDataItemsAsync() method cannot obtain unique values from a column whose data items do not implement IComparable type. If you call the method for such a column, an error may occur.
To define a common column filter menu template for all Grid columns, use the DxGrid.DataColumnFilterMenuTemplate property.
Run Demo: Column Filter Menu

<DxGrid Data="GridData"
FilterMenuButtonDisplayMode="GridFilterMenuButtonDisplayMode.Always"
SizeMode="Params.SizeMode">
<Columns>
<DxGridDataColumn FieldName="OrderDate" Width="140px">
<FilterMenuTemplate>
<Grid_Filtering_ColumnFilterMenu_DateRange FilterContext="context" />
</FilterMenuTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="ProductName" MinWidth="100" />
<DxGridDataColumn FieldName="CategoryId" Caption="Category" Width="130px">
<EditSettings>
<DxComboBoxSettings Data="Categories" ValueFieldName="CategoryId" TextFieldName="CategoryName" />
</EditSettings>
</DxGridDataColumn>
<DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c2" Width="140px" />
<DxGridDataColumn FieldName="Quantity" Width="110px" />
<DxGridDataColumn FieldName="Discount" DisplayFormat="p0" Width="110px" />
<DxGridDataColumn FieldName="Total"
UnboundType="GridUnboundColumnType.Decimal"
UnboundExpression="[UnitPrice] * [Quantity] * (1 - [Discount])"
DisplayFormat="c2"
Width="110px">
<FilterMenuTemplate>
<Grid_Filtering_ColumnFilterMenu_CustomRange FilterContext="context" Items="TotalPriceIntervals" />
</FilterMenuTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="Shipped"
UnboundType="GridUnboundColumnType.Boolean"
UnboundExpression="[ShippedDate] <> Null"
Width="100px" />
</Columns>
</DxGrid>
@code {
static IReadOnlyList<CustomRangeFilterItem> TotalPriceIntervals { get; } = CreateTotalPriceIntervals();
object GridData { get; set; }
IReadOnlyList<Category> Categories { get; set; }
protected override async Task OnInitializedAsync() {
Categories = (await NwindDataService.GetCategoriesAsync()).ToList();
var invoices = await NwindDataService.GetInvoicesAsync();
var products = await NwindDataService.GetProductsAsync();
GridData = invoices.Join(products, i => i.ProductId, p => p.ProductId, (i, p) => {
return new {
ProductName = i.ProductName,
CategoryId = p.CategoryId,
OrderDate = i.OrderDate,
UnitPrice = i.UnitPrice,
Quantity = i.Quantity,
Discount = i.Discount,
ShippedDate = i.ShippedDate
};
});
}
static IReadOnlyList<CustomRangeFilterItem> CreateTotalPriceIntervals() {
var prop = new OperandProperty("Total");
var result = new List<CustomRangeFilterItem>();
var step = 100M;
for(var i = 0; i < 10; i++) {
var start = step * i;
var end = start + step;
result.Add(new() {
Criteria = prop >= start & prop < end,
DisplayText = $"from {start:c} to {end - 0.01M:c}"
});
}
result.Add(new() {
Criteria = prop > 1000,
DisplayText = $"> {1000:c}"
});
return result;
}
}
@using DevExpress.Data.Filtering;
@using DevExpress.Data.Filtering.Helpers;
<style>
.spaced-content {
padding: 0.75rem;
}
</style>
<DxFormLayout CssClass="spaced-content" ItemCaptionAlignment="ItemCaptionAlignment.All">
<DxFormLayoutItem Caption="From" ColSpanSm="12">
<DxDateEdit T="DateTime?"
Enabled="DateEditEnabled"
Date="StartDate"
DateChanged="StartDate_Changed"
MinDate="StartDateEdit_MinDate"
MaxDate="StartDateEdit_MaxDate"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" />
</DxFormLayoutItem>
<DxFormLayoutItem Caption="To" ColSpanSm="12">
<DxDateEdit T="DateTime?"
Enabled="DateEditEnabled"
Date="EndDate"
DateChanged="EndDate_Changed"
MinDate="EndDateEdit_MinDate"
MaxDate="EndDateEdit_MaxDate"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" />
</DxFormLayoutItem>
</DxFormLayout>
@code {
[Parameter]
public GridDataColumnFilterMenuTemplateContext FilterContext { get; set; }
DateTime? StartDate { get; set; }
DateTime? EndDate { get; set; }
DateTime MinDate { get; set; } = DateTime.MinValue;
DateTime MaxDate { get; set; } = DateTime.MaxValue;
DateTime StartDateEdit_MinDate => MinDate;
DateTime StartDateEdit_MaxDate => EndDate != null && EndDate.Value >= StartDateEdit_MinDate ? EndDate.Value : MaxDate;
DateTime EndDateEdit_MinDate => StartDate != null && StartDate.Value <= EndDateEdit_MaxDate ? StartDate.Value : MinDate;
DateTime EndDateEdit_MaxDate => MaxDate;
bool DateEditEnabled { get; set; }
protected override async Task OnInitializedAsync() {
(StartDate, EndDate) = LoadDateRangeValues(FilterContext.FilterCriteria, FilterContext.DataColumn.FieldName);
var items = await FilterContext.GetDataItemsAsync();
var allDates = items.Select(i => Convert.ToDateTime(i.Value)).ToList();
if(allDates.Any()) {
MinDate = allDates.Min();
MaxDate = allDates.Max();
DateEditEnabled = true;
}
}
void StartDate_Changed(DateTime? value) {
StartDate = value;
if(StartDate > EndDate)
EndDate = StartDate;
UpdateCriteria();
}
void EndDate_Changed(DateTime? value) {
EndDate = value;
if(StartDate > EndDate)
StartDate = EndDate;
UpdateCriteria();
}
void UpdateCriteria() {
FilterContext.FilterCriteria = CreateDateRangeCriteria(StartDate, EndDate, FilterContext.DataColumn.FieldName);
}
static CriteriaOperator CreateDateRangeCriteria(DateTime? startDate, DateTime? endDate, string fieldName) {
CriteriaOperator left = null;
CriteriaOperator right = null;
var prop = new OperandProperty(fieldName);
if(startDate != null)
left = prop >= startDate;
if(endDate != null)
right = prop < ConvertEndDateToOperandDate(endDate);
return left & right;
}
static DateTime? ConvertEndDateToOperandDate(DateTime? endDate) => endDate?.Date.AddDays(1);
static (DateTime? startDate, DateTime? endDate) LoadDateRangeValues(CriteriaOperator criteria, string fieldName) {
CriteriaOperator left = null;
CriteriaOperator right = null;
if(criteria is GroupOperator groupOp && groupOp.OperatorType == GroupOperatorType.And && groupOp.Operands.Count == 2) {
left = groupOp.Operands[0];
right = groupOp.Operands[1];
} else {
left = right = criteria;
}
return (
ExtractRangeDate(left, fieldName, BinaryOperatorType.GreaterOrEqual),
ConvertOperandDateToEndDate(ExtractRangeDate(right, fieldName, BinaryOperatorType.Less))
);
}
static DateTime? ExtractRangeDate(CriteriaOperator criteria, string fieldName, BinaryOperatorType opType) {
var canExtract = criteria is BinaryOperator binaryOp &&
binaryOp.OperatorType == opType &&
binaryOp.LeftOperand is OperandProperty prop &&
binaryOp.RightOperand is OperandValue opValue &&
prop.PropertyName == fieldName &&
opValue.Value is DateTime;
if(canExtract)
return (DateTime)((OperandValue)((BinaryOperator)criteria).RightOperand).Value;
return null;
}
static DateTime? ConvertOperandDateToEndDate(DateTime? endDate) => endDate?.Date.AddDays(-1);
}
@using DevExpress.Data.Filtering;
@using DevExpress.Data.Filtering.Helpers;
<DxListBox TData="CustomRangeFilterItem"
TValue="CriteriaOperator"
Data="Items"
ValueFieldName="Criteria"
TextFieldName="DisplayText"
Values="SelectedValues"
ValuesChanged="SelectedValues_Changed"
SelectionMode="ListBoxSelectionMode.Multiple"
ShowCheckboxes="true"
CssClass="h-100" />
@code {
[Parameter]
public GridDataColumnFilterMenuTemplateContext FilterContext { get; set; }
[Parameter]
public IReadOnlyList<CustomRangeFilterItem> Items { get; set; }
IEnumerable<CriteriaOperator> SelectedValues { get; set; }
protected override void OnInitialized() {
SelectedValues = LoadSelectedValues(
FilterContext.FilterCriteria,
Items.Select(i => i.Criteria).ToHashSet()
);
}
void SelectedValues_Changed(IEnumerable<CriteriaOperator> value) {
SelectedValues = value;
FilterContext.FilterCriteria = CreateCriteria(SelectedValues);
}
static CriteriaOperator CreateCriteria(IEnumerable<CriteriaOperator> values) {
var orderedValues = values.OrderBy(v => v.ToString()).ToArray();
if (orderedValues.Length == 0)
return null;
return new GroupOperator(GroupOperatorType.Or, orderedValues);
}
static IEnumerable<CriteriaOperator> LoadSelectedValues(CriteriaOperator criteria, IReadOnlySet<CriteriaOperator> possibleCriterias) {
if(possibleCriterias.Contains(criteria))
return new[] { criteria };
if(criteria is GroupOperator groupOp && groupOp.OperatorType == GroupOperatorType.Or && groupOp.Operands.All(i => possibleCriterias.Contains(i)))
return groupOp.Operands;
return Array.Empty<CriteriaOperator>();
}
public record CustomRangeFilterItem {
public CriteriaOperator Criteria { get; init; }
public string DisplayText { get; init; }
}
}
For more information about templates in the Grid component, refer to the following topic: Templates in Blazor Grid.
See Also