Back to Devexpress

Working with Groups in Code

windowsforms-1967-controls-and-libraries-data-grid-grouping-working-with-groups-in-code.md

latest19.0 KB
Original Source

Working with Groups in Code

  • Jun 09, 2025
  • 9 minutes to read

Group and Ungroup Data

Use the following API to group data in code:

|

API

|

Description

| | --- | --- | |

GridColumn.Group

|

Groups data by the current column. If data is already grouped by other columns, the column is added after the last grouping level.

| |

GridColumn.GroupIndex

|

Specifies the grouping order. Accepts non-negative integer values.

Tip

Set the column’s GroupIndex property to a unique value when using merged column grouping.

| |

GridColumn.UnGroup

|

Removes grouping applied to the column. This method does not affect other grouped columns.

| |

GridView.ClearGrouping

|

Removes all data grouping and reverts the grid to an ungrouped state.

|

The following code snippet groups data by the Ship Country column and then by the Ship City column:

csharp
public Form1() {
    colShipCountry.Group();
    colShipCity.Group();
    // Or
    colShipCountry.GroupIndex = 0;
    colShipCity.GroupIndex = 1;
}
vb
Public Sub New()
    colShipCountry.Group()
    colShipCity.Group()
    ' Or
    colShipCountry.GroupIndex = 0
    colShipCity.GroupIndex = 1
End Sub

The following code snippet removes grouping by the last-level column when the button is clicked:

csharp
void removeLastGroupingButton_Click(object sender, EventArgs e) {
    if (gridView1.GroupedColumns.Count > 0)
        gridView1.GroupedColumns.Last().UnGroup();
}
vb
Private Sub removeLastGroupingButton_Click(ByVal sender As Object, ByVal e As EventArgs)
    If gridView1.GroupedColumns.Count > 0 Then
        gridView1.GroupedColumns.Last().UnGroup()
    End If
End Sub

A grouping operation raises ColumnView.StartGrouping and ColumnView.EndGrouping events. These events do not allow you to cancel the grouping operation.

Merged and Complex Grouping

Use the GridColumnSortInfoCollection.ClearAndAddRange method to modify the ColumnView.SortInfo collection. This collection stores ColumnSortInfo objects for every column involved in sorting and grouping.

The following code snippet applies two grouping layers:

  1. Merged grouping by “Ship Country”, “Ship City”, and “Ship Region” columns.
  2. Second-level grouping by the “Customer ID” column.
csharp
gridView1.SortInfo.ClearAndAddRange(new[] {
    new GridMergedColumnSortInfo(
        new[] {
            colShipCountry, colShipCity, colShipRegion},
        new[] {
            ColumnSortOrder.Ascending, ColumnSortOrder.Descending, ColumnSortOrder.Ascending }),
    new GridColumnSortInfo(colCustomerID, ColumnSortOrder.Descending)
}, 4);
vb
gridView1.SortInfo.ClearAndAddRange( {
    New GridMergedColumnSortInfo( { colShipCountry, colShipCity, colShipRegion}, { ColumnSortOrder.Ascending, ColumnSortOrder.Descending, ColumnSortOrder.Ascending }),
    New GridColumnSortInfo(colCustomerID, ColumnSortOrder.Descending)
}, 4)

Custom Grouping

Set the GridColumn.SortMode property to ColumnSortMode.Custom and handle the GridView.CustomColumnGroup event to implement custom grouping based on your preferences.

The grid raises the CustomColumnGroup event for all columns with custom sort mode enabled when data is grouped by these columns.

The following example breaks order dates into four seasons and groups orders by season:

csharp
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;

public Form1() {
    InitializeComponent();
    colOrderDate.SortMode = ColumnSortMode.Custom;
    colOrderDate.Group();
}
// Get the season by month
string GetSeason(DateTime date) {
    Int32 month = date.Month;
    string season = "Winter";
    if (month == 3 || month == 4 || month == 5) season = "Spring";
    if (month == 6 || month == 7 || month == 8) season = "Summer";
    if (month == 9 || month == 10 || month == 11) season = "Fall";
    return season;
}
// Implement custom grouping
void gridView1_CustomColumnGroup(object sender, CustomColumnSortEventArgs e) {
    if (e.Column != colOrderDate) return;
    DateTime value1 = (DateTime)e.Value1;
    DateTime value2 = (DateTime)e.Value2;
    if (GetSeason(value1) == GetSeason(value2))
        e.Result = 0;
    else e.Result = 1;
    e.Handled = true;
}
// Customize group row content
void gridView1_CustomDrawGroupRow(object sender, RowObjectCustomDrawEventArgs e) {
    GridGroupRowInfo info = e.Info as GridGroupRowInfo;
    GridView view = sender as GridView;
    if (info.Column == colOrderDate) {
        DateTime rowValue = (DateTime)view.GetGroupRowValue(info.RowHandle, info.Column);
        string season = GetSeason(rowValue);
        if (season != "Winter")
            info.GroupText = $"{season} {rowValue.Year}";
        else
            info.GroupText = $"{season} {rowValue.Year}/{rowValue.AddYears(1).Year}";
    }
}
vb
Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Grid.ViewInfo

