Skip to main content

Group Data in Blazor Grid

  • 17 minutes to read

The Grid can group data against one or more columns:

  • Users can drag column headers between the header panel and the group panel.
  • You can use Grid API to group data in code.

The Grid uses group rows to organize data rows into a tree when data grouping is applied.

Blazor Grid Group Data

Run Demo: Group Data Watch Video: Group Data

Every time a grid component groups data, it executes the following steps:

  1. Sorts data by the column. Note that a grouped column is always sorted.

    At this step, you can implement custom sort logic.

  2. Compares adjacent row values. If values are equal, the Grid puts them in the same group. If the values are different, the second value starts a new group.

    At this step, you can implement custom row value comparison logic (define custom group intervals).

  3. Hides the grouping column and displays its header in the group panel.

    You can set the ShowGroupedColumns property to true to display grouped columns in the grid.

  4. Shows collapsed group rows.

    You can set the AutoExpandAllGroupRows property to true to expand all group rows.

Group Data in the UI

Set the ShowGroupPanel property to true to display the group panel.

Blazor Grid Group Data

The following user operations are available:

  • Drag and drop a column header onto the group panel to group data against the column.
  • Drag headers within this panel to change group order.
  • Drag a column header from the group panel back to the column header panel to ungroup data.

You can use the following properties to prohibit end-user data grouping operations:

DxGrid.AllowGroup
Specifies whether users can group grid data.
DxGridDataColumn.AllowGroup
Specifies whether users can group grid data by the current column.
<DxGrid Data="Data" ShowGroupPanel="true" >
    <Columns>
        <DxGridDataColumn FieldName="ContactName" AllowGroup="false"/>
        <DxGridDataColumn FieldName="CompanyName" />
        <DxGridDataColumn FieldName="City" />
        <DxGridDataColumn FieldName="Region" />
        <DxGridDataColumn FieldName="Country" />
    </Columns>
</DxGrid>

Group Data in Code

Group Data

Use one of the following API members to group grid data by a column:

  • Use a column’s GroupIndex property to specify whether the column takes part in grouping and at which level.

    You can use this property to initially group rows or to change grouping at runtime. Note that you need to enclose your code between BeginUpdate and EndUpdate method calls to change values of Grid component parameters outside the Grid component markup. Otherwise, an exception occurs.

    <!-- Specifies initial group settings -->
    <DxGridDataColumn FieldName="Country" GroupIndex="0" />
    
    // Changes a group index at runtime
    Grid.BeginUpdate();
    Grid.GetDataColumns().First(i => i.FieldName == "Country").GroupIndex = 1;
    Grid.EndUpdate();
    
  • Call the GroupBy method.

      Grid.GroupBy("Country", 0);
      Grid.GroupBy("City");
    

Ungroup Data

You can ungroup grid data by a column in the following ways:

Call the GetGroupCount() method to get the number of grouped columns. Refer to the following section to learn how to get a group column collection: Get Grouped Columns.

Expand and Collapse Group Rows

When data is grouped by a column, the Grid displays collapsed group rows. Set the AutoExpandAllGroupRows property to true to expand all group rows automatically when the grid loads data or users interact with the grid, for instance, sorts or filters data.

Users can click group buttons to expand and collapse group rows. Users can also focus a group row and press the Left Arrow or Right Arrow key to expand or collapse this row. To change a group row’s state in code, call the following methods:

ExpandAllGroupRows()
Expands all group rows.
ExpandGroupRow(Int32, Boolean)
Expands a group row with the specified visible index.
CollapseAllGroupRows()
Collapses all group rows.
CollapseGroupRow(Int32, Boolean)
Collapses a group row with the specified visible index.

To determine a group row’s expanded state, call the IsGroupRowExpanded(Int32) method.

Group Data by Display Text

Set a column’s GroupInterval property to DisplayText to group grid rows by column display text. This scenario can be useful when you specify custom display text for column cells (see DisplayFormat property and CustomizeCellDisplayText event descriptions).

Note

The Grid does not support grouping by display text when you use a Server Mode data source or GridDevExtremeDataSource.

@inject WeatherForecastService ForecastService

