Rows in Blazor Grid
- 8 minutes to read
The DevExpress Blazor Grid supports multiple row types:
- Header Row — Displays column captions.
- Filter Row — Allows users to filter Grid data.
- New Item Row — Allows users to add a new row.
- Group Row — Displays a header for data rows that belong to the same group.
- Data Row — Displays data cells.
- Detail Row — Displays custom content defined in the DetailRowTemplate.
- Group Footer — Displays group summaries.
- Footer — Displays total summaries.

This topic describes row features, customization options, and row operations available in code.
Row Indexes
The Grid component assigns indexes to visible data and group rows to indicate row order. Indexes are zero-based and sequential across all Grid pages.

DxGrid reassigns row indexes each time users sort, filter, group data, or expand or collapse group rows. Filtered-out rows and rows in collapsed groups do not have indexes. In the following image, the Grid component does not assign indexes to data rows in the collapsed Partly cloudy group.
Get a Row Index
You can obtain row indexes as follows:
- Call the GetFocusedRowIndex() method to obtain the focused row’s index.
- Call the GetStartRowVisibleIndex() method to obtain the index of the first row on the current page.
- Within a template, use a
*VisibleIndexcontext property to obtain the associated row’s index. - In an event handler, use a
*VisibleIndexevent argument to obtain the processed row’s index. - Use values from
0toGrid.GetVisibleRowCount() - 1to iterate through all visible rows sequentially.
Once you obtain a row index, use the following API to obtain information about the row or interact with it.
Note
DxGrid bound to an Instant Feedback Data Source or GridDevExtremeDataSource loads data asynchronously in small portions (instead of the entire dataset). Before you call a method that accepts a row index as a parameter, call the WaitForRemoteSourceRowLoadAsync(Int32) method to ensure that the specified data row is loaded.
Identify Rows
Pass a row’s index to the following methods to retrieve row data and determine its type and state:
- GetRowLevel
- Gets the nesting level of the processed row.
- GetRowValue
- Gets the value of the data source field in the specified row.
- IsDetailRowExpanded
- Indicates whether the specified detail row is expanded.
- IsEditingRow
- Returns whether the specified Grid row is being edited.
- IsGroupRow
- Specifies whether the specified row is a group row.
- IsGroupRowExpanded
- Specifies whether the specified group row is expanded.
- IsRowFocused
- Identifies whether the row with the specified visible index is focused.
- IsRowSelected
- Returns whether the specified row is selected.
Interact with Rows
Call the following methods to change row state in code:
- CollapseDetailRow | ExpandDetailRow
- Collapse/expand the specified detail row.
- CollapseGroupRow | ExpandGroupRow
- Collapse/expand the specified group row.
- DeselectRow | SelectRow
- Select/deselect the specified row.
- DeselectRows | SelectRows
- Select/deselect the specified rows.
- MakeRowVisible | MakeCellVisible
- Navigate to the specified row/cell.
- SetFocusedRowIndex
- Moves focus to the specified row.
- ShowRowDeleteConfirmation
- Displays the delete confirmation dialog for the specified row.
- StartEditRowAsync
- Starts editing the specified row.
Row Indexes — Code Example
The following code sample adds a new command to group row context menus: Expand First-Level Groups.
@using DevExpress.Images.Blazor
@inject WeatherForecastService ForecastService
<DxGrid Data="@Data"
ShowGroupPanel="true"
ContextMenus="@GridContextMenus.GroupRow"
CustomizeContextMenu="CustomizeContextMenu">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Width="150px" />
<DxGridDataColumn FieldName="Forecast" GroupIndex="1" />
<DxGridDataColumn FieldName="CloudCover" GroupIndex="0"/>
</Columns>
</DxGrid>
@code {
object Data { get; set; }
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
void CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) {
if (args.Context is GridGroupRowCommandContext) {
var item = args.Items.AddCustomItem(0, "Expand First-Level Groups",
() => ExpandFirstLevelGroups(args.Grid));
item.IconUrl = @Icon.ArrowMaximizeVerticalDashed;
}
}
static void ExpandFirstLevelGroups(IGrid grid) {
grid.BeginUpdate();
for (var i = grid.GetVisibleRowCount() - 1; i >= 0; i--) {
if (grid.IsGroupRow(i) && grid.GetRowLevel(i) == 0 && !grid.IsGroupRowExpanded(i))
grid.ExpandGroupRow(i);
}
grid.EndUpdate();
}
}
Handle Row Clicks
The Blazor Grid component raises RowClick and RowDoubleClick events when users click or double-click data and group rows. Handle these events to respond to these actions. The following code example allows users to click group rows to expand/collapse them:
@inject WeatherForecastService ForecastService
<DxGrid Data="@Data" ShowGroupPanel="true" RowClick="OnRowClick">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Width="150px" />
<DxGridDataColumn FieldName="Forecast" GroupIndex="1" />
<DxGridDataColumn FieldName="CloudCover" GroupIndex="0"/>
</Columns>
</DxGrid>
@code {
object Data { get; set; }
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
void OnRowClick(GridRowClickEventArgs e) {
if (e.Grid.IsGroupRow(e.VisibleIndex)) {
e.Grid.BeginUpdate();
if(e.Grid.IsGroupRowExpanded(e.VisibleIndex))
e.Grid.CollapseGroupRow(e.VisibleIndex);
else
e.Grid.ExpandGroupRow(e.VisibleIndex);
e.Grid.EndUpdate();
}
}
}
Row Context Menus
The DevExpress Blazor Grid allows you to display context menus with predefined and custom commands for the following rows:
- Data rows
- Header and footer rows
- Group and group footer rows
Use the ContextMenus property to display contextual commands when users right-click Grid elements. Footer, group footer, and data row context menus do not include predefined commands. Handle the CustomizeContextMenu event to add custom commands for these rows.
The following example enables context menus for Grid rows:

