New to Telerik UI for ASP.NET Core? Download free 30-day trial

Implementing a Dynamic Grid

Environment

Product Grid for Progress® Telerik® UI for ASP.NET Core

Description

I am trying to bind a DataTable to a Telerik UI for ASP.NET Core Grid. The main reason for this is that the grid needs to be dynamic with a different number of columns etc. Although I've seen an example of this is MVC, I can't get this to work in ASP.NET Core. I need help with:

  1. Basic dynamic Grid binding to DataTable
  2. Dynamic name of column as per model value from backend
  3. Dynamic editing
  4. Bind to a DataTable in a Razor page

Solution

Basic Dynamic Binding

    @model System.Data.DataTable

    @(Html.Kendo().Grid<dynamic>()
        .Name("Grid")
        .Columns(columns =>
        {
            foreach (System.Data.DataColumn column in Model.Columns)
            {
                var c = columns.Bound(column.ColumnName);
            }
        })
        .Pageable()
        .Sortable()
        .Filterable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model =>
            {
                foreach (System.Data.DataColumn column in Model.Columns)
                {
                    var field = model.Field(column.ColumnName, column.DataType);
                }
            })
            .Read(read => read.Action("Customers_Read", "Grid"))
        )
    )
    public ActionResult Index()
    {
        DataTable products = GetDataTable(500);

        return View(products);
    }

    private static DataTable GetDataTable(int howMany)
    {
        var dataSource = new DataTable();
        dataSource.Columns.Add("Field1");
        dataSource.Columns.Add("Field2", typeof(int));
        for (int i = 0; i < howMany; i++)
        {
            dataSource.Rows.Add("value" + i, i);
        }
        return dataSource;
    }

    public ActionResult Customers_Read([DataSourceRequest] DataSourceRequest request)
    {
        return Json(GetDataTable(500).ToDataSourceResult(request));
    }

Dynamic Column Titles

  • The System.Data.DataColumn has a Caption property that can be used to dynamically change the tile of the columns:
    .Columns(columns =>
    {
        foreach (System.Data.DataColumn column in Model.Columns)
        {
            var c = columns.Bound(column.ColumnName).Title(column.Caption);
        }
    })

Dynamic Editing

Since the dynamic editing is not part of the official built-in options which utilized strongly typed models for ASP.NET Core, there are different solutions that can be applied. For a runnable example go to the examples repository. View.cshtml and Controller.

The first thing is to add the model Id - the primary key of the table and define the editors based on the column types:

    .Model(model =>
    {
        var id = Model.PrimaryKey[0].ColumnName;
        model.Id(id);
        foreach (System.Data.DataColumn column in Model.Columns)
        {
            var field = model.Field(column.ColumnName, column.DataType);
            if (column.ColumnName == id) {
                field.Editable(false);
            }

        }
    })
  1. You can use IFormCollection to intercept the updated item.

        public ActionResult Customers_Update([DataSourceRequest] DataSourceRequest request, IFormCollection data)
    
        public JsonResult OnPostUpdate([DataSourceRequest] DataSourceRequest request, [Bind(Prefix = "models")] IFormCollection models)
    
        public static DataTable db = new DataTable();
        public ActionResult Index()
        {
            db = GetDataTable(50);
    
            return View(db);
        }
    
        private DataTable GetDataTable(int howMany)
        {
            DataTable dt = GetDataTableColumns();
    
            for (int i = 0; i < howMany; i++)
            {
                int index = i + 1;
    
                DataRow row = dt.NewRow();
    
                row["OrderID"] = index;
                row["OrderDate"] = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0).AddHours(index);
                row["Freight"] = index * 0.1 + index * 0.01;
                row["ShipName"] = "Name " + index;
                row["ShipCity"] = "City " + index;
                row["ShipCountry"] = "Country " + index;
    
                dt.Rows.Add(row);
            }
    
            return dt;
        }
        public ActionResult Customers_Read([DataSourceRequest] DataSourceRequest request)
        {
            return Json(db.ToDataSourceResult(request));
        }
    
        public ActionResult Customers_Update([DataSourceRequest] DataSourceRequest request, IFormCollection data)
        {
            var dt = GetDataTableColumns();
            var updatedRow = dt.NewRow();
            for (int i = 0; i < db.Rows.Count; i++)
            {
                var itemToBeUpdatedId = data[db.PrimaryKey[0].ToString()][0];
                var row = db.Rows[i];
                if (row[db.PrimaryKey[0]].ToString() == itemToBeUpdatedId)
                {
                    for (var j = 0; j < db.Columns.Count; j++) 
                    {
                        if(data[db.Columns[j].ColumnName][0] != null)
                        { 
                            TypeConverter typeConverter = TypeDescriptor.GetConverter(db.Columns[j].DataType);
                            row[db.Columns[j].ColumnName] = typeConverter.ConvertFromString(data[db.Columns[j].ColumnName][0]);
                            updatedRow[db.Columns[j].ColumnName] = row[db.Columns[j].ColumnName];
                        }
                    }
    
                }
            }
            return Json(dt.ToDataSourceResult(request));
        }
    
        private DataTable GetDataTableColumns()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add(new DataColumn("OrderID", typeof(int)));
            dt.Columns.Add(new DataColumn("OrderDate", typeof(DateTime)));
            dt.Columns.Add(new DataColumn("Freight", typeof(decimal)));
            dt.Columns.Add(new DataColumn("ShipName", typeof(string)));
            dt.Columns.Add(new DataColumn("ShipCity", typeof(string)));
            dt.Columns.Add(new DataColumn("ShipCountry", typeof(string)));
            dt.PrimaryKey = new DataColumn[] { dt.Columns["OrderID"] };
            return dt;
        }
    
  2. Alternatively, you can post the model and values to the controller in a preferred format for you, so that you can process them on the server and update the corresponding table accordingly.