<DxGrid Data="@forecasts" ShowGroupPanel="true" CustomizeCellDisplayText="Grid_CustomizeCellDisplayText">
    <Columns>
        <DxGridDataColumn FieldName="Date" DisplayFormat="D"
                          GroupInterval="GridColumnGroupInterval.DisplayText" />
        <DxGridDataColumn FieldName="TemperatureC" TextAlignment="GridTextAlignment.Left" Caption="Forecast"
                          GroupInterval="GridColumnGroupInterval.DisplayText"/>
        <DxGridDataColumn FieldName="CloudCover" />
        </Columns>
</DxGrid>


@code {
    object forecasts;

    protected override void OnInitialized() {
        forecasts = ForecastService.GetForecast();
    }
    void Grid_CustomizeCellDisplayText(GridCustomizeCellDisplayTextEventArgs e) {
        if (e.FieldName == "TemperatureC") {
            int val = Convert.ToInt32(e.Value);
            if (val < 15)
                e.DisplayText = "Cold";
            else if (val < 25)
                e.DisplayText = "Warm";
            else
                e.DisplayText = "Hot";
        }
    }
}

Grid - Group Data by Display Text

Interval Grouping

Adjacent rows form a single group if they contain matching values in a grouping column.

If you group data by a date/time column, the grid only compares the date portion. Rows that contain matching dates form a single group (regardless of time).

You can group data rows by value intervals. The Grid implements a set or predefined intervals and allows you to create custom intervals. Use the GroupInterval property to apply the following predefined interval grouping:

DateMonth
The grid only compares the month and year portion of date/time values.
DateYear
The grid only compares the year portion of date/time values.
DateRange
The grid groups date/time values into intervals used by popular email clients (Last Month, Last Week, Yesterday, Today, Tomorrow, Next Week, and so on).
Alphabetical
The grid compares only the first character of string values.

Note

The Grid does not support interval grouping when you use GridDevExtremeDataSource.

Run Demo: Interval Grouping

<DxGrid Data="Data" ShowGroupedColumns="true">
    <Columns>
        <DxGridDataColumn FieldName="CompanyName" 
                          GroupIndex="0" 
                          GroupInterval="GridColumnGroupInterval.Alphabetical" />
        <DxGridDataColumn FieldName="ContactName" />
        <DxGridDataColumn FieldName="City" />
        <DxGridDataColumn FieldName="Country" />
    </Columns>
</DxGrid>

Blazor Grid Group Data Alphabetical

Custom Group Intervals

To group rows by custom intervals, follow the steps below:

  1. Set a column’s GroupInterval property to Custom.
  2. Handle the CustomGroup event that fires for every pair of adjacent row values. Use the event argument’s SameGroup property to specify whether the processed rows belong to the same group.
  3. (optional) When you implement custom group intervals, the Grid displays the value of the first row in the group as a group row value. Handle the CustomizeGroupValueDisplayText event to specify a custom group row value. For more information, refer to the following section: Customize Group Row Content and Appearance.

Note

The Grid does not support custom group intervals when you use Server Mode data source or GridDevExtremeDataSource.

Run Demo: Custom Grouping

The following code snippet implements custom group intervals for the Unit Price column. Data are grouped for the following intervals: $0.00 - $10.00, $10.00 - $20.00, etc.

<DxGrid Data="GridDataSource" 
        ShowGroupPanel="true"
        CustomGroup="Grid_CustomGroup"
        CustomizeGroupValueDisplayText="Grid_CustomizeGroupValueDisplayText">
    <Columns>
        <DxGridDataColumn FieldName="ProductName" />
        <DxGridDataColumn FieldName="Country" />
        <DxGridDataColumn FieldName="OrderDate" DisplayFormat="d" />
        <DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c"
                      GroupIndex="0" GroupInterval="GridColumnGroupInterval.Custom" />
        <DxGridDataColumn FieldName="Quantity" />
    </Columns>
</DxGrid>

