How to implement DataGrid that can dynamically Add/Delete Column

I want to create a DataGrid that input the commission rate of different years. But this table should be horizontal instead of vertical as below:

<RadzenDataGrid @ref="DynamicRateGrid" TItem="IDictionary<int, decimal?>" Data="@RateListDict"
                AllowPaging="false" AllowColumnResize="true">
    <EmptyTemplate>
        <p style="color: lightgrey; font-size: 24px; text-align: center; margin: 2rem;">No records to display.</p>
    </EmptyTemplate>
    <Columns>
        <RadzenDataGridColumn Title="Year" Width="80px">
            <Template Context="context1">
                <div>Rate</div>
            </Template>
        </RadzenDataGridColumn>
        @foreach (var year in YearList)
        {
            <RadzenDataGridColumn TItem="IDictionary<int, decimal?>" Title="@year.ToString()" TextAlign="TextAlign.Center" CssClass="rz-p-1" Width="70px">
                <Template Context="context1">
                    @(context1.TryGetValue(year, out var value))
                    <RadzenNumeric TValue="decimal?" ShowUpDown="false" Value="@(value * 100)" Change="@(a => OnRateChange(year, a))" class="rz-p-1"></RadzenNumeric>
                </Template>
            </RadzenDataGridColumn>
        }
        <RadzenDataGridColumn>
            <Template Context="context1">
                <RadzenButton Icon="add" ButtonStyle="ButtonStyle.Primary" Variant="Variant.Flat" Size="ButtonSize.Medium" class="my-1 ms-1" Click="@((args) => AddRate())" @onclick:stopPropagation="true"></RadzenButton>
                <RadzenButton Icon="delete" ButtonStyle="ButtonStyle.Danger" Variant="Variant.Flat" Size="ButtonSize.Medium" class="my-1 ms-1" Click="@((args) => DeleteRate())" @onclick:stopPropagation="true"></RadzenButton>
            </Template>
        </RadzenDataGridColumn>
    </Columns>
</RadzenDataGrid>
        private List<Dictionary<int, decimal?>> RateListDict = [];

        private void AddRate()
        {
            var key = RateListDict.First().Keys.Max() + 1;
            RateListDict.First().Add(key, null);
            DynamicRateGrid.Reload();
            StateHasChanged();
        }

        private void DeleteRate()
        {
            RateListDict.First().Remove(RateListDict.First().Keys.Max());
            DynamicRateGrid.Reload();
            StateHasChanged();
        }

The DataGrid does not show/remove the column after clicking the add/delete button even the Dictionary was updated.

Here is what I've tried using our dynamic data demo: Blazor DataGrid Component - Dynamic Data | Free UI Components by Radzen

@using System.Linq.Dynamic.Core

<RadzenDataGrid @bind-Value=@selectedItems Data="@data" TItem="IDictionary<string, object>" ColumnWidth="200px"
                AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" FilterMode="FilterMode.Advanced" AllowPaging="true" AllowSorting="true">
    <Columns>
        <RadzenDataGridColumn Title="#">
            <Template>
                <RadzenButton Text="Add column" Click=@(args => { columns.Add($"Column{columns.Count()}", typeof(string)); PopulateData();} ) />
                <RadzenButton Text="Remove column" Disabled=@(!columns.Any()) Click=@(args => { columns.Remove($"Column{columns.Count()-1}"); PopulateData();} ) />
            </Template>
        </RadzenDataGridColumn>
        @foreach (var column in columns)
        {
            <RadzenDataGridColumn @key=@column.Key Title="@column.Key" Type="column.Value"
                                  Property="@GetColumnPropertyExpression(column.Key, column.Value)">
                <Template>
                    @context[@column.Key]
                </Template>
            </RadzenDataGridColumn>
        }
    </Columns>
</RadzenDataGrid>

@code {
    IList<IDictionary<string, object>> selectedItems;

    public IEnumerable<IDictionary<string, object>> data { get; set; }

    public IDictionary<string, Type> columns { get; set; } = new Dictionary<string, Type>();

    public string GetColumnPropertyExpression(string name, Type type)
    {
        var expression = $@"it[""{name}""].ToString()";

        if (type == typeof(int))
        {
            return $"int.Parse({expression})";
        }
        else if (type == typeof(DateTime))
        {
            return $"DateTime.Parse({expression})";
        }
        else if (type.IsEnum)
        {
            return $@"Int32(Enum.Parse(it[""{name}""].GetType(), {expression}))";
        }

        return expression;
    }

    void PopulateData()
    {
        data = Enumerable.Range(0, 1).Select(i =>
        {
            var row = new Dictionary<string, object>();

            foreach (var column in columns)
            {
                row.Add(
                    column.Key,
                    column.Value == typeof(int)
                            ? i
                            : column.Value == typeof(DateTime)
                                ? DateTime.Now.AddMonths(i)
                                : $"{column.Key}{i}"
                );
            }

            return row;
        });
    }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        PopulateData();
    }
}

add-column

1 Like

Thanks. Your code is really help