For example in a grid with inline editing shared by another programmer with us:

    // grid Save event and editable mode
    .Events(events => events.Save("savePopUpChange"))
    .Editable(ed=>ed.Mode(GridEditMode.PopUp)) 

    // data source Update method with additional data sent
    .Update(update => update.Action("Metadata_Update", "KendoGrid").Data("updateValuesData"))

    <script>  
        var keyValueList = [];
        function savePopUpChange(e) {     
            keyValueList.push(keyValuePair);
            var inputs = e.container.find("input");
            for (var i = 0; i < inputs.length; i++) {
                var element = inputs[i];
                var keyValuePair = element.name + ":" + element.value;
                keyValueList.push(keyValuePair);
            }
        }

        function updateValuesData(e) {
            return { keyValueList: keyValueList.join("|") }
        }
    </script>
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Metadata_Update([DataSourceRequest] DataSourceRequest request, string keyValueList)
    {
        var list = keyValueList.Split('|');
        //server side saving   
        return result;
    } 

To define editors based on a condition, you can use a switch case as shown below

    @model System.Data.DataTable

    @(Html.Kendo().Grid<dynamic>()
        .Name("gridMetadata")
        .Columns(columns =>
        {
            foreach (System.Data.DataColumn dcolumn in Model.ViewDataTable.Columns)
            {
                switch (dcolumn.DataType.ToString())
                {
                    case "System.Int16":
                        case "System.Int32":
                        case "System.Int64":
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).EditorTemplateName("Integer");                                 
                            break;

                        case "System.Decimal":
                        case "System.Double":
                        case "System.Float":
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).EditorTemplateName("Number");
                            break;
                        case "System.String":
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).EditorTemplateName("String");                                 
                            break;
                        case "System.Byte":
                        case "System.Boolean":
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).EditorTemplateName("Boolean");                                 
                            break;
                        case "System.DateTime":
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).Format("{0:d}").EditorTemplateName("Date");
                            break;
                        default:
                            columns.Bound(dcolumn.ColumnName).Title(dcolumn.Caption).EditorTemplateName("String");                               
                            break;

                }

            }
            columns.Command(command =>  {command.Edit();});
        }).

Dynamic Grid In Razor Page

    @Html.AntiForgeryToken()

    <script>
        function sendTokens() {
            return kendo.antiForgeryTokens();
        }
    </script>

    @(Html.Kendo().Grid<dynamic>()
        .Name("Grid")
        .Columns(columns =>
        {
            foreach (System.Data.DataColumn column in Model.DataTable.Columns)
            {
                var c = columns.Bound(column.ColumnName);
            }
        })
        .DataSource(dataSource => dataSource
            .Ajax()
            .Model(model =>
            {
                foreach (System.Data.DataColumn column in Model.DataTable.Columns)
                {
                    var field = model.Field(column.ColumnName, column.DataType);
                }
            })
            .Read(read => read.Url("/Customer?handler=Read").Data("sendTokens"))
        )
    )

    public DataTable DataTable { get; set; }

    public void OnGet()
    {
        DataTable = GetDataTable(500);
    }

    private static DataTable GetDataTable(int howMany)
    {
        var dataSource = new DataTable();
        dataSource.Columns.Add("Field1");
        dataSource.Columns.Add("Field2", typeof(int));
        for (int i = 0; i < howMany; i++)
        {
            dataSource.Rows.Add("value" + i, i);
        }
        return dataSource;
    }

    public JsonResult OnPostRead([DataSourceRequest] DataSourceRequest request)
    {

        return new JsonResult(GetDataTable(500).ToDataSourceResult(request));
    }

For a runnable example of a dynamic Grid with editing, check the UI for ASP.NET Core Examples.

More ASP.NET Core Grid Resources

See Also

In this article