@code {
    // ...
    void Grid_CustomGroup(GridCustomGroupEventArgs e) {
        if(e.FieldName == "UnitPrice") {
            e.SameGroup = Grid_CompareColumnValues(e.Value1, e.Value2) == 0;
            e.Handled = true;
        }
    }
    int Grid_CompareColumnValues(object value1, object value2) {
        double val1 = Math.Floor(Convert.ToDouble(value1) / 10);
        double val2 = Math.Floor(Convert.ToDouble(value2) / 10);
        var res = System.Collections.Comparer.Default.Compare(val1, val2);
        if(res < 0)
            res = -1;
        else if(res > 0)
            res = 1;
        if(res == 0 || (val1 > 9 && val2 > 9))
            res = 0;
        return res;
    }
    void Grid_CustomizeGroupValueDisplayText(GridCustomizeGroupValueDisplayTextEventArgs e) {
        if(e.FieldName == "UnitPrice") {
            double val = Math.Floor(Convert.ToDouble(e.Value) / 10);
            string displayText = string.Format("{0:c} - {1:c} ", val * 10, (val + 1) * 10);
            if(val > 9)
                displayText = string.Format(">= {0:c} ", 100);
            e.DisplayText = displayText;
        }
    }
}

Custom Grouping

Custom Grouping

The Grid allows you to apply a custom grouping algorithm to its data. Follow the steps below to enable this functionality:

  1. Set a column’s SortMode property to Custom and handle the CustomSort event to implement custom data sorting.
  2. Set the column’s GroupInterval property to Custom and handle the CustomGroup event to implement custom group intervals.
  3. (optional) Handle the CustomizeGroupValueDisplayText event to specify custom group value display text.
  4. (optional) Implement the GroupRowTemplate template to specify custom group row content.

Note

The Grid does not support custom grouping when you use Server Mode data source or GridDevExtremeDataSource.

The following code snippet groups countries by population.

<DxGrid Data="countryInfo" ShowGroupPanel="true" ShowGroupedColumns="true" 
        CustomSort="Grid_CustomSort" 
        CustomGroup="Grid_CustomGroup" 
        CustomizeGroupValueDisplayText="Grid_CustomizeGroupValueDisplayText" >
    <Columns>
        <DxGridDataColumn FieldName="Country" SortMode="GridColumnSortMode.Custom" 
                          GroupInterval="GridColumnGroupInterval.Custom">
            <GroupRowTemplate>
                Countries with a population of @context.GroupValueDisplayText people
            </GroupRowTemplate>
        </DxGridDataColumn>
        <DxGridDataColumn FieldName="Population" />
    </Columns>
</DxGrid>

@code {
    CountryInfo[]? countryInfo;
    protected override async Task OnInitializedAsync() {
        countryInfo = await CountryInfo.GetData();
    }

    // Sorts the Country column by values of the Population column
    void Grid_CustomSort(GridCustomSortEventArgs e) {
        if(e.FieldName == "Country") {
            var population1 = (int)e.GetRow1Value("Population");
            var population2 = (int)e.GetRow2Value("Population");
            e.Result = population1.CompareTo(population2);
            e.Handled = true;
        }
    }
    // Specifies intervals for grouping
    void Grid_CustomGroup(GridCustomGroupEventArgs e) {
        if(e.FieldName == "Country") {
            var val1Exponent = e.GetRow1Value("Population").ToString().Length;
            var val2Exponent = e.GetRow2Value("Population").ToString().Length;
            e.SameGroup = val1Exponent == val2Exponent;
            e.Handled = true;
        }
    }
    // Customizes a group row value
    void Grid_CustomizeGroupValueDisplayText(GridCustomizeGroupValueDisplayTextEventArgs e) {
        if(e.FieldName == "Country") {
            var valueExponent = e.GetRowValue("Population").ToString().Length;
            var minGroupValue = Math.Pow(10, valueExponent-1);
            var maxGroupValue = Math.Pow(10, valueExponent)-1;
            e.DisplayText = string.Format("Countries with a population of {0:n0} to {1:n0} people",minGroupValue,maxGroupValue);
            if ((int)e.GetRowValue("Population") < 1000)
                e.DisplayText = "Countries with a population less than 1000 people";
            if ((int)e.GetRowValue("Population") > 1000000000)
                e.DisplayText = "Countries with a population more than 1,000,000,000 people";
        }

    }
}

