DevExpress v24.2 Update — Your Feedback Matters
Our What's New in v24.2 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.
Take the survey
Not interested
DxGridDataColumn.FilterMenuTemplate Property
Specifies a template used to display the column’s filter menu .
Namespace : DevExpress.Blazor
Assembly :
DevExpress.Blazor.v24.2.dll
NuGet Package :
DevExpress.Blazor
# Declaration
# 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. This parameter allows you to do the following:
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 View Example: Implement a date range filter
The following code snippet creates custom ranges for the Order Date and Total column’s filter menu:
<DxGrid Data ="GridData"
FilterMenuButtonDisplayMode ="GridFilterMenuButtonDisplayMode.Always" >
<Columns >
<DxGridDataColumn FieldName ="OrderDate" Width ="140px" >
<FilterMenuTemplate >
<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 >
<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 = 100 M;
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.01 M: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; }
public static IReadOnlyList<CustomRangeFilterItem> CreateIntervals (string fieldName, int step, int stepsCount, bool isMult, string numberDisplayFormat ) {
var prop = new OperandProperty(fieldName);
var result = new List<CustomRangeFilterItem>();
var start = 0 ;
var end = 0 ;
var firstStepIndex = isMult ? 0 : -1 ;
for (var i = firstStepIndex; i < stepsCount; ++i) {
start = isMult ? step * i : (int )Math.Pow(step, i);
end = isMult ? start + step : (int )Math.Pow(step, i + 1 );
result.Add(new CustomRangeFilterItem() {
Criteria = prop >= start & prop < end,
DisplayText = $"from {string .Format(numberDisplayFormat, start)} to {string .Format(numberDisplayFormat, end)} "
});
}
result.Add(new CustomRangeFilterItem() {
Criteria = prop >= end,
DisplayText = $"from {string .Format(numberDisplayFormat, end)} "
});
return result;
}
}
}
For more information about templates in the Grid component, refer to the following topic: Templates in Blazor Grid .
See Also