Public Sub New()
    InitializeComponent()
    colOrderDate.SortMode = ColumnSortMode.Custom
    colOrderDate.Group()
End Sub
' Get the season by month
Private Function GetSeason(ByVal [date] As DateTime) As String
    Dim month As Integer = [date].Month
    Dim season As String = "Winter"
    If month = 3 OrElse month = 4 OrElse month = 5 Then
        season = "Spring"
    End If
    If month = 6 OrElse month = 7 OrElse month = 8 Then
        season = "Summer"
    End If
    If month = 9 OrElse month = 10 OrElse month = 11 Then
        season = "Fall"
    End If
    Return season
End Function
' Implement custom grouping
Private Sub gridView1_CustomColumnGroup(ByVal sender As Object, ByVal e As CustomColumnSortEventArgs)
    If e.Column <> colOrderDate Then
        Return
    End If
    Dim value1 As DateTime = CDate(e.Value1)
    Dim value2 As DateTime = CDate(e.Value2)
    If GetSeason(value1) = GetSeason(value2) Then
        e.Result = 0
    Else
        e.Result = 1
    End If
    e.Handled = True
End Sub
' Customize group row content
Private Sub gridView1_CustomDrawGroupRow(ByVal sender As Object, ByVal e As RowObjectCustomDrawEventArgs)
    Dim info As GridGroupRowInfo = TryCast(e.Info, GridGroupRowInfo)
    Dim view As GridView = TryCast(sender, GridView)
    If info.Column = colOrderDate Then
        Dim rowValue As DateTime = CDate(view.GetGroupRowValue(info.RowHandle, info.Column))
        Dim season As String = GetSeason(rowValue)
        If season <> "Winter" Then
            info.GroupText = $"{season} {rowValue.Year}"
        Else
            info.GroupText = $"{season} {rowValue.Year}/{rowValue.AddYears(1).Year}"
        End If
    End If
End Sub

Customize Group Row Content

The following API members define group row content:

|

API

|

Description

| | --- | --- | |

GridView.GroupFormat

|

Allows you to format values in group rows for all columns.

| |

GridColumn.GroupFormat

|

Allows you to format values in group rows for a column.

| |

GridOptionsBehavior.AlignGroupSummaryInGroupRow

|

Allows you to display group summaries in group rows.

|

Handle the GridView.CustomDrawGroupRow event to modify captions of specific group rows.

The following code snippet customizes the display text for group rows that correspond to the “Ship City” grouping column:

csharp
private void gridView1_CustomDrawGroupRow(object sender, DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs e) {
    GridGroupRowInfo info = e.Info as GridGroupRowInfo;
    if (info.Column == colShipCity)
        info.GroupText = $"Shipped to {info.GroupValueText}";
}
vb
Private Sub gridView1_CustomDrawGroupRow(ByVal sender As Object, ByVal e As DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs)
    Dim info As GridGroupRowInfo = TryCast(e.Info, GridGroupRowInfo)
    If info.Column = colShipCity Then
        info.GroupText = $"Shipped to {info.GroupValueText}"
    End If
End Sub

Identify and Process Group Rows

Group rows are identified by negative row handles. The nesting level of each group row corresponds to the position of its grouping column in the ColumnView.GroupedColumns collection.

Tip

Refer to the following help topic for more information on row handles: Access Rows.

Use the following API to identify and process group rows:

|

API

|

Description

| | --- | --- | |

GridView.GetRowLevel

|

Identifies the nesting level of a group row.

| |

GridView.GetChildRowCount

|

Returns the number of immediate child rows for a group row.

| |

GridView.GetChildRowHandle

|

Returns the row handle of a group row’s child/nesting data row.

| |

GridView.GetParentRowHandle

|

Returns the row handle of a parent row.

| |

ColumnView.GetRow/ColumnView.GetDataRow

|

Methods that retrieve a DataRowView or another object that corresponds to a row.

|

The following example updates the “Price” column value for all data rows within the focused group row, including data rows in nested groups, when a button is clicked:

csharp
using DevExpress.XtraGrid.Views.Grid;
// ... 
private void buttonDiscount_Click(object sender, System.EventArgs e) {
    GridView view = gridView1;
    int rowHandle = view.FocusedRowHandle;
    ArrayList rowsToDiscount = new ArrayList();
    // Get related data rows 
    if(view.IsGroupRow(rowHandle))
        GetChildRows(view, rowHandle, rowsToDiscount);
    // Update row values
    view.BeginUpdate();
    Discount(rowsToDiscount);
    view.EndUpdate();
}