Grid - Custom Groups

Customize Group Row Content and Appearance

Group row text consists of the following elements:

{column}: {value} ({summary}, {summary})

The Grid allows you to customize display text of the specified elements or create templates to modify group row content (for instance, hide any part).

{column}
The grouping column’s header text. The Grid specifies column header text based on the FieldName property value. The component adds spaces between words if a field name uses the CamelCase naming convention. You can set the Caption property to specify the column header text explicitly.
{value}
The value’s display text. Handle the CustomizeGroupValueDisplayText event to customize a group value’s display text.
{summary}
A summary item’s display text. You can use the ValueDisplayFormat property to specify a display format for the summary item value. The DisplayText property allows you to specify the display text pattern for the summary item.

Customize Group Row Appearance

Handle the CustomizeElement event to customize group row appearance. Compare the event argument’s e.ElementType property with the GroupRow or GroupCell value to determine whether the processed element is a group row or cell.

void Grid_CustomizeElement(GridCustomizeElementEventArgs e) {
    if(e.ElementType == GridElementType.GroupRow) 
        e.CssClass = "custom-group-row-class";
}

Create a Group Row Template

Use the following properties to implement custom group row content:

DxGrid.DataColumnGroupRowTemplate
Specifies a common template used to display all group rows in the Grid.
DxGridDataColumn.GroupRowTemplate
Specifies a template used to display group rows when the Grid is grouped by this column.

Run Demo: Group Row Template

<DxGrid Data="Data" ShowGroupPanel="true">
    <Columns>
        <DxGridDataColumn FieldName="CompanyName" />
        <DxGridDataColumn FieldName="Country" GroupIndex="0">
            <GroupRowTemplate>
                <text>@context.ColumnCaption: @context.GroupValue</text>
                @{
                    var summaryItems = context.Grid.GetGroupSummaryItems();
                    if(summaryItems.Any()) {
                        <text> (</text>
                        foreach(var i in summaryItems) {
                            if(i != summaryItems.First()) {
                                <text>, </text>
                            }
                            @context.Grid.GetGroupSummaryLabel(i, context.VisibleIndex)
                            <text>: </text>
                            <b>@context.Grid.GetGroupSummaryFormattedValue(i, context.VisibleIndex)</b>
                        }
                        <text>)</text>
                    }
                }
            </GroupRowTemplate>
        </DxGridDataColumn>
        <DxGridDataColumn FieldName="City" />
        <DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c" />
        <DxGridDataColumn FieldName="Quantity" />
        <DxGridDataColumn FieldName="Total"
                          UnboundType="GridUnboundColumnType.Decimal"
                          UnboundExpression="[UnitPrice] * [Quantity]"
                          DisplayFormat="c" />
    </Columns>
    <GroupSummary>
        <DxGridSummaryItem SummaryType="GridSummaryItemType.Sum" FieldName="Total" />
        <DxGridSummaryItem SummaryType="GridSummaryItemType.Count" FieldName="CompanyName" />
    </GroupSummary>
</DxGrid>

DevExpress Blazor Grid - Group Row Template

Group Summary

Group summaries are calculated across all rows within a group and displayed in the group row or group footer. To create group summaries, declare DxGridSummaryItem objects in the GroupSummary template.

<DxGrid Data="Data" ShowGroupPanel="true">
    <Columns>
        <DxGridDataColumn FieldName="Country" GroupIndex="0" />
        <DxGridDataColumn FieldName="City" GroupIndex="1" />
        <DxGridDataColumn FieldName="CompanyName" />
        <DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c" />
        <DxGridDataColumn FieldName="Quantity" />
        <DxGridDataColumn FieldName="Total" DisplayFormat="c" UnboundType="GridUnboundColumnType.Decimal"
                          UnboundExpression="[UnitPrice] * [Quantity]" />
    </Columns>
    <GroupSummary>
        <DxGridSummaryItem SummaryType="GridSummaryItemType.Count" FieldName="CompanyName" />
        <DxGridSummaryItem SummaryType="GridSummaryItemType.Sum" FieldName="Total" />
    </GroupSummary>
</DxGrid>

Run Demo: Group Summary Run Demo: Group Footer Summary

