Skip to main content
All docs
V26.1
  • Pop-Up and Inline Edit Forms in Blazor Grid

    • 21 minutes to read

    In EditForm or PopupEditForm mode, the Grid displays an inline or pop-up edit form instead of the edited data row. Users can click command buttons to create, modify, and delete grid rows.

    Blazor Grid Edit Form

    Run Demo: Edit Forms

    Video Tutorial

    Enable Editing

    The built-in edit form shows only the predefined Save and Cancel buttons. Use the EditFormTemplate to populate the edit form with editors. Call the GetEditor(String) method to add an automatically generated column editor in the edit form.

    Note

    When you place a templated component in the edit form template, a Razor error may occur. To prevent this error, specify the Context parameter explicitly for either the Grid template or the nested component.

    To enable data editing, you must:

    1. Use the EditFormTemplate to populate the edit form with editors.

    2. Declare a DxGridCommandColumn object in the Columns template to display the command column.

    3. If your data object has a primary key, assign it to the KeyFieldName or KeyFieldNames property.

    4. Handle EditModelSaving and DataItemDeleting events to save changes and reload Grid data.

    5. (Optional) Set the EditMode property to PopupEditForm to display the edit form in a pop-up window.

    6. (Optional) Use the EditNewRowPosition property to display the new item row or change the position of the inline edit form 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 form:

    @using Microsoft.EntityFrameworkCore
    @inject IDbContextFactory<NorthwindContext> NorthwindContextFactory
    @implements IDisposable
    
    <DxGrid Data="Data"
            EditModelSaving="OnEditModelSaving"
            DataItemDeleting="OnDataItemDeleting"
            CustomizeEditModel="OnCustomizeEditModel"
            KeyFieldName="EmployeeId">
        <Columns>
            <DxGridCommandColumn />
            <DxGridDataColumn FieldName="FirstName" />
            <DxGridDataColumn FieldName="LastName" />
            <DxGridDataColumn FieldName="Title" />
            <DxGridDataColumn FieldName="HireDate" />
        </Columns>
        <EditFormTemplate Context="editFormContext">
            <DxFormLayout>
                <DxFormLayoutItem Caption="First Name:">
                    @editFormContext.GetEditor("FirstName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Last Name:">
                    @editFormContext.GetEditor("LastName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Title:">
                    @editFormContext.GetEditor("Title")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Hire Date:">
                    @editFormContext.GetEditor("HireDate")
                </DxFormLayoutItem>
            </DxFormLayout>
        </EditFormTemplate>
    </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();
        }
    }
    

    This section contains a comprehensive editing-related API reference.

    Show API Reference
    DxGrid API member Type Description
    CustomValidators Property Allows you to declare custom validator components.
    EditFormButtonsVisible Property Specifies whether the edit form contains the predefined Save and Cancel buttons.
    EditFormTemplate Property Specifies the template used to display the edit form.
    EditMode Property Specifies how users edit Grid data.
    EditNewRowPosition Property Specifies the position of UI elements used to create new rows.
    PopupEditFormCssClass Property Assigns a CSS class to the pop-up edit form.
    PopupEditFormHeaderText Property Specifies text displayed in the pop-up edit form’s header.
    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
    CellDisplayTemplate Property Specifies a template used to display command column cells.
    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.
    DxGridDataColumn API member Type Description
    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 form-editing functionality.

    Customize Pop-Up Edit Form

    You can set the EditMode property to PopupEditForm to display the edit form in a pop-up window. Specify the PopupEditFormCssClass property to apply a CSS class to the pop-up edit form. Use the PopupEditFormHeaderText property to change the text in the edit form header.

    The following code snippet changes the pop-up edit form’s size and header text:

    Blazor Grid Popup Edit Form

    <DxGrid Data="Data"
            EditModelSaving="OnEditModelSaving"
            DataItemDeleting="OnDataItemDeleting"
            KeyFieldName="EmployeeId"
            EditMode="GridEditMode.PopupEditForm"
            PopupEditFormHeaderText="Edit Employee"
            PopupEditFormCssClass="my-style">
        <Columns>
            <DxGridCommandColumn />
            <DxGridDataColumn FieldName="FirstName" />
            <DxGridDataColumn FieldName="LastName" />
            <DxGridDataColumn FieldName="Title" />
            <DxGridDataColumn FieldName="HireDate" />
        </Columns>
        <EditFormTemplate Context="editFormContext">
            <DxFormLayout>
                <DxFormLayoutItem Caption="First Name:">
                    @editFormContext.GetEditor("FirstName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Last Name:">
                    @editFormContext.GetEditor("LastName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Title:">
                    @editFormContext.GetEditor("Title")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Hire Date:">
                    @editFormContext.GetEditor("HireDate")
                </DxFormLayoutItem>
            </DxFormLayout>
        </EditFormTemplate>
    </DxGrid>
    

    Hide Predefined Edit Form Buttons

    Disable the EditFormButtonsVisible option to hide the predefined Save and Cancel buttons. Instead of them, you can implement your own buttons. Note the following specifics:

    • Instead of the Save button, you can use a submit button or a button that calls the SaveChangesAsync method on click.

    • To discard changes and hide the edit form, call the CancelEditAsync method in a custom button’s click event handler.

    The following example displays custom Save and Cancel buttons in the edit form:

    Blazor Grid Edit Form with Custom Buttons

    <DxGrid Data="Data"
            EditModelSaving="OnEditModelSaving"
            DataItemDeleting="OnDataItemDeleting"
            KeyFieldName="EmployeeId"
            EditFormButtonsVisible="false">
        <Columns>
            <DxGridCommandColumn />
            <DxGridDataColumn FieldName="FirstName" />
            <DxGridDataColumn FieldName="LastName" />
            <DxGridDataColumn FieldName="Title" />
            <DxGridDataColumn FieldName="HireDate" />
        </Columns>
        <EditFormTemplate Context="editFormContext">
            <DxFormLayout>
                <DxFormLayoutItem Caption="First Name:">
                    @editFormContext.GetEditor("FirstName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Last Name:">
                    @editFormContext.GetEditor("LastName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Title:">
                    @editFormContext.GetEditor("Title")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Hire Date:">
                    @editFormContext.GetEditor("HireDate")
                </DxFormLayoutItem>
                <DxFormLayoutItem ColSpanMd="12">
                    <DxButton SubmitFormOnClick="true" Text="Save" />
                    <DxButton Click="@(() => MyGrid.CancelEditAsync())" Text="Cancel" />
                </DxFormLayoutItem>
            </DxFormLayout>
        </EditFormTemplate>
    </DxGrid>
    

    View Example: Use an External Popup as an Edit Form

    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:

    @inject WeatherForecastService ForecastService
    
    <DxGrid @ref="myGrid" Data="@forecasts" EditModelSaving="OnEditModelSaving">
        <Columns>
            <DxGridCommandColumn NewButtonVisible=false>
                <CellDisplayTemplate>
                    @{
                        var date = context.DataItem != null ? (context.DataItem as WeatherForecast).Date : DateTime.Now;
                        <DxButton Text="Edit"
                                  RenderStyle="ButtonRenderStyle.Link"
                                  Click="() => myGrid.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>
        <EditFormTemplate Context="editFormContext">
            <DxFormLayout>
                <DxFormLayoutItem Caption="Date:">
                    @editFormContext.GetEditor("Date")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Summary:">
                    @editFormContext.GetEditor("Summary")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Temperature (C):">
                    @editFormContext.GetEditor("TemperatureC")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Temperature (F):">
                    @editFormContext.GetEditor("TemperatureF")
                </DxFormLayoutItem>
            </DxFormLayout>
        </EditFormTemplate>
    </DxGrid>
    
    @code {
        List<WeatherForecast>? forecasts;
        IGrid myGrid { get; set; }
    
        protected override async Task OnInitializedAsync() {
            forecasts = await ForecastService.GetForecastAsync(DateTime.Now.AddDays(-2));
        }
    
        void OnEditModelSaving(GridEditModelSavingEventArgs e) {
            e.CopyChangesToDataItem();
        }
    }
    

    View Example: Disable Row Editing Depending on Row Values

    Use Different Edit Modes for Add and Edit Operations

    The following code snippet displays a pop-up edit form for new rows. Users can edit individual cells in existing rows directly with in-place editors:

    @inject DataService DataService
    
    <DxGrid @ref="Grid"
            Data="@products"
            EditMode="@editMode"
            DataItemDeleting="OnDataItemDeleting"
            EditStart="OnEditStart"
            EditCanceling="OnEditCanceling"
            EditModelSaving="OnEditModelSaving">
        <Columns>
            <DxGridCommandColumn EditButtonVisible="false"
                                 CancelButtonVisible="false"
                                 SaveButtonVisible="false" />
            <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.EditCell;
        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.EditCell);
        }
    
        void OnEditCanceling(GridEditCancelingEventArgs e) {
            SetEditMode(GridEditMode.EditCell);
        }
    
        void OnEditModelSaving(GridEditModelSavingEventArgs e) {
            var editModel = (Product)e.EditModel;
            if (e.IsNew)
                products.Add(editModel);
            else
                e.CopyChangesToDataItem();
    
            SetEditMode(GridEditMode.EditCell);
        }
    
        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 EditFormTemplate. 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 lists 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 matching cities.

    @inject NwindDataService NwindDataService
    @inject WorldcitiesDataService WorldcitiesDataService
    
    <DxGrid Data="DataSource"
            KeyFieldName="SupplierId"
            EditModelSaving="Grid_EditModelSaving"
            DataItemDeleting="Grid_DataItemDeleting">
        <Columns>
            <DxGridCommandColumn Width="110px" />
            <DxGridDataColumn FieldName="CompanyName" />
            <DxGridDataColumn FieldName="ContactName" />
            <DxGridDataColumn FieldName="Country" Width="10%" />
            <DxGridDataColumn FieldName="City" Width="10%" />
        </Columns>        
        <EditFormTemplate Context="EditFormContext">
            @{
                var supplier = (Supplier)EditFormContext.EditModel;
            }
            <DxFormLayout CssClass="w-100">
                <DxFormLayoutItem Caption="Company Name:" ColSpanMd="6">
                    @EditFormContext.GetEditor("CompanyName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Contact Name:" ColSpanMd="6">
                    @EditFormContext.GetEditor("ContactName")
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="Country:" ColSpanMd="6">
                    <DxComboBox Data="@Countries"
                                NullText="Select Country..."
                                SearchMode="@ListSearchMode.AutoSearch"
                                SearchFilterCondition="@ListSearchFilterCondition.Contains"
                                TextFieldName="CountryName"
                                ValueFieldName="CountryName"
                                Value="@supplier.Country"
                                ValueExpression="() => supplier.Country"
                                ValueChanged="(string newCellValue) => {
                                                    supplier.Country = newCellValue;
                                                    supplier.City = null;
                                                }">
                    </DxComboBox>
                </DxFormLayoutItem>
                <DxFormLayoutItem Caption="City:" ColSpanMd="6">
                    @{
                        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>
                </DxFormLayoutItem>
            </DxFormLayout>
        </EditFormTemplate>  
    </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();
        }
    }