[Solved] Grouping in TelerikGrid with Dynamic Data (Dictionary<string, object>)

1 Answer 4 Views
Grid
Hamza
Top achievements
Rank 1
Hamza asked on 21 Jul 2026, 12:29 PM

I'm using the Telerik Blazor Grid with dynamic data (`Dictionary<string, object>`). Paging, sorting, and filtering work, but grouping doesn't.

It seems Telerik grouping relies on CLR properties (e.g., `Employee.Department`), while my data is stored as dictionary key/value pairs. I also tried using a `DataTable`, but grouping still doesn't work.

Has anyone successfully implemented grouping with `Dictionary<string, object>` or `DataTable`? Is there a supported solution for dynamic data?

 

# Minimal Code 
@page "/1"
@using Telerik.Blazor.Components
@using Telerik.DataSource;
@using Telerik.DataSource.Extensions;

<TelerikGrid TItem="Dictionary<string, object>"
             Pageable="true"
             Groupable="true"
             Sortable="true" OnRead="OnLoadData"
             Navigable="true"
             PageSize="10"
             SortMode="Telerik.Blazor.SortMode.Single"
             
             FilterMode="@Telerik.Blazor.GridFilterMode.FilterRow"
             Height="500px">
    <GridColumns>
        @foreach(var col in Columns)
        {
            <GridColumn Field="@col.Key" Title="@col.Key" FieldType="typeof(string)" />
        }
    </GridColumns>
</AgirhDataGrid>

@code {

    public List<KeyValuePair<string, string>> Columns { get; set; }


    private Task OnLoadData(GridReadEventArgs args)
    {
        var request = new DataSourceRequest
        {
            Groups = args.Request.Groups,
        };
        // GetData it's a System.DataTable 
        var dataSource = GetData.ToDataSourceResult(request);
        args.Data = dataSource.AggregateResults ?? dataSource.Data; 
        args.Total = dataSource.Total;
        return Task.CompletedTask;
    }
}

 

1 Answer, 1 is accepted

Sort by
0
Ivan Danchev
Telerik team
answered on 22 Jul 2026, 01:08 PM

Hello Hamza,

DataTable and Dictionary<string, object> cannot produce the nested AggregateFunctionsGroup structure that the Grid requires for grouping. The recommended workaround is ExpandoObject, which can be converted to that structure by ToDataSourceResult().

  • Use TItem="System.Dynamic.ExpandoObject" on the Grid
  • Convert your DataTable (or dictionary rows) to List<ExpandoObject> once
  • Let ToDataSourceResult(args.Request) handle all operations including grouping
  • Set FieldType correctly on each GridColumn

Here's an example: 

@using System.Dynamic
@using Telerik.Blazor.Components
@using Telerik.DataSource
@using Telerik.DataSource.Extensions

<h3>Grid with Dynamic Data and Grouping</h3>


<TelerikGrid TItem="ExpandoObject"
             OnRead="@OnReadHandler"
             Pageable="true"
             Sortable="true"
             Groupable="true"
             FilterMode="@GridFilterMode.FilterRow"
             Height="500px"
             PageSize="10">
    <GridColumns>
        @foreach (var col in Columns)
        {
            <GridColumn Field="@col.Name" Title="@col.Name" FieldType="@col.Type" />
        }
    </GridColumns>
</TelerikGrid>

@code {
    // Describes each dynamic column: its name and CLR type (needed by FieldType).
    private record ColInfo(string Name, Type Type);

    private List<ColInfo> Columns { get; set; } = new();
    private List<ExpandoObject> ExpandoData { get; set; } = new();

    protected override void OnInitialized()
    {
        // Build sample data that mimics what you would get from a DataTable
        // In your real code, replace this block with your actual DataTable/dictionary source
        // and call ConvertToExpandoList(yourDataTable) or ConvertToExpandoList(yourDictList).

        var sampleTable = BuildSampleDataTable();
        ExpandoData = ConvertToExpandoList(sampleTable);

        // Derive column metadata from the DataTable schema.
        Columns = sampleTable.Columns
            .Cast<System.Data.DataColumn>()
            .Select(c => new ColInfo(c.ColumnName, c.DataType))
            .ToList();
    }

    // OnRead: hand the full ExpandoObject list to ToDataSourceResult().
    // It handles paging, sorting, filtering AND grouping — including building the
    // AggregateFunctionsGroup hierarchy that the Grid requires.
    private Task OnReadHandler(GridReadEventArgs args)
    {
        var result = ExpandoData.ToDataSourceResult(args.Request);
        args.Data = result.Data;           // flat list OR nested AggregateFunctionsGroup list
        args.Total = result.Total;
        args.AggregateResults = result.AggregateResults;
        return Task.CompletedTask;
    }

    // Helpers - replace / remove once you plug in your real data source
    private static System.Data.DataTable BuildSampleDataTable()
    {
        var dt = new System.Data.DataTable();
        dt.Columns.Add("Name", typeof(string));
        dt.Columns.Add("Department", typeof(string));
        dt.Columns.Add("City", typeof(string));
        dt.Columns.Add("Salary", typeof(decimal));

        var departments = new[] { "Engineering", "Marketing", "HR" };
        var cities = new[] { "New York", "London", "Berlin" };
        var rng = new Random(42);

        for (int i = 1; i <= 50; i++)
        {
            dt.Rows.Add(
                $"Employee {i}",
                departments[i % departments.Length],
                cities[i % cities.Length],
                Math.Round((decimal)(30_000 + rng.NextDouble() * 70_000), 2));
        }

        return dt;
    }

    // Converts a DataTable to List<ExpandoObject>.
    // Each DataRow becomes one ExpandoObject whose properties match the column names.
    private static List<ExpandoObject> ConvertToExpandoList(System.Data.DataTable dt)
    {
        var list = new List<ExpandoObject>(dt.Rows.Count);

        foreach (System.Data.DataRow row in dt.Rows)
        {
            var expando = new ExpandoObject() as IDictionary<string, object?>;
            foreach (System.Data.DataColumn col in dt.Columns)
                expando[col.ColumnName] = row.IsNull(col) ? null : row[col];
            list.Add((ExpandoObject)expando);
        }

        return list;
    }

    // If your source is already List<Dictionary<string, object>> use this overload instead:
    private static List<ExpandoObject> ConvertToExpandoList(
        IEnumerable<Dictionary<string, object?>> rows)
    {
        var list = new List<ExpandoObject>();
        foreach (var dict in rows)
        {
            var expando = new ExpandoObject() as IDictionary<string, object?>;
            foreach (var kv in dict)
                expando[kv.Key] = kv.Value;
            list.Add((ExpandoObject)expando);
        }
        return list;
    }
}

Regards,
Ivan Danchev
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Tags
Grid
Asked by
Hamza
Top achievements
Rank 1
Answers by
Ivan Danchev
Telerik team
Share this question
or