For more information about group summaries, refer to the following topic: Summary in Blazor Grid.

Limitations

The Grid has the following specifics and limitations when you use GridDevExtremeDataSource:

The Grid has the following specifics and limitations when you use a Server Mode data source:

  • Grouping by display text is not supported.
  • Custom grouping is not supported.
  • A data grouping operation cancels any “select all” or “deselect all” operation in progress.

This section contains a comprehensive grouping-related API reference.

Show API Reference
Grid API member Type Description
AllowGroup Property Specifies whether users can group grid data.
AutoExpandAllGroupRows Property Specifies whether to expand all group rows automatically when the grid loads data or users interact with the grid.
CollapseAllGroupRows() Method Collapses all group rows.
CollapseGroupRow(Int32, Boolean) Method Collapses a group row with the specified visible index.
ColumnGroupFooterTemplate Property Specifies a common template for all group footer cells in the Grid.
CustomGroup Event Enables you to implement custom logic used to group data in the grid.
CustomizeGroupValueDisplayText Event Allows you to customize the group value’s display text.
DataColumnGroupRowTemplate Property Specifies a common template used to display all group rows in the Grid.
ExpandGroupRow(Int32, Boolean) Method Expands a group row with the specified visible index.
GetGroupCount() Method Gets the number of grouped columns.
GetGroupSummaryDisplayText(IGridSummaryItem, Int32) Method Gets a group summary item’s display text.
GetGroupSummaryFormattedValue(IGridSummaryItem, Int32) Method Gets a group summary item’s formatted value.
GetGroupSummaryItems() Method Gets the collection of group summary items.
GetGroupSummaryLabel(IGridSummaryItem, Int32) Method Gets the name of a group summary‘s function name.
GetGroupSummaryValue(IGridSummaryItem, Int32) Method Gets a group summary item’s value.
GroupBy Method Groups data by values of the specified column and inserts the grouped column in the specified position between other grouped columns.
GroupFooterDisplayMode Property Specifies when to display group footers in the Grid.
GetRowLevel(Int32) Method Gets the nesting level of the processed row.
GroupSummary Property Contains group summary items.
IsGroupRow(Int32) Method Specifies whether the specified row is a group row.
IsGroupRowExpanded(Int32) Method Specifies whether the specified group row is expanded.
ShowGroupedColumns Property Specifies whether to display grouped columns with other columns in the grid’s data area.
ShowGroupPanel Property Specifies whether to show the Group Panel.
Column API member Type Description
AllowGroup Property Specifies whether users can group data by this column.
GroupFooterTemplate Property Specifies a template for the column’s group footer cell.
GroupIndex Property Specifies the column’s index among grouped columns.
GroupIndexChanged Event Fires when the column’s group index changes.
GroupInterval Property Specifies how to group data rows.
GroupRowTemplate Property Specifies a template used to display group rows when the Grid is grouped by this column.

Task-Based Examples

This section contains code samples that involve data grouping functionality.

Get Grouped Columns

The grouped columns are always sorted and have the highest sort priority. Thereby, the Grid maintains grouped columns at the beginning of the collection of sorted columns. Use GetSortedColumns() and GetGroupCount() methods to get a collection of the grouped columns.

IEnumerable<IGridDataColumn> groupedColumns = Grid.GetSortedColumns().Take(grid.GetGroupCount());

Show Grouped Column in Grid

The Grid hides grouped columns and displays their headers in the group panel. Set the ShowGroupedColumns property to true to display grouped columns in the grid.

<DxGrid Data="countryInfo" ShowGroupPanel="true" ShowGroupedColumns="true" >
    <!-- ... -->
</DxGrid>

Sort Groups by Another Column’s Values

When you group data by a field, the Grid sorts all data by this field as well. You can handle the CustomSort event to implement custom sorting logic for a column.

In the following code snippet, the Group column values are sorted according to ImportanceValue field values.

<DxGrid Data="Data" ShowGroupPanel="true" CustomSort="Grid_CustomSort">
    <Columns>
        <DxGridDataColumn FieldName="GroupName" Caption="Group" GroupIndex="0"
                          SortMode="GridColumnSortMode.Custom" />
        <DxGridDataColumn FieldName="Name" SortIndex="1" />
        <DxGridDataColumn FieldName="Value" />
    </Columns>
