Back to Devexpress

DxGridDataColumn.FilterMenuTemplate Property

blazor-devexpress-dot-blazor-dot-dxgriddatacolumn-952fddac.md

latest16.1 KB
Original Source

DxGridDataColumn.FilterMenuTemplate Property

Specifies a template used to display the column’s filter menu.

Namespace : DevExpress.Blazor

Assembly : DevExpress.Blazor.v25.2.dll

NuGet Package : DevExpress.Blazor

Declaration

csharp
[Parameter]
public RenderFragment<GridDataColumnFilterMenuTemplateContext> FilterMenuTemplate { get; set; }

Property Value

TypeDescription
RenderFragment<GridDataColumnFilterMenuTemplateContext>

The template for the column filter menu.

|

Remarks

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 Read Tutorial: Templates

Display Dates as a Flat List

The default filter menu displays DateTime and DateTime? values hierarchically. The following example displays filter menu values for the Order Date column as a flat list:

razor
<DxGrid Data="GridData"
        FilterMenuButtonDisplayMode="GridFilterMenuButtonDisplayMode.Always">
    <Columns>
        <DxGridDataColumn FieldName="OrderDate">
            <FilterMenuTemplate>
                @context.ListView
            </FilterMenuTemplate>
        </DxGridDataColumn>
        <DxGridDataColumn FieldName="ProductName" MinWidth="100" />
        <DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c2" />
        <DxGridDataColumn FieldName="Quantity" />
        <DxGridDataColumn FieldName="Discount" DisplayFormat="p0" />
        <DxGridDataColumn FieldName="Total"
                          UnboundType="GridUnboundColumnType.Decimal"
                          UnboundExpression="[UnitPrice] * [Quantity] * (1 - [Discount])"
                          DisplayFormat="c2" />
    </Columns>
</DxGrid>

@code {
    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
            };
        });
    }
}

Create Custom Ranges

The following code snippet creates custom ranges for the Order Date and Total column’s filter menu:

razor
<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 = 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;
    }
}
razor
@using DevExpress.Data.Filtering;
@using DevExpress.Data.Filtering.Helpers;

<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);
}
css
.spaced-content {
    padding: 0.75rem;
}
razor
@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;
        }
    }
}

Implements

FilterMenuTemplate

See Also

DxGridDataColumn Class

DxGridDataColumn Members

DevExpress.Blazor Namespace