Inline Edit Row in Blazor Grid
- 22 minutes to read
In EditRow mode, the Grid component displays in-place editors in all data cells of the edited row. Users can click command buttons to create, modify, and delete grid rows.

The following keyboard shortcuts speed up row editing in EditRow mode:
- Enter
- Validates input and saves changes.
- Esc
- Discards all changes made in the row and cancels row editing.
- Tab and Shift+Tab
- Move focus between focusable content in Grid cells (editors, buttons, cells in display mode).
Refer to the following help topic for the full list of shortcut keys: Keyboard Support in Blazor Grid.
Video Tutorial
Enable Editing
Follow the steps below to allow users to create, modify, and delete rows in EditRow mode.
Set the EditMode property to
EditRow.If your data object has a primary key, assign it to the KeyFieldName or KeyFieldNames property.
Declare a DxGridCommandColumn object in the Columns template to display the command column.
Handle EditModelSaving and DataItemDeleting events to save changes and reload Grid data. Refer to the following topic for additional information: Save Changes.
(Optional) Use the EditNewRowPosition property to display the new item row or change the position of the edit row displayed for new records.
When users start editing a row, the Grid creates a clone of the edited data item, called an edit model. During edit operations, the Grid applies user changes only to the edit model and keeps the data item unchanged until the EditModelSaving or DataItemDeleting event fires. Refer to the following topic for additional information: Edit Model in Blazor Grid.
The following code sample allows users to edit Grid data in the inline edit row:
@inject NwindDataService NwindDataService
@using Microsoft.EntityFrameworkCore
@inject IDbContextFactory<NorthwindContext> NorthwindContextFactory
@implements IDisposable
<DxGrid Data="Data"
KeyFieldName="EmployeeId"
EditModelSaving="OnEditModelSaving"
DataItemDeleting="OnDataItemDeleting"
CustomizeEditModel="OnCustomizeEditModel"
EditMode="GridEditMode.EditRow">
<Columns>
<DxGridCommandColumn />
<DxGridDataColumn FieldName="FirstName" />
<DxGridDataColumn FieldName="LastName" />
<DxGridDataColumn FieldName="Title" />
<DxGridDataColumn FieldName="HireDate" />
</Columns>
</DxGrid>
@code {
IEnumerable<Employee> Data { get; set; }
NorthwindContext Northwind { get; set; }
protected override async Task OnInitializedAsync() {
Northwind = NorthwindContextFactory.CreateDbContext();
Data = await Northwind.Employees.ToListAsync();
}
void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e) {
if(e.IsNew) {
var editModel = (Employee)e.EditModel;
editModel.EmployeeId = Data.Max(x => x.EmployeeId) + 1;
}
}
async Task OnEditModelSaving(GridEditModelSavingEventArgs e) {
var editModel = (Employee)e.EditModel;
if (e.IsNew)
await Northwind.AddAsync(editModel);
else
e.CopyChangesToDataItem();
// Post changes to the database.
await Northwind.SaveChangesAsync();
// Reload the entire Grid.
Data = await Northwind.Employees.ToListAsync();
}
async Task OnDataItemDeleting(GridDataItemDeletingEventArgs e) {
// Remove the data item from the database.
Northwind.Remove(e.DataItem);
await Northwind.SaveChangesAsync();
// Reload the entire Grid.
Data = await Northwind.Employees.ToListAsync();
}
public void Dispose() {
Northwind?.Dispose();
}
}
Related API
This section contains a comprehensive editing-related API reference.
| DxGrid API member | Type | Description |
|---|---|---|
| CustomValidators | Property | Allows you to declare custom validator components. |
| DataColumnCellEditTemplate | Property | Allows you to replace automatically generated editors with custom content in all edit cells displayed for data columns. |
| EditMode | Property | Specifies how users edit Grid data. |
| EditNewRowPosition | Property | Specifies the position of UI elements used to create new rows. |
| EditorRenderMode | Property | Specifies how the Grid renders editors in the filter row and in data rows during edit operations. |
| ValidationEnabled | Property | Specifies whether the Grid validates user input in DevExpress data editors located in the edit form or edit cells. |
| CancelEditAsync() | Method | Cancels row editing and discards changes. |
| GetColumnEditSettings<T>(String) | Method | Returns editor settings of the column bound to the specified data source field. |
| GetEditContext() | Method | Returns the edit context. |
| IsEditing() | Method | Returns whether the Grid is being edited. |
| IsEditingNewRow() | Method | Returns whether a new Grid row is being edited. |
| IsEditingRow(Int32) | Method | Returns whether the specified Grid row is being edited. |
| SaveChangesAsync() | Method | Triggers validation and raises the EditModelSaving event if validation succeeds. The method immediately raises this event if validation is disabled. |
| ShowDataItemDeleteConfirmation(Object) | Method | Displays the delete confirmation dialog for the specified data item. If a user confirms the operation, the method raises the DataItemDeleting event. |
| ShowRowDeleteConfirmation(Int32) | Method | Displays the delete confirmation dialog for the specified row. If a user confirms the operation, the method raises the DataItemDeleting event. |
| StartEditDataItemAsync(Object, String) | Method | Starts editing the specified data item. |
| StartEditNewRowAsync(String) | Method | Starts editing a new row. |
| StartEditRowAsync(Int32, String) | Method | Starts editing the row with the specified visible index. |
| CustomizeDataRowEditor | Event | Allows you to customize a cell editor in a data row. |
| CustomizeEditModel | Event | Allows you to create a custom edit model or customize an automatically generated edit model. |
| DataItemDeleting | Event | Fires when a user confirms the delete operation in the delete confirmation dialog. |
| EditCanceling | Event | Fires before the Grid cancels the edit operation and discards changes. |
| EditModelSaving | Event | Fires if validation succeeds after a user saves changes or you call the SaveChangesAsync() method. |
| EditStart | Event | Fires before the Grid starts editing a row. |
| DxGridCommandColumn API member | Type | Description |
|---|---|---|
| CancelButtonVisible | Property | Specifies whether the command column displays the Cancel button in EditRow or EditCell edit mode. |
| CellDisplayTemplate | Property | Specifies a template used to display command column cells. |
| CellEditTemplate | Property | Specifies a template used to display the command column’s edit cell. |
| DeleteButtonVisible | Property | Specifies whether the command column displays Delete buttons. |
| DisplayMode | Property | Specifies whether command buttons display icons, captions, or both. |
| EditButtonVisible | Property | Specifies whether the command column displays Edit buttons. |
| HeaderTemplate | Property | Specifies a template used to display the command column header. |
| NewButtonVisible | Property | Specifies whether the command column displays the New button. |
| SaveButtonVisible | Property | Specifies whether the command column displays the Save button in EditRow or EditCell edit mode. |
| DxGridDataColumn API member | Type | Description |
|---|---|---|
| CellEditTemplate | Property | Allows you to replace an automatically generated editor with custom content in the column’s edit cell. |
| DataRowEditorVisible | Property | Specifies whether to render the editor associated with this column in the column edit cell, edit form, or pop-up edit form. |
| EditSettings | Property | Allows you to customize the editor associated with this column. |
| ReadOnly | Property | Specifies whether a user can change the column editor value when the Grid is in edit mode. |
Task-Based Examples
This section includes code samples that demonstrate row-editing functionality.
Data Editing With Custom Command Buttons
You can also manage edit operations without a command column. Instead of built-in command buttons, create custom buttons in the Grid toolbar or another location.
Use the following methods to implement external command buttons:
- StartEditNewRowAsync
- Starts editing a new row.
- StartEditRowAsync | StartEditDataItemAsync
- Starts editing the specified row or data item.
- SaveChangesAsync
- Initializes the save process and fires the EditModelSaving event. Handle this event to post changes to the underlying data source.
- CancelEditAsync
- Cancels row editing and discards changes.
- ShowRowDeleteConfirmation | ShowDataItemDeleteConfirmation
- Initializes the delete process and displays the delete confirmation dialog for the specified row or data item. When a user confirms the delete operation, the DataItemDeleting event fires. Handle this event to delete the record from the underlying data source.
<div class="grid-container">
<div class="pager-container">
<div>
<DxButton Text="Add" Click="() => Grid.StartEditNewRowAsync()" />
<DxButton Text="Edit" Click="() => Grid.StartEditRowAsync(Grid.GetFocusedRowIndex())" />
<DxButton Text="Delete" Click="() => Grid.ShowRowDeleteConfirmation(Grid.GetFocusedRowIndex())" />
</div>
<div>
<DxButton Text="Save" Click="() => Grid.SaveChangesAsync()" Enabled="IsEditing" />
<DxButton Text="Cancel" Click="() => Grid.CancelEditAsync()" Enabled="IsEditing"/>
</div>
</div>
<DxGrid @ref="Grid" Data="DataSource" FocusedRowEnabled="true" EditMode="GridEditMode.EditRow"
KeyFieldName="EmployeeId"
CustomizeEditModel="Grid_CustomizeEditModel"
EditModelSaving="Grid_EditModelSaving"
DataItemDeleting="Grid_DataItemDeleting">
<Columns>
<DxGridDataColumn FieldName="FirstName" />
<DxGridDataColumn FieldName="LastName" />
<DxGridDataColumn FieldName="Title" />
</Columns>
</DxGrid>
</div>
@code {
bool IsEditing => Grid != null ? Grid.IsEditing() : false;
IEnumerable<EditableEmployee> DataSource { get; set; }
IGrid Grid { get; set; }
protected override async Task OnInitializedAsync() {
DataSource = await NwindDataService.GetEmployeesEditableAsync();
}
void Grid_CustomizeEditModel(GridCustomizeEditModelEventArgs e) {
if(e.IsNew) {
var newEmployee = (EditableEmployee)e.EditModel;
newEmployee.FirstName = "John";
newEmployee.LastName = "Doe";
}
}
async Task Grid_EditModelSaving(GridEditModelSavingEventArgs e) {
if(e.IsNew)
await NwindDataService.InsertEmployeeAsync((EditableEmployee)e.EditModel);
else
await NwindDataService.UpdateEmployeeAsync((EditableEmployee)e.DataItem, (EditableEmployee)e.EditModel);
await UpdateDataAsync();
}
async Task Grid_DataItemDeleting(GridDataItemDeletingEventArgs e) {
await NwindDataService.RemoveEmployeeAsync((EditableEmployee)e.DataItem);
await UpdateDataAsync();
}
async Task UpdateDataAsync() {
DataSource = await NwindDataService.GetEmployeesEditableAsync();
}
}