</DxGrid>

@code {
    object Data { get; set; }

    protected override void OnInitialized() {
        Data = new List<MyData> {
            new MyData { ImportanceValue = 1, GroupName = "High", Name = "Item1", Value = "Value1" },
            new MyData { ImportanceValue = 3, GroupName = "Low", Name = "Item5", Value = "Value5" },
            new MyData { ImportanceValue = 1, GroupName = "High", Name = "Item6", Value = "Value6" },
            new MyData { ImportanceValue = 2, GroupName = "Normal", Name = "Item4", Value = "Value4" },
            new MyData { ImportanceValue = 2, GroupName = "Normal", Name = "Item2", Value = "Value2" },
            new MyData { ImportanceValue = 3, GroupName = "Low", Name = "Item8", Value = "Value8" },
            new MyData { ImportanceValue = 1, GroupName = "High", Name = "Item3", Value = "Value3" },
            new MyData { ImportanceValue = 2, GroupName = "Normal", Name = "Item7", Value = "Value7" }
        };
    }
    void Grid_CustomSort(GridCustomSortEventArgs e) {
        if(e.FieldName == "GroupName") {
            var val1 = (int)e.GetRow1Value("ImportanceValue");
            var val2 = (int)e.GetRow2Value("ImportanceValue");
            e.Result = val1.CompareTo(val2);
            e.Handled = true;
        }
    }
    class MyData {
        public int ImportanceValue { get; set; }
        public string GroupName { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
    }
}

Custom Sorted Groups

Select Items in a Group

To allow users to select and deselect all rows in a group, implement the DataColumnGroupRowTemplate template. Place the DxCheckBox<T> component in the template and handle checked state changes.

Custom Sorted Groups

You can see a complete code sample in the following GitHub example: Select and Deselect All Rows in a Group.

Customize Date Format in Group Rows

Handle the CustomizeGroupValueDisplayText event to format a group value in a group row.

void OnCustomizeGroupValueDisplayText(GridCustomizeGroupValueDisplayTextEventArgs e) {
    if(e.FieldName == "OrderDate") {
        e.DisplayText = string.Format("{0:D} ", e.Value);
    }
}

Custom Date Format in Group Row

Get Rows in a Group

You can filter a data source based on a group value to get a list of data items contained in the group.

private Customer[]? customers;

public static List<object> GetGroupDataItems(object groupValue) {
    var children = customers.Where(e => e.Country == groupValue.ToString()).ToList();
    return children;
}

Alternatively, you can iterate manually through grid rows.

public static List<object> GetGroupDataItems(IGrid grid, int visibleIndex) {
    var result = new List<object>();
    var rowLevel = grid.GetRowLevel(visibleIndex);
    grid.ExpandGroupRow(visibleIndex, true);
    for(var i = visibleIndex + 1; i < grid.GetVisibleRowCount(); i++) {
        if(grid.GetRowLevel(i) <= rowLevel)
            break;
        if(!grid.IsGroupRow(i))
            result.Add(grid.GetDataItem(i));
    }
    return result;
}

Hide Group Header Field Name

Use the GroupRowTemplate property to display the required value without the name of the column.

<DxGridDataColumn FieldName="Summary" GroupIndex="0" >
    <GroupRowTemplate>@context.GroupValue</GroupRowTemplate>
</DxGridDataColumn>

Expand First-Level Groups Only

To expand first-level groups only, follow the steps below:

  1. Iterate through the grid’s visible rows.
  2. Call the GetRowLevel(Int32) method to determine the nesting level of a row.
  3. Call the ExpandGroupRow(Int32, Boolean) method to expand rows of the first level.
static void ExpandFirstLevelsOnly(IGrid grid) {
    grid.BeginUpdate();
    grid.CollapseAllGroupRows();
    for(var i = grid.GetVisibleRowCount() - 1; i >= 0; i--){
        if(grid.GetRowLevel(i) == 0)
            grid.ExpandGroupRow(i);
    }
    grid.EndUpdate();
}