RadzenDataGrid Inline Editing

I will have 3 components on 1 page where the same functions will be used

When I click on the add, edit or delete button, I get this error

Error

Error: System.InvalidOperationException: The render handle is not yet assigned.
   at Microsoft.AspNetCore.Components.RenderHandle.ThrowNotInitialized()
   at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
   at Radzen.Blazor.RadzenDataGrid`1.EditRowInternal(TItem item)
   at Radzen.Blazor.RadzenDataGrid`1.EditRow(TItem item)
   at BlazorWebApp.Components.Pages.Home.EditRow(Order order) in Project\BlazorWebApp\Components\Pages\Home.razor:line 60
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at BlazorWebApp.Components.Pages.Components.FirstGrid.EditRow(Order order) in Project\BlazorWebApp\Components\Pages\Components\FirstGrid.razor:line 98
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Radzen.Blazor.RadzenButton.OnClick(MouseEventArgs args)
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
@page "/"
@using BlazorWebApp.Components.Pages.Components

<PageTitle>Home</PageTitle>

<FirstGrid _orders="_orders" EditMode="EditMode"
           OrdersToInsert="_ordersToInsert"
           FuncReset="()=>Reset()"
           FuncEditRow="args => EditRow(args)"
           FuncOnUpdateRow="args => OnUpdateRow(args)"
           FuncSaveRow="args => SaveRow(args)"
           FuncCancelEdit="args => CancelEdit(args)"
           FuncDeleteRow="args => DeleteRow(args)"
           FuncInsertRow="InsertRow"
           FuncOnCreateRow="args => OnCreateRow(args)"
/>


@code{
    List<Order> _orders = [];
    List<Order> _ordersToInsert = [];
    List<Order> _ordersToUpdate = [];
    private RadzenDataGrid<Order> _ordersGrid = new();
    private const DataGridEditMode EditMode = DataGridEditMode.Single;
    protected override async Task OnInitializedAsync()
    {
        _orders =
        [
            new Order { OrderID = 1, Freight = 12.34m, ShipName = "Ship A", ShipCountry = "USA", ShipCity = "New York" },
            new Order { OrderID = 2, Freight = 56.78m, ShipName = "Ship B", ShipCountry = "Canada", ShipCity = "Toronto" },
            new Order { OrderID = 3, Freight = 9.99m, ShipName = "Ship C", ShipCountry = "UK", ShipCity = "London" },
            new Order { OrderID = 4, Freight = 22.22m, ShipName = "Ship D", ShipCountry = "Germany", ShipCity = "Berlin" },
            new Order { OrderID = 5, Freight = 44.44m, ShipName = "Ship E", ShipCountry = "France", ShipCity = "Paris" },
            new Order { OrderID = 6, Freight = 77.77m, ShipName = "Ship F", ShipCountry = "Italy", ShipCity = "Rome" },
            new Order { OrderID = 7, Freight = 15.15m, ShipName = "Ship G", ShipCountry = "Spain", ShipCity = "Madrid" },
            new Order { OrderID = 8, Freight = 99.99m, ShipName = "Ship H", ShipCountry = "Australia", ShipCity = "Sydney" },
            new Order { OrderID = 9, Freight = 10.10m, ShipName = "Ship I", ShipCountry = "Japan", ShipCity = "Tokyo" },
            new Order { OrderID = 10, Freight = 20.20m, ShipName = "Ship J", ShipCountry = "China", ShipCity = "Beijing" }
        ];
    }
    
    void Reset()
    {
        _ordersToInsert.Clear();
        _ordersToUpdate.Clear();
    }
    void Reset(Order order)
    {
        _ordersToInsert.Remove(order);
        _ordersToUpdate.Remove(order);
    }
    async Task EditRow(Order order)
    {
        if (EditMode == DataGridEditMode.Single && _ordersToInsert.Any())
        {
            Reset();
        }
        _ordersToUpdate.Add(order);
        await _ordersGrid.EditRow(order);
    }
    void OnUpdateRow(Order order)
    {
        Reset(order);

        // dbContext.Update(order);
        //
        // dbContext.SaveChanges();
    }
    async Task SaveRow(Order order)
    {
        await _ordersGrid.UpdateRow(order);
    }
    void CancelEdit(Order order)
    {
        Reset(order);

        _ordersGrid.CancelEditRow(order);

        // var orderEntry = dbContext.Entry(order);
        // if (orderEntry.State == EntityState.Modified)
        // {
        //     orderEntry.CurrentValues.SetValues(orderEntry.OriginalValues);
        //     orderEntry.State = EntityState.Unchanged;
        // }
    }
    async Task DeleteRow(Order order)
    {
        Reset(order);

        if (_orders.Contains(order))
        {
            // dbContext.Remove<Order>(order);
            //
            // dbContext.SaveChanges();

            await _ordersGrid.Reload();
        }
        else
        {
            _ordersGrid.CancelEditRow(order);
            await _ordersGrid.Reload();
        }
    }

    async Task InsertRow()
    {
        if (EditMode == DataGridEditMode.Single)
        {
            Reset();
        }

        var order = new Order();
        _ordersToInsert.Add(order);
        await _ordersGrid.InsertRow(order);
    }
    void OnCreateRow(Home.Order order)
    {
        // dbContext.Add(order);
        //
        // dbContext.SaveChanges();

        _ordersToInsert.Remove(order);
    }
    
}