@inject WeatherForecastService ForecastService
<DxGrid Data="@Data"
ShowGroupPanel="true"
ContextMenus="AvailableContextMenus"
CustomizeContextMenu="CustomizeContextMenu">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Width="150px" />
<DxGridDataColumn FieldName="Forecast" />
<DxGridDataColumn FieldName="CloudCover" GroupIndex="0"/>
</Columns>
<GroupSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Max" FieldName="TemperatureC"
FooterColumnName="TemperatureC" />
</GroupSummary>
<TotalSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Count" FieldName="Date" />
</TotalSummary>
</DxGrid>
@code {
object Data { get; set; }
GridContextMenus AvailableContextMenus = GridContextMenus.DataRow
| GridContextMenus.Header | GridContextMenus.Footer
| GridContextMenus.GroupRow | GridContextMenus.GroupFooter;
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
void CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) {
// Populates empty context menus with predefined commands
if (args.Context is GridFooterCommandContext) {
args.Items.Add(GridContextMenuDefaultItemNames.ShowColumnChooser);
args.Items.Add(GridContextMenuDefaultItemNames.ShowFilterBuilder);
}
if (args.Context is GridGroupFooterCommandContext || args.Context is GridDataRowCommandContext) {
args.Items.Add(GridContextMenuDefaultItemNames.ExpandAll);
args.Items.Add(GridContextMenuDefaultItemNames.CollapseAll);
}
}
}
Customize Rows
Highlight Rows on Hover
Set the HighlightRowOnHover to true to highlight a data row when users hover the mouse pointer over it:

@inject WeatherForecastService ForecastService
<DxGrid Data="@Data" HighlightRowOnHover="true">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Width="150px" />
<DxGridDataColumn FieldName="Forecast" />
<DxGridDataColumn FieldName="CloudCover" />
</Columns>
</DxGrid>
@code {
object Data { get; set; }
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
}
Conditionally Customize Rows
Handle the CustomizeElement event to customize row appearance. The following code sample adds tooltips that identify row types:
@inject WeatherForecastService ForecastService
<DxGrid Data="@Data"
ShowGroupPanel="true"
ShowFilterRow="true"
EditNewRowPosition="GridEditNewRowPosition.FixedOnTop"
CustomizeElement="Grid_CustomizeElement" >
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Width="150px" />
<DxGridDataColumn FieldName="Forecast" GroupIndex="1" />
<DxGridDataColumn FieldName="CloudCover" GroupIndex="0"/>
</Columns>
<GroupSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Max" FieldName="TemperatureC"
FooterColumnName="TemperatureC" />
</GroupSummary>
<TotalSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Count" FieldName="Date" />
</TotalSummary>
</DxGrid>
@code {
object Data { get; set; }
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
void Grid_CustomizeElement(GridCustomizeElementEventArgs e) {
// Use e.CssClass property to customize an element
e.Attributes["title"] = e.ElementType switch {
GridElementType.HeaderRow => "This is a header row",
GridElementType.DataRow => "This is a data row",
GridElementType.DetailCell => "This is a detail row",
GridElementType.GroupRow => "This is a group row",
GridElementType.FilterRow => "This is a filter row",
GridElementType.GroupFooterRow => "This is a group footer row",
GridElementType.FooterRow => "This is a footer row",
GridElementType.EditNewItemRow => "This is a new item row",
_ => null
};
}
}
Alternating Row Style
You can use the CustomizeElement event to highlight alternating (odd) rows with a different style to improve Grid readability:

@inject NwindDataService NwindDataService
<DxGrid @ref="Grid"
Data="@Data"
CustomizeElement="Grid_CustomizeElement"
VirtualScrollingEnabled="true">
<Columns>
<DxGridDataColumn FieldName="CompanyName" MinWidth="100" />
<DxGridDataColumn FieldName="City" Width="10%" />
<DxGridDataColumn FieldName="Region" Width="10%" />
<DxGridDataColumn FieldName="Country" Name="Country" Width="10%" />
<DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c" Width="10%" />
<DxGridDataColumn FieldName="Quantity" MinWidth="80" Width="10%" />
<DxGridDataColumn FieldName="Total"
Name="Total"
UnboundType="GridUnboundColumnType.Decimal"
UnboundExpression="[UnitPrice] * [Quantity]"
DisplayFormat="c"
MinWidth="100"
Width="15%" />
</Columns>
</DxGrid>
@code {
object Data { get; set; }
IGrid Grid { get; set; }
protected override async Task OnInitializedAsync() {
var invoices = await NwindDataService.GetInvoicesAsync();
var customers = await NwindDataService.GetCustomersAsync();
Data = invoices.OrderBy(i => i.OrderDate).Join(customers, i => i.CustomerId, c => c.CustomerId, (i, c) => {
return new {
CompanyName = c.CompanyName,
City = i.City,
Region = i.Region,
Country = i.Country,
UnitPrice = i.UnitPrice,
Quantity = i.Quantity
};
});
}
void Grid_CustomizeElement(GridCustomizeElementEventArgs e) {
if(e.ElementType == GridElementType.DataRow && e.VisibleIndex % 2 == 1) {
e.CssClass = "alt-item";
}
if(e.ElementType == GridElementType.HeaderCell) {
e.CssClass = "header-bold";
}
}
}