// Fill the rowsToDiscount list
public void GetChildRows(GridView view, int groupRowHandle, ArrayList childRows) {
    if(!view.IsGroupRow(groupRowHandle)) return;
    // Get the number of immediate children 
    int childCount = view.GetChildRowCount(groupRowHandle);
    for(int i = 0; i < childCount; i++) {
        // Get the handle of a child row 
        int childHandle = view.GetChildRowHandle(groupRowHandle, i);
        // If the child is a group row, add its children to the list 
        if(view.IsGroupRow(childHandle))
            GetChildRows(view, childHandle, childRows);
        else {                    
            // The child is a data row
            // Add the row to childRows if it wasn't added before 
            object row = view.GetRow(childHandle);
            if(!childRows.Contains(row))
                childRows.Add(row);
        }       
    }
}

// Update Price column values in specified rows 
public void Discount(ArrayList rows) {
    for(int i = 0; i < rows.Count; i++) {
        DataRow row = (rows[i] as DataRowView).Row;    
        decimal oldValue = (decimal)row["Price"];
        row["Price"] = oldValue * 0.95m;               
    }
}
vb
Imports DevExpress.XtraGrid.Views.Grid
' ... 
Private Sub ButtonDiscount_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles ButtonDiscount.Click
    Dim View As GridView = GridView1
    Dim rowHandle As Integer = View.FocusedRowHandle
    Dim rowsToDiscount As ArrayList = New ArrayList
    ' Get related data rows 
    If View.IsGroupRow(rowHandle) Then 
        GetChildRows(View, rowHandle, rowsToDiscount)
    End If 
    ' Change row values
    View.BeginUpdate()
    Discount(rowsToDiscount)
    View.EndUpdate()
End Sub

' Fill the rowsToDiscount list
Public Sub GetChildRows(ByVal View As GridView, ByVal groupRowHandle As Integer, _
ByVal childRows As ArrayList)
    If Not View.IsGroupRow(groupRowHandle) Then Return 
    ' Get the number of immediate children 
    Dim childCount As Integer = View.GetChildRowCount(groupRowHandle)
    Dim i As Integer 
    For i = 0 To childCount - 1
        ' Get the handle of a child row 
        Dim childHandle As Integer = View.GetChildRowHandle(groupRowHandle, i)
        ' If the child is a group row, add its children to the list 
        If View.IsGroupRow(childHandle) Then 
            GetChildRows(View, childHandle, childRows)
        Else 
            ' The child is a data row
            ' Add the row to childRows if it was not added before 
            Dim row As Object = View.GetRow(childHandle)
            If Not childRows.Contains(row) Then childRows.Add(row)
        End If 
    Next 
End Sub 

' Update Price column values in specified rows 
Public Sub Discount(ByVal rows As ArrayList)
    Dim i As Integer 
    For i = 0 To rows.Count - 1
        Dim row As DataRow = CType(rows(i), DataRowView).Row
        Dim oldValue As Decimal = row("Price")
        row("Price") = oldValue * 0.95
    Next 
End Sub

Expand and Collapse Group Rows

Use the following API to expand and collapse group rows:

|

API

|

Description

| | --- | --- | |

GridView.GetRowExpanded

|

Gets whether the group row is expanded.

| |

GridView.ExpandGroupRow

|

Expands the specified group row.

| |

GridView.CollapseGroupRow

|

Collapses the specified group row.

| |

GridView.SetRowExpanded

|

Expands or collapses the specified group row.

| |

ColumnView.FocusedRowHandle

|

Scrolls the View to the specified row and focuses this row. Expands the row’s parent group.

| |

GridView.MakeRowVisible

|

Scrolls the View to display the required row. If the row belongs to a group, this group expands.

|

Expand/collapse operations raise the following events:

|

API

|

Description

| | --- | --- | |

GridView.GroupRowExpanding

|

Fires when a group row is about to be expanded and allows you to cancel the expand operation.

| |

GridView.GroupRowCollapsing

|

Fires when a group row is about to be collapsed and allows you to cancel the collapse operation.

| |

GridView.GroupRowExpanded

|

Fires after a group row is expanded.

| |

GridView.GroupRowCollapsed

|

Fires after a group row is collapsed.

|

The following code snippet displays all data rows where the “InStock” column value is true:

csharp
using DevExpress.XtraGrid.Views.Grid;
//... 
GridView View = gridView1;
int rowHandle = -1;
do {
    rowHandle = View.LocateByValue(rowHandle + 1, View.Columns["IsInStock"], true);
    View.MakeRowVisible(rowHandle, false);
} while (rowHandle != GridControl.InvalidRowHandle);
vb
Imports DevExpress.XtraGrid.Views.Grid
'... 
Dim View As GridView = GridView1
Dim rowHandle As Integer = -1
Do 
    rowHandle = View.LocateByValue(rowHandle + 1, View.Columns("IsInStock"), True)
    View.MakeRowVisible(rowHandle, False)
Loop Until rowHandle = GridControl.InvalidRowHandle

See Also

Data Grouping

Tutorial: Custom Grouping Algorithms

Edit Data. Create Cell Editors. Validate User Input

End-User Capabilities: Grouping