@code{
    public class Order
    {
        public int OrderID { get; set; }
        public DateTime OrderDate { get; set; }
        public decimal Freight { get; set; }
        public string ShipName { get; set; } = string.Empty;
        public string ShipCountry { get; set; } = string.Empty;
        public string ShipCity { get; set; } = string.Empty;
    }
}

FirstGrid - Component

<RadzenDataGrid @ref="_ordersGrid" AllowAlternatingRows="false" AllowFiltering="true" AllowPaging="true" PageSize="5" AllowSorting="true" 
                EditMode="@EditMode"
                Data="@_orders" TItem="Home.Order" 
                RowUpdate="@OnUpdateRow" RowCreate="@OnCreateRow" 
                Sort="@HandleReset" Page="@HandleReset" Filter="@HandleReset">
    <HeaderTemplate>
        <RadzenButton ButtonStyle="ButtonStyle.Success" Icon="add_circle" Text="Add New Order" 
                      Click="@InsertRow" 
                      Disabled="@(EditMode == DataGridEditMode.Single && OrdersToInsert.Any())" />
    </HeaderTemplate>
    <Columns>
        <RadzenDataGridColumn Property="OrderID" Title="Order ID"  Frozen="true" Width="5%"/>
        <RadzenDataGridColumn Property="@nameof(Home.Order.OrderDate)" Title="Order Date">
            <Template Context="order">
                @String.Format("{0:d}", order.OrderDate)
            </Template>
            <EditTemplate Context="order">
                <RadzenDatePicker @bind-Value="order.OrderDate" Style="width:100%" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "Select order date" }})" />
            </EditTemplate>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="@nameof(Home.Order.Freight)" Title="Freight">
            <Template Context="order">
                @String.Format(new System.Globalization.CultureInfo("en-US"), "{0:C}", order.Freight)
            </Template>
            <EditTemplate Context="order">
                <RadzenNumeric @bind-Value="order.Freight" InputAttributes="@(new Dictionary<string,object>(){ { "aria-label", "Select freight" }})" />
            </EditTemplate>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="@nameof(Home.Order.ShipName)" Title="Ship Name">
            <EditTemplate Context="order">
                <RadzenTextBox @bind-Value="order.ShipName"Name="ShipName" aria-label="Enter ship name" />
                <RadzenRequiredValidator Text="ShipName is required" Component="ShipName" Popup="true" />
            </EditTemplate>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="@nameof(Home.Order.ShipCountry)" Title="ShipCountry">
            <EditTemplate Context="order">
                <RadzenTextBox @bind-Value="order.ShipCountry" Name="ShipCountry" aria-label="Enter ship country" />
                <RadzenRequiredValidator Text="ShipCountry is required" Component="ShipCountry" Popup="true" />
            </EditTemplate>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="@nameof(Home.Order.ShipCity)" Title="ShipCity">
            <EditTemplate Context="order">
                <RadzenTextBox @bind-Value="order.ShipCity" Style="width:200px; display: block" Name="ShipCity" aria-label="Enter ship city" />
                <RadzenRequiredValidator Text="ShipCity is required" Component="ShipCity" Popup="true" />
            </EditTemplate>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Context="order" Filterable="false" Sortable="false" 
                              TextAlign="TextAlign.Right" Frozen="true" FrozenPosition="FrozenColumnPosition.Right">
            <Template Context="order">
                <RadzenButton Icon="edit" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" Click="@(args => EditRow(order))" @onclick:stopPropagation="true">
                </RadzenButton>
                <RadzenButton ButtonStyle="ButtonStyle.Danger" Icon="delete" Variant="Variant.Flat" Shade="Shade.Lighter" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => DeleteRow(order))" @onclick:stopPropagation="true">
                </RadzenButton>
            </Template>
            <EditTemplate Context="order">
                <RadzenButton Icon="check" ButtonStyle="ButtonStyle.Success" Variant="Variant.Flat" Size="ButtonSize.Medium" Click="@((args) => SaveRow(order))" aria-label="Save">
                </RadzenButton>
                <RadzenButton Icon="close" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@((args) => CancelEdit(order))" aria-label="Cancel">
                </RadzenButton>
                <RadzenButton ButtonStyle="ButtonStyle.Danger" Icon="delete" Variant="Variant.Flat" Shade="Shade.Lighter" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => DeleteRow(order))" aria-label="Delete">
                </RadzenButton>
            </EditTemplate>
        </RadzenDataGridColumn>
    </Columns>