Disable Column Editing
You can use one of the following column properties to prevent users from editing a specific column:
- ReadOnly
- Specifies whether column editors are in read-only mode.
- DataRowEditorVisible
- Specifies whether the edit row displays an editor for this column. If set to
false, the Grid preserves the column in display mode and skips it during Tab navigation within the edited row.
The following code snippet prevents users from editing the Employee ID column:
<DxGrid Data="Data"
KeyFieldName="EmployeeId"
EditModelSaving="OnEditModelSaving"
DataItemDeleting="OnDataItemDeleting"
CustomizeEditModel="OnCustomizeEditModel"
EditMode="GridEditMode.EditRow">
<Columns>
<DxGridCommandColumn />
<DxGridDataColumn FieldName="EmployeeId" DataRowEditorVisible="false" />
<DxGridDataColumn FieldName="FirstName" />
<DxGridDataColumn FieldName="LastName" />
<DxGridDataColumn FieldName="Title" />
<DxGridDataColumn FieldName="HireDate" />
</Columns>
</DxGrid>
Conditionally Disable Row Editing
To prevent users from editing specific records, create a custom Edit button and disable it for the corresponding rows. The following example prevents users from editing records dated today or earlier:
@using DevExpress.Images.Blazor
@inject WeatherForecastService ForecastService
<DxGrid Data="@forecasts" EditMode="GridEditMode.EditRow" EditModelSaving="OnEditModelSaving">
<Columns>
<DxGridCommandColumn NewButtonVisible=false>
<CellDisplayTemplate>
@{
var date = context.DataItem != null ?
(context.DataItem as WeatherForecast).Date : DateTime.Now;
<DxButton IconUrl="@Icon.EditPen"
RenderStyle="ButtonRenderStyle.Link"
Attributes="@(new Dictionary<string, object> { { "aria-label", "Edit" },
{ "Title", "Edit" } })"
Click="() => context.Grid.StartEditDataItemAsync(context.DataItem)"
Enabled="@(date >= DateTime.Now)" />
}
</CellDisplayTemplate>
</DxGridCommandColumn>
<DxGridDataColumn Caption="Date" FieldName="Date"/>
<DxGridDataColumn Caption="Summary" FieldName="Summary"/>
<DxGridDataColumn Caption="Temperature (C)" FieldName="TemperatureC"/>
<DxGridDataColumn Caption="Temperature (F)" FieldName="TemperatureF"/>
</Columns>
</DxGrid>
@code {
List<WeatherForecast>? forecasts;
protected override async Task OnInitializedAsync() {
forecasts = await ForecastService.GetForecastAsync(DateTime.Now.AddDays(-2));
}
void OnEditModelSaving(GridEditModelSavingEventArgs e) {
e.CopyChangesToDataItem();
}
}
Use Different Edit Modes for Add and Edit Operations
The following code snippet displays a pop-up edit form for new rows. For existing records, the Grid displays an edit row:
@inject DataService DataService
<DxGrid @ref="Grid"
Data="@products"
EditMode="@editMode"
DataItemDeleting="OnDataItemDeleting"
EditStart="OnEditStart"
EditModelSaving="OnEditModelSaving">
<Columns>
<DxGridCommandColumn />
<DxGridDataColumn FieldName="ProductName" />
<DxGridDataColumn FieldName="QuantityPerUnit" />
<DxGridDataColumn FieldName="UnitPrice" DisplayFormat="c2" />
<DxGridDataColumn FieldName="Discontinued" />
</Columns>
<EditFormTemplate Context="editFormContext">
<DxFormLayout>
<DxFormLayoutItem Caption="Product Name:" ColSpanMd="6">
@editFormContext.GetEditor("ProductName")
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Quantity per Unit:" ColSpanMd="6">
@editFormContext.GetEditor("QuantityPerUnit")
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Unit Price:" ColSpanMd="6">
@editFormContext.GetEditor("UnitPrice")
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Discontinued:" ColSpanMd="6">
@editFormContext.GetEditor("Discontinued")
</DxFormLayoutItem>
</DxFormLayout>
</EditFormTemplate>
</DxGrid>
@code {
IGrid Grid { get; set; }
private GridEditMode editMode = GridEditMode.EditRow;
private List<Product> products = new();
private List<Category> categories = new();
protected override void OnInitialized() {
products = DataService.GetProducts().ToList();
categories = DataService.GetCategories().ToList();
}
void OnDataItemDeleting(GridDataItemDeletingEventArgs e) {
if (e.DataItem is Product product)
products.Remove(product);
}
void OnEditStart(GridEditStartEventArgs e) {
SetEditMode(e.IsNew ? GridEditMode.PopupEditForm : GridEditMode.EditRow);
}
void OnEditModelSaving(GridEditModelSavingEventArgs e) {
var editModel = (Product)e.EditModel;
if (e.IsNew)
products.Add(editModel);
else
e.CopyChangesToDataItem();
}
void SetEditMode(GridEditMode mode) {
if (editMode == mode) return;
Grid.BeginUpdate();
editMode = mode;
Grid.EndUpdate();
}
}
Implement Cascading ComboBoxes
To implement cascading cell editors, place DxComboBox components in the CellEditTemplate properties of the related columns. Bind each ComboBox to the corresponding edit model field and use the Data property to populate cascading editors with data.
In the following example, the Country ComboBox displays the complete list of countries. When a user selects a country, the ValueChanged handler updates the Country field and clears the current City value. The City ComboBox filters its bound data source based on the selected country and displays only matching cities.
@inject NwindDataService NwindDataService
@inject WorldcitiesDataService WorldcitiesDataService
<DxGrid Data="DataSource"
KeyFieldName="SupplierId"
EditModelSaving="Grid_EditModelSaving"
DataItemDeleting="Grid_DataItemDeleting"
EditMode="GridEditMode.EditRow">
<Columns>
<DxGridCommandColumn Width="110px" />
<DxGridDataColumn FieldName="CompanyName" />
<DxGridDataColumn FieldName="ContactName" />
<DxGridDataColumn FieldName="Country" Width="10%">
<CellEditTemplate Context="cellEditContext">
@{
var supplier = (Supplier)cellEditContext.EditModel;
}
<DxComboBox Data="@Countries"
NullText="Select Country..."
SearchMode="@ListSearchMode.AutoSearch"
SearchFilterCondition="@ListSearchFilterCondition.Contains"
TextFieldName="CountryName"
ValueFieldName="CountryName"
ValueExpression="() => supplier.Country"
Value="@supplier.Country"
ValueChanged="(string newCellValue) => {
supplier.Country = newCellValue;
supplier.City = null;
}">
</DxComboBox>
</CellEditTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="City" Width="10%">
<CellEditTemplate Context="cellEditContext">
@{
var supplier = (Supplier)cellEditContext.EditModel;
var countryId = Countries.Where(c => c.CountryName == supplier.Country)
.FirstOrDefault()?.CountryId;
var cities = Cities.Where(x => x.CountryId == countryId);
}
<DxComboBox Data="@cities"
NullText="Select City..."
SearchMode="@ListSearchMode.AutoSearch"
SearchFilterCondition="@ListSearchFilterCondition.Contains"
@bind-Value="@supplier.City"
TextFieldName="CityName"
ValueFieldName="CityName">
</DxComboBox>
</CellEditTemplate>
</DxGridDataColumn>
</Columns>
</DxGrid>
@code {
IEnumerable<Supplier> DataSource { get; set; }
IEnumerable<Country> Countries { get; set; }
IEnumerable<City> Cities { get; set; }
protected override async Task OnInitializedAsync() {
DataSource = await NwindDataService.GetSuppliersEditableAsync();
Countries = await WorldcitiesDataService.GetCountriesAsync();
Cities = await WorldcitiesDataService.GetCitiesAsync();
}
async Task Grid_EditModelSaving(GridEditModelSavingEventArgs e) {
if (e.IsNew)
await NwindDataService.InsertSupplierAsync((Supplier)e.EditModel);
else
await NwindDataService.UpdateSupplierAsync((Supplier)e.DataItem, (Supplier)e.EditModel);
await UpdateDataAsync();
}
async Task Grid_DataItemDeleting(GridDataItemDeletingEventArgs e) {
await NwindDataService.RemoveSupplierAsync((Supplier)e.DataItem);
await UpdateDataAsync();
}
async Task UpdateDataAsync() {
DataSource = await NwindDataService.GetSuppliersEditableAsync();
}
}