</RadzenDataGrid>
@code {
    [Parameter] public List<Home.Order> _orders { get; set; } = [];
    [Parameter] public List<Home.Order> OrdersToInsert { get; set; } = [];
    [Parameter] public RadzenDataGrid<Home.Order> _ordersGrid {get; set; }
    [Parameter] public DataGridEditMode EditMode { get; set; } = DataGridEditMode.Single;
    [Parameter] public EventCallback<Home.Order> FuncReset { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncEditRow { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncOnUpdateRow { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncSaveRow { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncCancelEdit { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncDeleteRow { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncInsertRow { get; set; }
    [Parameter] public EventCallback<Home.Order> FuncOnCreateRow { get; set; }
    
    private async Task HandleReset()
    {
        if (FuncReset.HasDelegate)
        {
            await FuncReset.InvokeAsync();
        }
    }
    private async Task HandleReset(Home.Order order)
    {
        if (FuncReset.HasDelegate)
        {
            await FuncReset.InvokeAsync(order);
        }
    }
    private async Task EditRow(Home.Order order)
    {
        if (FuncEditRow.HasDelegate)
        {
            await FuncEditRow.InvokeAsync(order);
        }
    }
    private void OnUpdateRow(Home.Order order)
    {
        if (FuncOnUpdateRow.HasDelegate)
        {
            FuncOnUpdateRow.InvokeAsync(order);
        }
    }
    private async Task SaveRow(Home.Order order)
    {
        if (FuncSaveRow.HasDelegate)
        {
            await FuncSaveRow.InvokeAsync(order);
        }
    }
    void CancelEdit(Home.Order order)
    {
        if (FuncCancelEdit.HasDelegate)
        {
            FuncCancelEdit.InvokeAsync(order);
        }
    }
    async Task DeleteRow(Home.Order order)
    {
        if (FuncDeleteRow.HasDelegate)
        {
            await FuncDeleteRow.InvokeAsync(order);
        }
    }
    async Task InsertRow()
    {
        if (FuncInsertRow.HasDelegate)
        {
            await FuncInsertRow.InvokeAsync();
        }
    }
    void OnCreateRow(Home.Order order)
    {
        if (FuncOnCreateRow.HasDelegate)
        {
            FuncOnCreateRow.InvokeAsync(order);
        }
    }
}

There is a good explanation on this thread:

In my opinion the following happens in your case:

  • You have a circular dependency between a child and a parent component that might cause a parent to re-render as part of a children invoking some callback parameter from the parent as part of its initialization