diff --git a/blazor/datagrid/data-binding/local-data.md b/blazor/datagrid/data-binding/local-data.md index 365a0e1e1e..edda92ed76 100644 --- a/blazor/datagrid/data-binding/local-data.md +++ b/blazor/datagrid/data-binding/local-data.md @@ -9,31 +9,30 @@ documentation: ug # Local data in Blazor DataGrid -The Syncfusion® Blazor DataGrid offers a straightforward way to bind local data, such as arrays or JSON objects, to the Grid. This feature allows you to display and manipulate data within the Grid without the need for external server calls, making it particularly useful for scenarios where you're working with static or locally stored data. +The Syncfusion® Blazor DataGrid offers a straightforward way to bind local data such as lists or static collections. This approach is suitable for scenarios where data is available within the application and does not require communication with a remote server. -To achieve this, you can assign a JavaScript object array to the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property. Additionally, you have an option to provide the local data source using an instance of the **SfDataManager**. - -The following example demonstrates how to utilize the local data binding feature in the Grid: +Local data can be assigned directly to the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property of the Grid. {% tabs %} {% highlight razor tabtitle="Index.razor" %} @using Syncfusion.Blazor.Grids - + - - - - + + + + @code { - private List OrderData; + private List orderData = new(); + protected override void OnInitialized() { - OrderData = OrderDetails.GetAllRecords(); + orderData = OrderDetails.GetAllRecords(); } } @@ -43,65 +42,58 @@ The following example demonstrates how to utilize the local data binding feature public class OrderDetails { - public static List order = new List(); - - public OrderDetails() { } + public int OrderID { get; set; } + public string CustomerID { get; set; } + public string ShipCity { get; set; } + public string ShipName { get; set; } - public OrderDetails(int OrderID, string CustomerId, string ShipCity, string ShipName) + public OrderDetails(int orderID, string customerID, string shipCity, string shipName) { - this.OrderID = OrderID; - this.CustomerID = CustomerId; - this.ShipCity = ShipCity; - this.ShipName = ShipName; + OrderID = orderID; + CustomerID = customerID; + ShipCity = shipCity; + ShipName = shipName; } public static List GetAllRecords() { - if (order.Count == 0) + return new List { - order.Add(new OrderDetails(10248, "VINET", "Reims", "Vins et alcools Chevalier")); - order.Add(new OrderDetails(10249, "TOMSP", "Münster", "Toms Spezialitäten")); - order.Add(new OrderDetails(10250, "HANAR", "Rio de Janeiro", "Hanari Carnes")); - order.Add(new OrderDetails(10251, "VICTE", "Lyon", "Victuailles en stock")); - order.Add(new OrderDetails(10252, "SUPRD", "Charleroi", "Suprêmes délices")); - order.Add(new OrderDetails(10253, "HANAR", "Rio de Janeiro", "Hanari Carnes")); - order.Add(new OrderDetails(10254, "CHOPS", "Bern", "Chop-suey Chinese")); - order.Add(new OrderDetails(10255, "RICSU", "Genève", "Richter Supermarkt")); - order.Add(new OrderDetails(10256, "WELLI", "Resende", "Wellington Importadora")); - order.Add(new OrderDetails(10257, "HILAA", "San Cristóbal", "HILARION-Abastos")); - order.Add(new OrderDetails(10258, "ERNSH", "Graz", "Ernst Handel")); - order.Add(new OrderDetails(10259, "CENTC", "México D.F.", "Centro comercial Moctezuma")); - order.Add(new OrderDetails(10260, "OTTIK", "Köln", "Ottilies Käseladen")); - order.Add(new OrderDetails(10261, "QUEDE", "Rio de Janeiro", "Que Delícia")); - order.Add(new OrderDetails(10262, "RATTC", "Albuquerque", "Rattlesnake Canyon Grocery")); - } - return order; + new OrderDetails(10248, "VINET", "Reims", "Vins et alcools Chevalier"), + new OrderDetails(10249, "TOMSP", "Münster", "Toms Spezialitäten"), + new OrderDetails(10250, "HANAR", "Rio de Janeiro", "Hanari Carnes"), + new OrderDetails(10251, "VICTE", "Lyon", "Victuailles en stock"), + new OrderDetails(10252, "SUPRD", "Charleroi", "Suprêmes délices"), + new OrderDetails(10253, "HANAR", "Rio de Janeiro", "Hanari Carnes"), + new OrderDetails(10254, "CHOPS", "Bern", "Chop-suey Chinese"), + new OrderDetails(10255, "RICSU", "Genève", "Richter Supermarkt"), + new OrderDetails(10256, "WELLI", "Resende", "Wellington Importadora"), + new OrderDetails(10257, "HILAA", "San Cristóbal", "HILARION-Abastos"), + new OrderDetails(10258, "ERNSH", "Graz", "Ernst Handel"), + new OrderDetails(10259, "CENTC", "México D.F.", "Centro comercial Moctezuma"), + new OrderDetails(10260, "OTTIK", "Köln", "Ottilies Käseladen"), + new OrderDetails(10261, "QUEDE", "Rio de Janeiro", "Que Delícia"), + new OrderDetails(10262, "RATTC", "Albuquerque", "Rattlesnake Canyon Grocery") + }; } - public int OrderID { get; set; } - public string CustomerID { get; set; } - public string ShipCity { get; set; } - public string ShipName { get; set; } -} {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/BthSXpBAUaAyeguK?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} +{% previewsample "https://blazorplayground.syncfusion.com/embed/LjBeCstRzwuSLsxV?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ## List binding -The Syncfusion® Blazor DataGrid supports binding data from a list of objects (e.g., a List<T> or IEnumerable<T> collection). This is the most common approach when working with in-memory data in Blazor applications. +The Syncfusion® Blazor DataGrid supports binding data from a list of objects such as List<T> or IEnumerable<T>. This is a common approach when working with in-memory data in Blazor applications. -List binding allows the Grid to render and manage a collection of data directly in memory without requiring a remote service or external data manager unless needed. This is ideal for local CRUD operations, small datasets, or preloaded data. +List binding enables the Grid to render and manage a collection of data directly in memory. This is suitable for scenarios involving small datasets, preloaded data, or local CRUD operations. -**List binding can be enabled in the following scenarios:** +**To Bind a List to the Grid** -You can bind a list of data to the Syncfusion® Blazor DataGrid using the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property. The list can be: - -* A basic in-memory IEnumerable<T> (e.g., List<Order>). - -* Provided via an [SfDataManager](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Data.SfDataManager.html) for additional data operations or offline capabilities. +- Create a model class with required properties. +- Populate a list of model objects. +- Assign the list to the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property of the Grid. {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -111,16 +103,17 @@ You can bind a list of data to the Syncfusion® - - - - - + + + + + @code { - private List OrderData; + private List OrderData = new(); + protected override void OnInitialized() { OrderData = OrderDetails.GetAllRecords(); @@ -133,105 +126,108 @@ You can bind a list of data to the Syncfusion® orders = new List(); - public OrderDetails() { } + public int OrderID { get; set; } + public string CustomerID { get; set; } + public DateOnly OrderDate { get; set; } + public TimeOnly OrderTime { get; set; } + public double Freight { get; set; } + public OrderDetails(int orderID, string customerID, DateTime orderDateTime, double freight) { - this.OrderID = orderID; - this.CustomerID = customerID; - this.OrderDate = DateOnly.FromDateTime(orderDateTime); - this.OrderTime = TimeOnly.FromDateTime(orderDateTime); - this.Freight = freight; + OrderID = orderID; + CustomerID = customerID; + OrderDate = DateOnly.FromDateTime(orderDateTime); + OrderTime = TimeOnly.FromDateTime(orderDateTime); + Freight = freight; } + public static List GetAllRecords() { - if (orders.Count == 0) + return new List { - orders.Add(new OrderDetails(10248, "VINET", new DateTime(1996, 7, 4, 9, 30, 0), 32.38)); - orders.Add(new OrderDetails(10249, "TOMSP", new DateTime(1996, 7, 5, 11, 45, 0), 11.61)); - orders.Add(new OrderDetails(10250, "HANAR", new DateTime(1996, 7, 8, 14, 15, 0), 65.83)); - orders.Add(new OrderDetails(10251, "VICTE", new DateTime(1996, 7, 8, 16, 0, 0), 41.34)); - orders.Add(new OrderDetails(10252, "SUPRD", new DateTime(1996, 7, 9, 10, 20, 0), 51.3)); - orders.Add(new OrderDetails(10253, "HANAR", new DateTime(1996, 7, 10, 13, 5, 0), 58.17)); - orders.Add(new OrderDetails(10254, "CHOPS", new DateTime(1996, 7, 11, 17, 45, 0), 22.98)); - orders.Add(new OrderDetails(10255, "RICSU", new DateTime(1996, 7, 12, 8, 50, 0), 148.33)); - orders.Add(new OrderDetails(10256, "WELLI", new DateTime(1996, 7, 15, 12, 10, 0), 13.97)); - orders.Add(new OrderDetails(10257, "HILAA", new DateTime(1996, 7, 16, 15, 30, 0), 81.91)); - orders.Add(new OrderDetails(10258, "ERNSH", new DateTime(1996, 7, 17, 10, 45, 0), 140.51)); - orders.Add(new OrderDetails(10259, "CENTC", new DateTime(1996, 7, 18, 9, 0, 0), 3.25)); - orders.Add(new OrderDetails(10260, "OTTIK", new DateTime(1996, 7, 19, 16, 20, 0), 55.09)); - orders.Add(new OrderDetails(10261, "QUEDE", new DateTime(1996, 7, 19, 13, 25, 0), 3.05)); - orders.Add(new OrderDetails(10262, "RATTC", new DateTime(1996, 7, 22, 11, 40, 0), 48.29)); - } - return orders; + new OrderDetails(10248, "VINET", new DateTime(1996, 7, 4, 9, 30, 0), 32.38), + new OrderDetails(10249, "TOMSP", new DateTime(1996, 7, 5, 11, 45, 0), 11.61), + new OrderDetails(10250, "HANAR", new DateTime(1996, 7, 8, 14, 15, 0), 65.83), + new OrderDetails(10251, "VICTE", new DateTime(1996, 7, 8, 16, 0, 0), 41.34), + new OrderDetails(10252, "SUPRD", new DateTime(1996, 7, 9, 10, 20, 0), 51.30), + new OrderDetails(10253, "HANAR", new DateTime(1996, 7, 10, 13, 5, 0), 58.17), + new OrderDetails(10254, "CHOPS", new DateTime(1996, 7, 11, 17, 45, 0), 22.98), + new OrderDetails(10255, "RICSU", new DateTime(1996, 7, 12, 8, 50, 0), 148.33), + new OrderDetails(10256, "WELLI", new DateTime(1996, 7, 15, 12, 10, 0), 13.97), + new OrderDetails(10257, "HILAA", new DateTime(1996, 7, 16, 15, 30, 0), 81.91), + new OrderDetails(10258, "ERNSH", new DateTime(1996, 7, 17, 10, 45, 0), 140.51), + new OrderDetails(10259, "CENTC", new DateTime(1996, 7, 18, 9, 0, 0), 3.25), + new OrderDetails(10260, "OTTIK", new DateTime(1996, 7, 19, 16, 20, 0), 55.09), + new OrderDetails(10261, "QUEDE", new DateTime(1996, 7, 19, 13, 25, 0), 3.05), + new OrderDetails(10262, "RATTC", new DateTime(1996, 7, 22, 11, 40, 0), 48.29) + }; } - public int OrderID { get; set; } - public string CustomerID { get; set; } - public DateOnly OrderDate { get; set; } - public TimeOnly OrderTime { get; set; } - public double Freight { get; set; } } {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/BtreNzrLVoDAAALh?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} - -> By default, the [SfDataManager](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Data.SfDataManager.html) uses the **BlazorAdaptor** for list data binding. +{% previewsample "https://blazorplayground.syncfusion.com/embed/BZLyCCtdzbJPootL?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ### ExpandoObject binding -The Syncfusion® Blazor DataGrid is a strongly-typed generic component typically bound to a specific model at compile time. However, there are scenarios, especially in dynamic or metadata-driven applications, where the structure of the data is not known until runtime. In such cases, you can bind the Grid to a collection of [ExpandoObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-9.0) instances for a fully dynamic Grid structure. - -**ExpandoObject can be used in the following scenarios:** +The Syncfusion® Blazor DataGrid is a strongly-typed generic component typically bound to a predefined model. However, in scenarios where the data structure is determined at runtime—such as metadata-driven applications or dynamic user input—the Grid can be bound to a collection of [ExpandoObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-9.0) instances. -* For dynamic or runtime-generated data models. +This approach enables dynamic rendering of both data and columns without requiring compile-time type definitions. -* For dynamically constructing Grid columns and data (e.g., based on user input or metadata). +**When to Use ExpandoObject:** -* For integrating with systems where the data structure cannot be statically defined. +- For runtime-generated or dynamic data models. +- For constructing Grid columns and data based on metadata or user input. +- For integration with systems where the data schema is not statically defined. -For a visual demonstration of how to bind **ExpandoObject** in the Grid, watch this video: +**ExpandoObject binding** supports core Grid features such as **paging**, **sorting**, **filtering**, and **editing**. {% youtube "youtube:https://www.youtube.com/watch?v=Xhaw3DdHmJk"%} -You can assign a list of **ExpandoObject** to the Grid’s [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property. Grid features like **paging**, **sorting**, **filtering**, and even **editing** are supported when using **ExpandoObject**. - {% tabs %} {% highlight razor tabtitle="Index.razor" %} @using Syncfusion.Blazor.Grids @using System.Dynamic - + - + @code { - public List Orders { get; set; } = new List(); - private List ToolbarItems = new List() { "Add", "Edit", "Delete", "Update", "Cancel" }; + private List orders = new(); + private readonly List toolbarItems = new() { "Add", "Edit", "Delete", "Update", "Cancel" }; + protected override void OnInitialized() { - Orders = Enumerable.Range(1, 75).Select((x) => + orders = Enumerable.Range(1, 75).Select(index => { - dynamic Order = new ExpandoObject(); - Order.OrderID = 1000 + x; - Order.CustomerID = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)]; - Order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x; - Order.OrderDate = (new DateTime[] { new DateTime(1996, 11, 5), new DateTime(1996, 10, 3), new DateTime(1996, 9, 9), new DateTime(1996, 8, 2), new DateTime(1996, 4, 11) })[new Random().Next(5)]; - Order.ShipCountry = (new string[] { "USA", "UK" })[new Random().Next(2)]; - Order.Verified = (new bool[] { true, false })[new Random().Next(2)]; - return Order; - }).Cast().ToList(); + dynamic order = new ExpandoObject(); + order.OrderID = 1000 + index; + order.CustomerID = new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" }[Random.Shared.Next(5)]; + order.Freight = new[] { 2.0, 1.0, 4.0, 5.0, 3.0 }[Random.Shared.Next(5)] * index; + order.OrderDate = new[] + { + new DateTime(1996, 11, 5), + new DateTime(1996, 10, 3), + new DateTime(1996, 9, 9), + new DateTime(1996, 8, 2), + new DateTime(1996, 4, 11) + }[Random.Shared.Next(5)]; + order.ShipCountry = new[] { "USA", "UK" }[Random.Shared.Next(2)]; + order.Verified = new[] { true, false }[Random.Shared.Next(2)]; + return order; + }).Cast().ToList(); } } @@ -242,9 +238,9 @@ Please find the sample in this [GitHub location](https://github.com/SyncfusionEx ### ExpandoObject complex data binding -When working with complex or nested data structures using [ExpandoObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-9.0), the Syncfusion® Blazor DataGrid allows you to bind these nested fields using dot (.) notation. This is especially helpful when your **ExpandoObject** contains sub-objects or hierarchical data, and you want to present specific properties of those nested objects in individual Grid columns. +When working with complex or nested data structures using [ExpandoObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=net-9.0), the Syncfusion® Blazor DataGrid allows binding of nested fields using dot (.) notation. This is helpful when **ExpandoObject** contains sub-objects or hierarchical data, and specific properties of those nested objects need display in individual Grid columns. -The following example demonstrates how to bind complex properties within an **ExpandoObject** to the Grid. In this sample, **CustomerID.Name** and **ShipCountry.Country** represent nested fields from the underlying dynamic object, and they are individually bound to display in their respective columns: +The following example demonstrates binding of complex properties within an **ExpandoObject** to the Grid. In this sample, **CustomerID.Name** and **ShipCountry.Country** represent nested fields from the underlying dynamic object, and they are individually bound to display in their respective columns: {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -252,64 +248,73 @@ The following example demonstrates how to bind complex properties within an **Ex @using Syncfusion.Blazor.Grids @using System.Dynamic - + - - + + @code { - public List Orders { get; set; } = new List(); - private List ToolbarItems = new List() { "Add", "Edit", "Delete", "Update", "Cancel" }; + + private List orders = new(); + private readonly List toolbarItems = new() { "Add", "Edit", "Delete", "Update", "Cancel" }; + protected override void OnInitialized() { - Orders = Enumerable.Range(1, 75).Select((x) => + orders = Enumerable.Range(1, 75).Select(Index => { dynamic Order = new ExpandoObject(); - dynamic customerName = new ExpandoObject(); - dynamic countryName = new ExpandoObject(); - Order.OrderID = 1000 + x; - customerName.Name = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)]; - Order.CustomerID = customerName; - Order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x; - Order.OrderDate = (new DateTime[] { new DateTime(1996, 11, 5), new DateTime(1996, 10, 3), new DateTime(1996, 9, 9), new DateTime(1996, 8, 2), new DateTime(1996, 4, 11) })[new Random().Next(5)]; - countryName.Country = (new string[] { "USA", "UK" })[new Random().Next(2)]; - Order.ShipCountry = countryName; - Order.Verified = (new bool[] { true, false })[new Random().Next(2)]; + dynamic CustomerName = new ExpandoObject(); + dynamic CountryName = new ExpandoObject(); + + Order.OrderID = 1000 + Index; + CustomerName.Name = new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" }[Random.Shared.Next(5)]; + Order.CustomerID = CustomerName; + Order.Freight = new[] { 2.0, 1.0, 4.0, 5.0, 3.0 }[Random.Shared.Next(5)] * Index; + Order.OrderDate = new[] + { + new DateTime(1996, 11, 5), + new DateTime(1996, 10, 3), + new DateTime(1996, 9, 9), + new DateTime(1996, 8, 2), + new DateTime(1996, 4, 11) + }[Random.Shared.Next(5)]; + CountryName.Country = new[] { "USA", "UK" }[Random.Shared.Next(2)]; + Order.ShipCountry = CountryName; + Order.Verified = new[] { true, false }[Random.Shared.Next(2)]; return Order; - }).Cast().ToList(); + }).Cast().ToList(); } } - {% endhighlight %} {% endtabs %} -> You can perform data operations and CRUD operations for complex ExpandoObject binding fields as well. +> Perform data operations and CRUD operations for complex ExpandoObject binding fields as well. The following image represents ExpandoObject complex data binding, -![Binding ExpandObject with complex data in Blazor DataGrid](./images/blazor-datagrid-expand-complex-data.png) +![Binding ExpandoObject with complex data in Blazor DataGrid](./images/blazor-datagrid-expand-complex-data.png) Please find the sample in this [GitHub location](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/blob/master/ListBinding/ListBinding/Components/Pages/ExpandoObjectComplexBinding.razor). ### DynamicObject binding -The Syncfusion® Blazor DataGrid is designed to work with strongly-typed models. However, in scenarios where the model structure is not known at compile time, such as metadata-driven Grids or dynamic data sources, you can bind the Grid to a list of objects derived from [DynamicObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject). +The Syncfusion® Blazor DataGrid is designed to work with strongly-typed models. However, in scenarios where the model structure is known only at compile time, such as metadata-driven Grids or dynamic data sources, bind the Grid to a list of objects derived from [DynamicObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject). -**DynamicObject can be implemented in this below scenarios:** +**Implement DynamicObject in scenarios such as:** -* In cases where data models are unknown at compile time. +* Cases where data models are known only at compile time. -* For creating dynamic, metadata-driven Grid layouts. +* Creation of dynamic, metadata-driven Grid layouts. -* During integration with external or runtime-generated data sources with unpredictable structures. +* Integration with external or runtime-generated data sources with unpredictable structures. For a visual demonstration of how to bind a **DynamicObject** in the Grid, watch this video: @@ -318,7 +323,7 @@ For a visual demonstration of how to bind a **DynamicObject** in the Grid, watch To bind a **DynamicObject**, assign a list of dynamic instances to the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_DataSource) property. -> You must override the [GetDynamicMemberNames](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject.getdynamicmembernames?view=net-9.0) method in your **DynamicObject** implementation. This allows the Grid to detect the property names during rendering and perform **editing**, **sorting**, **filtering**, and **paging** operations. +> Override the [GetDynamicMemberNames](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject.getdynamicmembernames?view=net-9.0) method in the **DynamicObject** implementation. This allows the Grid to detect property names during rendering and perform **editing**, **sorting**, **filtering**, and **paging** operations. {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -326,7 +331,7 @@ To bind a **DynamicObject**, assign a list of dynamic instances to the [DataSour @using Syncfusion.Blazor.Grids @using System.Dynamic - + @@ -337,36 +342,47 @@ To bind a **DynamicObject**, assign a list of dynamic instances to the [DataSour @code { - private List ToolbarItems = new List(){ "Add", "Edit", "Delete", "Update", "Cancel"}; - public List Orders = new List() { }; + private List toolbarItems = new List(){ "Add", "Edit", "Delete", "Update", "Cancel"}; + private List orders = new(); + protected override void OnInitialized() { - Orders = Enumerable.Range(1, 15).Select((x) => + orders = Enumerable.Range(1, 15).Select(Index => { dynamic Order = new DynamicDictionary(); - Order.OrderID = 1000 + x; - Order.CustomerID = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)]; - Order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x; - Order.OrderDate = (new DateTime[] { new DateTime(1996, 11, 5), new DateTime(1996, 10, 3), new DateTime(1996, 9, 9), new DateTime(1996, 8, 2), new DateTime(1996, 4, 11) })[new Random().Next(5)]; + Order.OrderID = 1000 + Index; + Order.CustomerID = new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" }[Random.Shared.Next(5)]; + Order.Freight = new[] { 2.0, 1.0, 4.0, 5.0, 3.0 }[Random.Shared.Next(5)] * Index; + Order.OrderDate = new[] + { + new DateTime(1996, 11, 5), + new DateTime(1996, 10, 3), + new DateTime(1996, 9, 9), + new DateTime(1996, 8, 2), + new DateTime(1996, 4, 11) + }[Random.Shared.Next(5)]; return Order; - }).Cast().ToList(); + }).Cast().ToList(); } + public class DynamicDictionary : DynamicObject { - Dictionary dictionary = new Dictionary(); - public override bool TryGetMember(GetMemberBinder binder, out object result) + private readonly Dictionary Dictionary = new(); + + public override bool TryGetMember(GetMemberBinder Binder, out object Result) { - string name = binder.Name; - return dictionary.TryGetValue(name, out result); + return Dictionary.TryGetValue(Binder.Name, out Result); } - public override bool TrySetMember(SetMemberBinder binder, object value) + + public override bool TrySetMember(SetMemberBinder Binder, object Value) { - dictionary[binder.Name] = value; + Dictionary[Binder.Name] = Value; return true; } - public override System.Collections.Generic.IEnumerable GetDynamicMemberNames() + + public override IEnumerable GetDynamicMemberNames() { - return this.dictionary?.Keys; + return Dictionary.Keys; } } } @@ -378,9 +394,9 @@ Please find the sample in this [GitHub location](https://github.com/SyncfusionEx ### DynamicObject complex data binding -When working with complex or nested data structures using [DynamicObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject), the Syncfusion® Blazor DataGrid allows you to bind these nested fields using dot (.) notation. This is especially helpful when your **DynamicObject** contains sub-objects or hierarchical data, and you want to present specific properties of those nested objects in individual Grid columns. +When working with complex or nested data structures using [DynamicObject](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject), the Syncfusion® Blazor DataGrid allows binding of nested fields using dot (.) notation. This is helpful when **DynamicObject** contains sub-objects or hierarchical data, and specific properties of those nested objects need display in individual Grid columns. -The following example demonstrates how to bind complex properties within a **DynamicObject** to the Grid. In this sample, **CustomerID.Name** and **ShipCountry.Country** represent nested fields from the underlying dynamic object, and they are individually bound to display in their respective columns: +The following example demonstrates binding of complex properties within a **DynamicObject** to the Grid. In this sample, **CustomerID.Name** and **ShipCountry.Country** represent nested fields from the underlying dynamic object, and they are individually bound to display in their respective columns: {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -388,7 +404,7 @@ The following example demonstrates how to bind complex properties within a **Dyn @using Syncfusion.Blazor.Grids @using System.Dynamic - + @@ -400,41 +416,54 @@ The following example demonstrates how to bind complex properties within a **Dyn @code { - private List ToolbarItems = new List() { "Add", "Edit", "Delete", "Update", "Cancel" }; - public List Orders = new List() { }; + private readonly List toolbarItems = new() { "Add", "Edit", "Delete", "Update", "Cancel" }; + private List orders = new(); + protected override void OnInitialized() { - Orders = Enumerable.Range(1, 15).Select((x) => + orders = Enumerable.Range(1, 15).Select(Index => { dynamic Order = new DynamicDictionary(); - dynamic customerName = new DynamicDictionary(); - dynamic countryName = new DynamicDictionary(); - Order.OrderID = 1000 + x; - customerName.Name = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)]; - Order.CustomerID = customerName; - Order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x; - Order.OrderDate = (new DateTime[] { new DateTime(1996, 11, 5), new DateTime(1996, 10, 3), new DateTime(1996, 9, 9), new DateTime(1996, 8, 2), new DateTime(1996, 4, 11) })[new Random().Next(5)]; - countryName.Country = (new string[] { "USA", "UK" })[new Random().Next(2)]; - Order.ShipCountry = countryName; + dynamic CustomerName = new DynamicDictionary(); + dynamic CountryName = new DynamicDictionary(); + + Order.OrderID = 1000 + Index; + CustomerName.Name = new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" }[Random.Shared.Next(5)]; + Order.CustomerID = CustomerName; + Order.Freight = new[] { 2.0, 1.0, 4.0, 5.0, 3.0 }[Random.Shared.Next(5)] * Index; + Order.OrderDate = new[] + { + new DateTime(1996, 11, 5), + new DateTime(1996, 10, 3), + new DateTime(1996, 9, 9), + new DateTime(1996, 8, 2), + new DateTime(1996, 4, 11) + }[Random.Shared.Next(5)]; + CountryName.Country = new[] { "USA", "UK" }[Random.Shared.Next(2)]; + Order.ShipCountry = CountryName; + return Order; - }).Cast().ToList(); + }).Cast().ToList(); } - public class DynamicDictionary : DynamicObject + + private class DynamicDictionary : DynamicObject { - Dictionary dictionary = new Dictionary(); + private readonly Dictionary _dictionary = new(); + public override bool TryGetMember(GetMemberBinder binder, out object result) { - string name = binder.Name; - return dictionary.TryGetValue(name, out result); + return _dictionary.TryGetValue(binder.Name, out result); } + public override bool TrySetMember(SetMemberBinder binder, object value) { - dictionary[binder.Name] = value; + _dictionary[binder.Name] = value; return true; } - public override System.Collections.Generic.IEnumerable GetDynamicMemberNames() + + public override IEnumerable GetDynamicMemberNames() { - return this.dictionary?.Keys; + return _dictionary.Keys; } } } @@ -442,19 +471,19 @@ The following example demonstrates how to bind complex properties within a **Dyn {% endhighlight %} {% endtabs %} -> You can perform data operations and CRUD operations for complex DynamicObject binding fields as well. +> Perform data operations and CRUD operations for complex DynamicObject binding fields as well. The following image represents DynamicObject complex data binding -![Binding DynamicObject with Complex Data in Blazor DataGrid](./images/blazor-datagrid-dynamic-complex-data.png) +![Binding DynamicObject with complex data in Blazor DataGrid](./images/blazor-datagrid-dynamic-complex-data.png) Please find the sample in this [GitHub location](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/blob/master/ListBinding/ListBinding/Components/Pages/DynamicObjectComplexBinding.razor). -> When binding the Grid DataSource dynamically as a list of IEnumerable collections, you need to call the [Refresh](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_Refresh_System_Boolean_) method of the Grid to reflect the changes externally. This is because tracking changes made externally to IEnumerable items is avoided for performance considerations. +> When binding the Grid DataSource dynamically as a list of IEnumerable collections, call the [Refresh](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_Refresh_System_Boolean_) method of the Grid to reflect changes externally. This avoids tracking changes made externally to IEnumerable items for performance considerations. ### DataTable binding -The Syncfusion® Blazor DataGrid supports binding data from a `System.Data.DataTable` using a custom adaptor, enabling dynamic generation of rows and columns based on backend data. This approach is particularly useful for scenarios where data is retrieved or processed in a DataTable format, and it provides full support for built-in data operations like paging, filtering, sorting, and searching. +The Syncfusion® Blazor DataGrid supports binding data from a `System.Data.DataTable` using a custom adaptor, enabling dynamic generation of rows and columns based on backend data. This approach is useful for scenarios where data is retrieved or processed in a DataTable format, and it provides full support for built-in data operations like paging, filtering, sorting, and searching. To bind a `DataTable` to Grid, set `TValue` to **ExpandoObject**, convert it into an **IQueryable<ExpandoObject>** collection, and supply it through a custom adaptor that extends DataAdaptor. @@ -468,133 +497,129 @@ To bind a `DataTable` to Grid, set `TValue` to **ExpandoObject**, convert it int * Override the [Read](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataAdaptor.html#Syncfusion_Blazor_DataAdaptor_Read_Syncfusion_Blazor_DataManagerRequest_System_String_) method to handle data fetching and operations. -The following example demonstrates how to bind a `DataTable` with a **CustomAdaptor**. In the example below, the `DataTable` is passed to the `ToQueryableCollection` method, which converts the `DataTable` data source into an **IQueryable** collection data source. +The following example demonstrates binding a `DataTable` with a **CustomAdaptor**. In the example, the `DataTable` is passed to the `ToQueryableCollection` method, which converts the `DataTable` data source into an **IQueryable** collection data source. -You can perform data operations like **searching**, **sorting**, and **filtering** using the `PerformDataOperation` method. This method takes a `DataTable` and a [DataManagerRequest](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataManagerRequest.html) object as parameters, processes the data operations, and then returns an **IQueryable** data source. +Perform data operations like searching, sorting, and filtering using the `PerformDataOperation` method. This method takes a `DataTable` and a [DataManagerRequest](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataManagerRequest.html) object as parameters, processes the data operations, and returns an **IQueryable** data source. {% tabs %} {% highlight razor tabtitle="Index.razor" %} -@using Syncfusion.Blazor; +@using Syncfusion.Blazor @using Syncfusion.Blazor.Data @using Syncfusion.Blazor.Grids -@using System.Dynamic; -@using System.Data; +@using System.Dynamic +@using System.Data - - + + @code { - public static DataTable dataTable { get; set; } - public static IQueryable DataSource; + + private static DataTable? ordersTable; + private static IQueryable? dataSource; protected override void OnInitialized() { - dataTable = GetData(); - - // Convert the DataTable to an IQueryable collection. - DataSource = ToQueryableCollection(dataTable); + ordersTable = GetData(); + dataSource = ToQueryableCollection(ordersTable); } - // Custom adaptor class to handle data operations by extending the DataAdaptor class. - public class CustomAdaptor : DataAdaptor + private static DataTable GetData() { - // Perform the Read operation to fetch data from the source. - public override object Read(DataManagerRequest DataManagerRequest, string key = null) + var table = new DataTable(); + table.Columns.AddRange(new DataColumn[] + { + new DataColumn("OrderID", typeof(long)), + new DataColumn("CustomerID", typeof(string)), + new DataColumn("EmployeeID", typeof(int)), + new DataColumn("OrderDate", typeof(DateTime)) + }); + + int code = 1000; + int id = 0; + + for (int i = 1; i <= 15; i++) { - // Apply searching, sorting, and filtering. - DataSource = PerformDataOperation(dataTable, DataManagerRequest); + table.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15)); + table.Rows.Add(code + 2, "CHOPS", id + 2, new DateTime(1990, 04, 04)); + table.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30)); + table.Rows.Add(code + 4, "DRACH", id + 4, new DateTime(1930, 10, 22)); + table.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18)); + code += 5; + id += 5; + } - // Get the total record count. - int count = DataSource.Cast().Count(); + return table; + } - // Perform paging operation using skip and take. - if (DataManagerRequest.Skip != 0) - { - DataSource = QueryableOperation.PerformSkip((IQueryable)DataSource, DataManagerRequest.Skip); - } - if (DataManagerRequest.Take != 0) + private static IQueryable ToQueryableCollection(DataTable table) + { + var expandoList = new List(); + + foreach (DataRow row in table.Rows) + { + var expandoDict = new ExpandoObject() as IDictionary; + foreach (DataColumn col in table.Columns) { - DataSource = QueryableOperation.PerformTake((IQueryable)DataSource, DataManagerRequest.Take); + var colValue = row[col.ColumnName]; + colValue = (colValue == DBNull.Value) ? null : colValue; + expandoDict.Add(col.ColumnName, colValue); } - - // Return the result with count if required. - return DataManagerRequest.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource; + expandoList.Add((ExpandoObject)expandoDict); } - } - // Performs data operations like searching, sorting, and filtering. - public static IQueryable PerformDataOperation(DataTable DataTable, DataManagerRequest DataManagerRequest) + return expandoList.AsQueryable(); + } + + private static IQueryable PerformDataOperation(DataTable table, DataManagerRequest request) { - // Convert the DataTable to an IQueryable collection. - DataSource = ToQueryableCollection(DataTable); + dataSource = ToQueryableCollection(table); - if (DataManagerRequest.Search != null && DataManagerRequest.Search.Count > 0) + if (request.Search?.Count > 0) { - // Perform searching operation. - DataSource = DynamicObjectOperation.PerformSearching(DataSource, DataManagerRequest.Search); + dataSource = DynamicObjectOperation.PerformSearching(dataSource, request.Search); } - if (DataManagerRequest.Where != null && DataManagerRequest.Where.Count > 0) + if (request.Where?.Count > 0) { - // Perform filtering operation. - DataSource = DynamicObjectOperation.PerformFiltering(DataSource, DataManagerRequest.Where, DataManagerRequest.Where[0].Operator); + dataSource = DynamicObjectOperation.PerformFiltering(dataSource, request.Where, request.Where[0].Operator); } - if (DataManagerRequest.Sorted != null && DataManagerRequest.Sorted.Count > 0) + if (request.Sorted?.Count > 0) { - // Perform sorting operation. - DataSource = DynamicObjectOperation.PerformSorting(DataSource, DataManagerRequest.Sorted); + dataSource = DynamicObjectOperation.PerformSorting(dataSource, request.Sorted); } - return DataSource; + + return dataSource!; } - // Converts a DataTable to an IQueryable collection of ExpandoObjects. - public static IQueryable ToQueryableCollection(DataTable DataTable) + private class CustomAdaptor : DataAdaptor { - List expandoList = new List(); - foreach (DataRow row in DataTable.Rows) + public override object Read(DataManagerRequest request, string? key = null) { - var expandoDict = new ExpandoObject() as IDictionary; - foreach (DataColumn col in DataTable.Columns) + dataSource = PerformDataOperation(ordersTable!, request); + + int count = dataSource.Cast().Count(); + + if (request.Skip != 0) { - var colValue = row[col.ColumnName]; - colValue = (colValue == DBNull.Value) ? null : colValue; - expandoDict.Add(col.ToString(), colValue); + dataSource = QueryableOperation.PerformSkip((IQueryable)dataSource, request.Skip); + } + if (request.Take != 0) + { + dataSource = QueryableOperation.PerformTake((IQueryable)dataSource, request.Take); } - expandoList.Add((ExpandoObject)expandoDict); - } - return expandoList.AsQueryable(); - } - public DataTable GetData() - { - DataTable DataTable = new DataTable(); - DataTable.Columns.AddRange(new DataColumn[4] { - new DataColumn("OrderID", typeof(long)), - new DataColumn("CustomerID", typeof(string)), - new DataColumn("EmployeeID",typeof(int)), - new DataColumn("OrderDate",typeof(DateTime)) - }); - int code = 1000; - int id = 0; - for (int i = 1; i <= 15; i++) - { - DataTable.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15)); - DataTable.Rows.Add(code + 2, "CHOPS", id + 2, new DateTime(1990, 04, 04)); - DataTable.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30)); - DataTable.Rows.Add(code + 4, "DRACH", id + 4, new DateTime(1930, 10, 22)); - DataTable.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18)); - code += 5; - id += 5; + return request.RequiresCounts + ? new DataResult() { Result = dataSource, Count = count } + : (object)dataSource; } - return DataTable; } } @@ -603,15 +628,15 @@ You can perform data operations like **searching**, **sorting**, and **filtering **Grouping and Aggregates with DataTable:** -The Syncfusion® Blazor DataGrid supports dynamic **grouping** and **aggregates** even when bound to a `DataTable` via a custom adaptor. This allows you to group rows by one or more columns and apply aggregate functions (such as **Sum**, **Average**, **Count**, etc.) on those groups or entire datasets. +The Syncfusion® Blazor DataGrid supports dynamic **grouping** and **aggregates** even when bound to a `DataTable` via a custom adaptor. This allows grouping of rows by one or more columns and application of aggregate functions (such as **Sum**, **Average**, **Count**, etc.) on those groups or entire datasets. -Below is an example showing how to implement grouping and aggregates in a custom adaptor: +Below is an example showing implementation of grouping and aggregates in a custom adaptor: {% tabs %} {% highlight razor tabtitle="Index.razor" %} -@using Syncfusion.Blazor.Grids @using Syncfusion.Blazor +@using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Data @using System.Data @using System.Dynamic @@ -624,129 +649,126 @@ Below is an example showing how to implement grouping and aggregates in a custom - + @{ var aggregate = (context as AggregateTemplateContext); -
-

Sum: @aggregate.Sum

-
} +
+

Sum: @aggregate.Sum

+
- - - - + + + + @code { - public static DataTable dataTable { get; set; } - public static IQueryable DataSource; + private static DataTable? ordersTable; + private static IQueryable? dataSource; protected override void OnInitialized() { - dataTable = GetData(); - - // Convert the DataTable to an IQueryable collection. - DataSource = ToQueryableCollection(dataTable); + ordersTable = GetData(); + dataSource = ToQueryableCollection(ordersTable); } - // Custom adaptor class to handle data operations by extending the DataAdaptor class. - public class CustomAdaptor : DataAdaptor + private static DataTable GetData() { - // Perform the Read operation to fetch data from the source. - public override object Read(DataManagerRequest DataManagerRequest, string key = null) + var table = new DataTable(); + table.Columns.AddRange(new DataColumn[] { - // Convert DataTable to IQueryable ExpandoObject list - DataSource = ToQueryableCollection(dataTable); - - // Get the total record count. - int count = DataSource.Cast().Count(); - - // Perform paging operation using skip and take. - if (DataManagerRequest.Skip != 0) - { - DataSource = QueryableOperation.PerformSkip((IQueryable)DataSource, DataManagerRequest.Skip); - } - if (DataManagerRequest.Take != 0) - { - DataSource = QueryableOperation.PerformTake((IQueryable)DataSource, DataManagerRequest.Take); - } - - // Perform aggregation operation. - IDictionary aggregates = new Dictionary(); - if (DataManagerRequest.Aggregates != null) - { - aggregates = DataUtil.PerformAggregation(DataSource, DataManagerRequest.Aggregates); - } + new DataColumn("OrderID", typeof(long)), + new DataColumn("CustomerID", typeof(string)), + new DataColumn("EmployeeID", typeof(int)), + new DataColumn("OrderDate", typeof(DateTime)), + new DataColumn("Freight", typeof(double)) + }); - // Perform grouping operation. - DataResult DataObject = new DataResult(); - if (DataManagerRequest.Group != null) - { - IEnumerable result = (IEnumerable)DataSource; - foreach (var group in DataManagerRequest.Group) - { - result = DataUtil.Group(result, group, DataManagerRequest.Aggregates, 0, DataManagerRequest.GroupByFormatter); - } + int code = 1000; + int id = 0; - // Return grouped data with count and aggregates if required. - return DataManagerRequest.RequiresCounts ? new DataResult() { Result = result, Count = count, Aggregates = aggregates } : (object)DataSource; - } - - // Return the final result with count and aggregates if required. - return DataManagerRequest.RequiresCounts ? new DataResult() { Result = DataSource, Count = count, Aggregates = aggregates } : (object)DataSource; + for (int i = 1; i <= 15; i++) + { + table.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15), 2.32 * i); + table.Rows.Add(code + 2, "CHOPS", id + 2, new DateTime(1990, 04, 04), 1.28 * i); + table.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30), 4.31 * i); + table.Rows.Add(code + 4, "DRACH", id + 4, new DateTime(1930, 10, 22), 2.56 * i); + table.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18), 5.54 * i); + code += 5; + id += 5; } + + return table; } - // Converts a DataTable to an IQueryable collection of ExpandoObjects. - public static IQueryable ToQueryableCollection(DataTable DataTable) + private static IQueryable ToQueryableCollection(DataTable table) { - List expandoList = new List(); - foreach (DataRow row in DataTable.Rows) + var expandoList = new List(); + + foreach (DataRow row in table.Rows) { - var expandoDict = new ExpandoObject() as IDictionary; - foreach (DataColumn col in DataTable.Columns) + var expandoDict = new ExpandoObject() as IDictionary; + foreach (DataColumn col in table.Columns) { var colValue = row[col.ColumnName]; colValue = (colValue == DBNull.Value) ? null : colValue; - expandoDict.Add(col.ToString(), colValue); + expandoDict.Add(col.ColumnName, colValue); } expandoList.Add((ExpandoObject)expandoDict); } + return expandoList.AsQueryable(); } - public DataTable GetData() + private class CustomAdaptor : DataAdaptor { - DataTable DataTable = new DataTable(); - DataTable.Columns.AddRange(new DataColumn[5] { - new DataColumn("OrderID", typeof(long)), - new DataColumn("CustomerID", typeof(string)), - new DataColumn("EmployeeID",typeof(int)), - new DataColumn("OrderDate",typeof(DateTime)), - new DataColumn("Freight", typeof(double)) - }); - int code = 1000; - int id = 0; - for (int i = 1; i <= 15; i++) + public override object Read(DataManagerRequest request, string? key = null) { - DataTable.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15), 2.32 * i); - DataTable.Rows.Add(code + 2, "CHOPS", id + 2, new DateTime(1990, 04, 04), 1.28 * i); - DataTable.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30), 4.31 * i ); - DataTable.Rows.Add(code + 4, "DRACH", id + 4, new DateTime(1930, 10, 22), 2.56 * i); - DataTable.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18), 5.54 * i); - code += 5; - id += 5; + dataSource = ToQueryableCollection(ordersTable!); + + int count = dataSource.Cast().Count(); + + if (request.Skip != 0) + { + dataSource = QueryableOperation.PerformSkip((IQueryable)dataSource, request.Skip); + } + if (request.Take != 0) + { + dataSource = QueryableOperation.PerformTake((IQueryable)dataSource, request.Take); + } + + IDictionary aggregates = new Dictionary(); + if (request.Aggregates != null) + { + aggregates = DataUtil.PerformAggregation(dataSource, request.Aggregates); + } + + if (request.Group != null) + { + IEnumerable result = (IEnumerable)dataSource; + foreach (var group in request.Group) + { + result = DataUtil.Group(result, group, request.Aggregates, 0, request.GroupByFormatter); + } + + return request.RequiresCounts + ? new DataResult() { Result = result, Count = count, Aggregates = aggregates } + : (object)dataSource; + } + + return request.RequiresCounts + ? new DataResult() { Result = dataSource, Count = count, Aggregates = aggregates } + : (object)dataSource; } - return DataTable; } } @@ -755,7 +777,7 @@ Below is an example showing how to implement grouping and aggregates in a custom **DataTable with CRUD operations** -The Syncfusion® Blazor DataGrid supports CRUD (Create, Read, Update, and Delete) operations with a DataTable using a custom adaptor. You can enable editing in the Grid and override specific methods of the [DataAdaptor](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataAdaptor.html) base class to update your `DataTable` in memory. +The Syncfusion® Blazor DataGrid supports CRUD (Create, Read, Update, and Delete) operations with a DataTable using a custom adaptor. Enable editing in the Grid and override specific methods of the [DataAdaptor](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataAdaptor.html) base class to update `DataTable` in memory. **The supported methods are:** @@ -767,142 +789,170 @@ The Syncfusion® Blazor DataGrid supports CR * [BatchUpdate](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataAdaptor.html#Syncfusion_Blazor_DataAdaptor_BatchUpdate_Syncfusion_Blazor_DataManager_System_Object_System_Object_System_Object_System_String_System_String_System_Nullable_System_Int32__) / [BatchUpdateAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.DataAdaptor.html#Syncfusion_Blazor_DataAdaptor_BatchUpdateAsync_Syncfusion_Blazor_DataManager_System_Object_System_Object_System_Object_System_String_System_String_System_Nullable_System_Int32__) – Handles batch operations like add, update, and delete in a single transaction (used for Batch Editing). -When using batch editing in the Grid, use the `BatchUpdate`/`BatchUpdateAsync` method to handle the corresponding CRUD operations. +When using batch editing in the Grid, use the `BatchUpdate`/`BatchUpdateAsync` method to handle corresponding CRUD operations. {% tabs %} {% highlight razor tabtitle="Index.razor" %} -@using Syncfusion.Blazor; +@using Syncfusion.Blazor @using Syncfusion.Blazor.Data @using Syncfusion.Blazor.Grids -@using System.Dynamic; -@using System.Data; +@using System.Dynamic +@using System.Data - + - - - + + + @code { - public static DataTable dataTable { get; set; } - public static IQueryable DataSource; + private static DataTable? ordersTable; + private static IQueryable? dataSource; + protected override void OnInitialized() { - dataTable = GetData(); + ordersTable = GetData(); + dataSource = ToQueryableCollection(ordersTable); + } + + private static IQueryable ToQueryableCollection(DataTable table) + { + var expandoList = new List(); + + foreach (DataRow row in table.Rows) + { + var expandoDict = new ExpandoObject() as IDictionary; + foreach (DataColumn col in table.Columns) + { + var colValue = row[col.ColumnName]; + colValue = (colValue == DBNull.Value) ? null : colValue; + expandoDict.Add(col.ColumnName, colValue); + } + expandoList.Add((ExpandoObject)expandoDict); + } - // Convert the DataTable to an IQueryable collection. - DataSource = ToQueryableCollection(dataTable); + return expandoList.AsQueryable(); + } + + private static DataTable GetData() + { + var table = new DataTable(); + table.Columns.AddRange(new DataColumn[] + { + new DataColumn("OrderID", typeof(long)), + new DataColumn("CustomerID", typeof(string)), + new DataColumn("EmployeeID", typeof(int)), + new DataColumn("OrderDate", typeof(DateTime)) + }); + + int code = 1000; + int id = 0; + + for (int i = 1; i <= 15; i++) + { + table.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15)); + table.Rows.Add(code + 2, "ANATR", id + 2, new DateTime(1990, 04, 04)); + table.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30)); + table.Rows.Add(code + 4, "BLONP", id + 4, new DateTime(1930, 10, 22)); + table.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18)); + code += 5; + id += 5; + } + + return table; } - // Custom adaptor class to handle data operations by extending the DataAdaptor class. - public class CustomAdaptor : DataAdaptor + private class CustomAdaptor : DataAdaptor { - // Perform the Read operation to fetch data from the source. - public override object Read(DataManagerRequest DataManagerRequest, string key = null) + public override object Read(DataManagerRequest request, string? key = null) { - // Convert DataTable to IQueryable ExpandoObject list - DataSource = ToQueryableCollection(dataTable); + dataSource = ToQueryableCollection(ordersTable!); - // Get the total record count. - int count = DataSource.Cast().Count(); + int count = dataSource.Cast().Count(); - // Perform paging operation using skip and take. - if (DataManagerRequest.Skip != 0) + if (request.Skip != 0) { - DataSource = QueryableOperation.PerformSkip((IQueryable)DataSource, DataManagerRequest.Skip); + dataSource = QueryableOperation.PerformSkip((IQueryable)dataSource, request.Skip); } - if (DataManagerRequest.Take != 0) + if (request.Take != 0) { - DataSource = QueryableOperation.PerformTake((IQueryable)DataSource, DataManagerRequest.Take); + dataSource = QueryableOperation.PerformTake((IQueryable)dataSource, request.Take); } - - // Return the result with count if required. - return DataManagerRequest.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource; + + return request.RequiresCounts + ? new DataResult() { Result = dataSource, Count = count } + : (object)dataSource; } - // Perform insert operation. - public override object Insert(DataManager DataManagerRequest, object value, string key) + public override object Insert(DataManager request, object value, string key) { - DataRow newRow = dataTable.NewRow(); + var newRow = ordersTable!.NewRow(); var data = (ExpandoObject)value; + foreach (var item in data) { newRow[item.Key] = item.Value ?? DBNull.Value; } - - // Insert the new row at the top of the DataTable. - dataTable.Rows.InsertAt(newRow, 0); + ordersTable.Rows.InsertAt(newRow, 0); return value; } - // Perform remove operation. - public override object Remove(DataManager DataManagerRequest, object value, string keyField, string key) + public override object Remove(DataManager request, object value, string keyField, string key) { - DataRow? rowToRemove = null; + var rowToRemove = ordersTable!.Rows + .Cast() + .FirstOrDefault(row => row[keyField].Equals(value)); - // Find the row to remove based on the key field value. - foreach (DataRow row in dataTable.Rows) - { - if (row[keyField].Equals(value)) - { - rowToRemove = row; - break; - } - } - - // Remove the row from the DataTable if it exists. if (rowToRemove != null) { - dataTable.Rows.Remove(rowToRemove); + ordersTable.Rows.Remove(rowToRemove); } + return value; } - // Perform update operation. - public override object Update(DataManager DataManagerRequest, object value, string keyField, string key) + public override object Update(DataManager request, object value, string keyField, string key) { var data = (IDictionary)value; - // Find the row to update based on the key field value. - var rowToUpdate = dataTable.Rows - .Cast() - .FirstOrDefault(row => row[keyField].Equals(data[keyField])); + var rowToUpdate = ordersTable!.Rows + .Cast() + .FirstOrDefault(row => row[keyField].Equals(data[keyField])); - // Update the row with new values if found. if (rowToUpdate != null) { - foreach (DataColumn column in dataTable.Columns) + foreach (DataColumn column in ordersTable.Columns) { - var columnName = column.ColumnName; - var newValue = data[column.ColumnName] ?? column.DefaultValue; - rowToUpdate[columnName] = newValue; + rowToUpdate[column.ColumnName] = data[column.ColumnName] ?? column.DefaultValue; } } + return value; } - // Perform batch update operation for changed, added, and deleted records. - public override object BatchUpdate(DataManager DataManagerRequest, object Changed, object Added, object Deleted, string KeyField, string Key, int? dropIndex) + public override object BatchUpdate(DataManager request, object changed, object added, object deleted, string keyField, string key, int? dropIndex) { - // Handle changed records. - if (Changed != null) + if (changed != null) { - var changedRecords = (IEnumerable>)Changed; - foreach (var record in changedRecords) + foreach (var record in (IEnumerable>)changed) { - foreach (DataRow row in dataTable.Rows) + foreach (DataRow row in ordersTable!.Rows) { - if (row[KeyField].Equals(record[KeyField])) + if (row[keyField].Equals(record[keyField])) { - foreach (DataColumn column in dataTable.Columns) + foreach (DataColumn column in ordersTable.Columns) { row[column.ColumnName] = record[column.ColumnName] ?? column.DefaultValue; } @@ -911,91 +961,41 @@ When using batch editing in the Grid, use the `BatchUpdate`/`BatchUpdateAsync` m } } - // Handle added records. - if (Added != null) + if (added != null) { - var addedRecords = (IEnumerable>)Added; - - foreach (var record in addedRecords) + foreach (var record in (IEnumerable>)added) { - DataRow newRow = dataTable.NewRow(); + var newRow = ordersTable!.NewRow(); foreach (var item in record) { newRow[item.Key] = item.Value ?? DBNull.Value; } - - // Add the new row to the DataTable. - dataTable.Rows.Add(newRow); + ordersTable.Rows.Add(newRow); } } - // Handle deleted records. - if (Deleted != null) + if (deleted != null) { - var deletedRecords = (IEnumerable>)Deleted; - List rowsToRemove = new List(); - foreach (var record in deletedRecords) + var rowsToRemove = new List(); + foreach (var record in (IEnumerable>)deleted) { - foreach (DataRow row in dataTable.Rows) + foreach (DataRow row in ordersTable!.Rows) { - if (row[KeyField].Equals(record[KeyField])) + if (row[keyField].Equals(record[keyField])) { rowsToRemove.Add(row); } } } - // Remove the rows from the DataTable. - foreach (DataRow rowToRemove in rowsToRemove) + foreach (var row in rowsToRemove) { - dataTable.Rows.Remove(rowToRemove); + ordersTable.Rows.Remove(row); } } - return dataTable; - } - } - // Converts a DataTable to an IQueryable collection of ExpandoObjects. - public static IQueryable ToQueryableCollection(DataTable DataTable) - { - List expandoList = new List(); - foreach (DataRow row in DataTable.Rows) - { - var expandoDict = new ExpandoObject() as IDictionary; - foreach (DataColumn col in DataTable.Columns) - { - var colValue = row[col.ColumnName]; - colValue = (colValue == DBNull.Value) ? null : colValue; - expandoDict.Add(col.ToString(), colValue); - } - expandoList.Add((ExpandoObject)expandoDict); + return ordersTable!; } - return expandoList.AsQueryable(); - } - - public DataTable GetData() - { - DataTable DataTable = new DataTable(); - DataTable.Columns.AddRange(new DataColumn[4] { - new DataColumn("OrderID", typeof(long)), - new DataColumn("CustomerID", typeof(string)), - new DataColumn("EmployeeID",typeof(int)), - new DataColumn("OrderDate",typeof(DateTime)) - }); - - int code = 1000; - int id = 0; - for (int i = 1; i <= 15; i++) - { - DataTable.Rows.Add(code + 1, "ALFKI", id + 1, new DateTime(1991, 05, 15)); - DataTable.Rows.Add(code + 2, "ANATR", id + 2, new DateTime(1990, 04, 04)); - DataTable.Rows.Add(code + 3, "ANTON", id + 3, new DateTime(1957, 11, 30)); - DataTable.Rows.Add(code + 4, "BLONP", id + 4, new DateTime(1930, 10, 22)); - DataTable.Rows.Add(code + 5, "BOLID", id + 5, new DateTime(1953, 02, 18)); - code += 5; - id += 5; - } - return DataTable; } } @@ -1006,11 +1006,11 @@ Please find the sample in this [GitHub location](https://github.com/SyncfusionEx ## Managing spinner visibility during data loading -Showing a spinner during data loading in the Syncfusion® Blazor DataGrid enhances the experience by providing a visual indication of the loading progress. This feature helps to understand that data is being fetched or processed. +Showing a spinner during data loading in the Syncfusion® Blazor DataGrid enhances the UX by providing a visual indication of the loading progress. This feature helps to understand that data is being fetched or processed. -To show or hide a spinner during data loading in the Grid, you can utilize the [ShowSpinnerAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_ShowSpinnerAsync) and [HideSpinnerAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_HideSpinnerAsync) methods provided by the Grid. +To show or hide a spinner during data loading in the Grid, utilize the [ShowSpinnerAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_ShowSpinnerAsync) and [HideSpinnerAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Grids.SfGrid-1.html#Syncfusion_Blazor_Grids_SfGrid_1_HideSpinnerAsync) methods provided by the Grid. -The following example demonstrates how to show and hide the spinner during data loading using external buttons in a Grid: +The following example demonstrates showing and hiding the spinner during data loading using external buttons in a Grid: {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -1018,14 +1018,15 @@ The following example demonstrates how to show and hide the spinner during data @using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Buttons -
- - - +
+ + +
- + + - + @@ -1033,87 +1034,74 @@ The following example demonstrates how to show and hide the spinner during data @code { - private SfGrid Grid; - public List Orders { get; set; } + private SfGrid? grid; + private List orders { get; set; } = new List(); protected override void OnInitialized() { - Orders = OrderData.GetAllRecords(); + orders = OrderData.GetAllRecords(); } private async Task OnLoadData() { - await Grid.ShowSpinnerAsync(); - Grid.DataSource = Orders; - await Grid.HideSpinnerAsync(); + await grid!.ShowSpinnerAsync(); + grid.DataSource = orders; + await grid!.HideSpinnerAsync(); } - // Method to show/hide the spinner based on button click. private async Task ShowHideSpinner(string buttonId) { if (buttonId == "showButton") { - await Grid.ShowSpinnerAsync(); + await grid!.ShowSpinnerAsync(); } else if (buttonId == "hideButton") { - await Grid.HideSpinnerAsync(); + await grid!.HideSpinnerAsync(); } } } - {% endhighlight %} {% highlight c# tabtitle="OrderData.cs" %} public class OrderData { - public static List Orders = new List(); - - public OrderData(int orderID, string customerID, string productName, int quantity) - { - this.OrderID = orderID; - this.CustomerID = customerID; - this.ProductName = productName; - this.Quantity = quantity; - } + public int OrderID { get; set; } + public string? CustomerID { get; set; } + public string? ProductName { get; set; } + public int Quantity { get; set; } public static List GetAllRecords() { - if (Orders.Count == 0) - { - Random random = new Random(); - var customerIDs = new[] { "VINET", "TOMSP", "HANAR", "VICTE", "SUPRD", "CHOPS", "RICSU", "WELLI", "HILAA", "ERNSH", "CENTC", "OTTIK", "QUEDE", "RATTC" }; - var productNames = new[] { "Apple", "Orange", "Banana", "Grapes", "Pineapple", "Peach", "Mango", "Strawberry", "Blueberry", "Watermelon" }; + var random = new Random(); + var customerIds = new[] { "VINET", "TOMSP", "HANAR", "VICTE", "SUPRD", "CHOPS", "RICSU", "WELLI", "HILAA", "ERNSH", "CENTC", "OTTIK", "QUEDE", "RATTC" }; + var productNames = new[] { "Apple", "Orange", "Banana", "Grapes", "Pineapple", "Peach", "Mango", "Strawberry", "Blueberry", "Watermelon" }; - for (int i = 1; i <= 20000; i++) + var records = new List(); + for (int i = 1; i <= 100; i++) + { + records.Add(new OrderData { - var orderID = i; - var customerID = customerIDs[random.Next(customerIDs.Length)]; - var productName = productNames[random.Next(productNames.Length)]; - var quantity = random.Next(1, 100); // Random quantity between 1 and 100 - - Orders.Add(new OrderData(orderID, customerID, productName, quantity)); - } + OrderID = i, + CustomerID = customerIds[random.Next(customerIds.Length)], + ProductName = productNames[random.Next(productNames.Length)], + Quantity = random.Next(1, 100) + }); } - return Orders; + return records; } - - public int OrderID { get; set; } - public string CustomerID { get; set; } - public string ProductName { get; set; } - public int Quantity { get; set; } } {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/LZVSXfVYgqFBPutf?appbar=true&editor=true&result=true&errorlist=true&theme=bootstrap5" %} +{% previewsample "https://blazorplayground.syncfusion.com/embed/rjhesWXxzzwnSnMm?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} ## Change datasource dynamically -The Syncfusion® Blazor DataGrid allows to change the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Charts.ChartSeries.html#Syncfusion_Blazor_Charts_ChartSeries_DataSource) of the Grid dynamically through an external button. This feature is useful to display different sets of data based on specific actions. +The Syncfusion® Blazor DataGrid allows changing the [DataSource](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Charts.ChartSeries.html#Syncfusion_Blazor_Charts_ChartSeries_DataSource) of the Grid dynamically through an external button. This feature is useful to display different sets of data based on specific actions. To implement this: @@ -1125,37 +1113,37 @@ To implement this: * The Grid automatically detects the data change and re-renders with the new content. -The following example demonstrates how to change the `DataSource` of the Grid dynamically: +The following example demonstrates changing the `DataSource` of the Grid dynamically: {% tabs %} {% highlight razor tabtitle="Index.razor" %} @using Syncfusion.Blazor.Grids +@using Syncfusion.Blazor.Buttons -Change Data Source +Change Data Source - + - - - - - + + + + + @code { private SfGrid grid; - public List Orders { get; set; } + private List orders { get; set; } = new List(); protected override void OnInitialized() { - Orders = OrderData.GetAllRecords(); + orders = OrderData.GetAllRecords(); } private void ChangeDataSource() { - // Replace the DataSource with a new list of records. - Orders = OrderData.GetNewRecords(); + orders = OrderData.GetNewRecords(); } } @@ -1164,31 +1152,31 @@ The following example demonstrates how to change the `DataSource` of the Grid dy public class OrderData { - public static List Orders = new List(); - - public OrderData() { } + public int OrderID { get; set; } + public string CustomerID { get; set; } + public double Freight { get; set; } + public DateTime? OrderDate { get; set; } public OrderData(int orderID, string customerID, double freight, DateTime? orderDate) { - this.OrderID = orderID; - this.CustomerID = customerID; - this.Freight = freight; - this.OrderDate = orderDate; + OrderID = orderID; + CustomerID = customerID; + Freight = freight; + OrderDate = orderDate; } public static List GetAllRecords() { - if (Orders.Count == 0) + return new List { - Orders.Add(new OrderData(10248, "VINET", 32.38, new DateTime(1996, 7, 4))); - Orders.Add(new OrderData(10249, "TOMSP", 11.61, new DateTime(1996, 7, 5))); - Orders.Add(new OrderData(10250, "HANAR", 65.83, new DateTime(1996, 7, 6))); - Orders.Add(new OrderData(10251, "VINET", 41.34, new DateTime(1996, 7, 7))); - Orders.Add(new OrderData(10252, "SUPRD", 151.30, new DateTime(1996, 7, 8))); - Orders.Add(new OrderData(10253, "HANAR", 58.17, new DateTime(1996, 7, 9))); - Orders.Add(new OrderData(10254, "CHOPS", 22.98, new DateTime(1996, 7, 10))); - } - return Orders; + new OrderData(10248, "VINET", 32.38, new DateTime(1996, 7, 4)), + new OrderData(10249, "TOMSP", 11.61, new DateTime(1996, 7, 5)), + new OrderData(10250, "HANAR", 65.83, new DateTime(1996, 7, 6)), + new OrderData(10251, "VINET", 41.34, new DateTime(1996, 7, 7)), + new OrderData(10252, "SUPRD", 151.30, new DateTime(1996, 7, 8)), + new OrderData(10253, "HANAR", 58.17, new DateTime(1996, 7, 9)), + new OrderData(10254, "CHOPS", 22.98, new DateTime(1996, 7, 10)) + }; } public static List GetNewRecords() @@ -1201,24 +1189,20 @@ public class OrderData new OrderData(20004, "BERGS", 65.20, DateTime.Now.AddDays(-4)) }; } - - public int OrderID { get; set; } - public string CustomerID { get; set; } - public double Freight { get; set; } - public DateTime? OrderDate { get; set; } } + {% endhighlight %} {% endtabs %} -{% previewsample "https://blazorplayground.syncfusion.com/embed/rDhIXeCHJywphWgL?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} +{% previewsample "https://blazorplayground.syncfusion.com/embed/VtVICCDxpINPCgRu?appbar=false&editor=false&result=true&errorlist=false&theme=bootstrap5" %} -![Changing Datasource Dynamically in Blazor DataGrid](../images/blazor-datagrid-dynamic-datasource.gif) +![Changing datasource dynamically in Blazor DataGrid](./images/blazor-datagrid-dynamic-datasource.gif) ## Data binding with SignalR -The Syncfusion® Blazor DataGrid provides support for real-time data binding using SignalR, allowing you to update the Grid automatically as data changes on the server-side. This feature is particularly useful for applications requiring live updates and synchronization across multiple clients. +The Syncfusion® Blazor DataGrid provides support for real-time data binding using SignalR, allowing update of the Grid automatically as data changes on the server-side. This feature is particularly useful for applications requiring live updates and synchronization across multiple clients. -To achieve real-time data binding with SignalR in your Syncfusion® Blazor DataGrid, follow the steps below: +To achieve real-time data binding with SignalR in the Syncfusion® Blazor DataGrid, follow the steps: **Step 1:** Install the SignalR server package: @@ -1276,46 +1260,52 @@ namespace SignalRDataGrid.Data public string CustomerID { get; set; } public string ShipName { get; set; } - public static List OrderList = new List(); - private static readonly string[] CustomerIDs = new[] { - "VINET", "TOMSP", "HANAR", "VICTE", "SUPRD", "CHOPS", "RICSU", "WELLI", "HILAA", "ERNSH", "CENTC", "OTTIK", "QUEDE", "RATTC" + "VINET", "TOMSP", "HANAR", "VICTE", "SUPRD", "CHOPS", "RICSU", "WELLI", "HILAA", "ERNSH", "CENTC", "OTTIK", "QUEDE", "RATTC" }; private static readonly string[] ShipNames = new[] { - "Vins et alcools Chevalier", "Toms Spezialitäten", "Hanari Carnes", "Victuailles en stock", "Suprêmes délices", - "Chop-suey Chinese", "Richter Supermarkt", "Wellington Importadora", "HILARION-Abastos", "Ernst Handel", - "Centro comercial Moctezuma", "Ottilies Käseladen", "Que Delícia", "Rattlesnake Canyon Grocery" + "Vins et alcools Chevalier", "Toms Spezialitäten", "Hanari Carnes", "Victuailles en stock", "Suprêmes délices", + "Chop-suey Chinese", "Richter Supermarkt", "Wellington Importadora", "HILARION-Abastos", "Ernst Handel", + "Centro comercial Moctezuma", "Ottilies Käseladen", "Que Delicia", "Rattlesnake Canyon Grocery" }; - public static Task> GetOrdersAsync() + public static List OrderList { get; private set; } = new(); + + public Task> GetOrdersAsync() { - var rng = new Random(); + var random = new Random(); + if (OrderList.Count == 0) { OrderList = Enumerable.Range(10248, 75).Select(index => new OrderDetails { OrderID = index, - CustomerID = CustomerIDs[rng.Next(CustomerIDs.Length)], - ShipName = ShipNames[rng.Next(ShipNames.Length)] + CustomerID = CustomerIDs[random.Next(CustomerIDs.Length)], + ShipName = ShipNames[random.Next(ShipNames.Length)] }).ToList(); } return Task.FromResult(OrderList); - } + } + public Task UpdateAsync(OrderDetails model) { - var ord = OrderList.Where(x => x.OrderID == model.OrderID).FirstOrDefault(); - ord.CustomerID = model.CustomerID; - ord.ShipName = model.ShipName; + var order = OrderList.FirstOrDefault(x => x.OrderID == model.OrderID); + if (order != null) + { + order.CustomerID = model.CustomerID; + order.ShipName = model.ShipName; + } + return Task.FromResult(model); } + public List DeleteAsync(OrderDetails model) { - var ord = OrderList.Remove(model); - + OrderList.RemoveAll(x => x.OrderID == model.OrderID); return OrderList; } } @@ -1336,83 +1326,82 @@ namespace SignalRDataGrid.Data @inject OrderDetails OrderService @implements IAsyncDisposable - + - - - + + + @code { - SfGrid Grid { get; set; } - private HubConnection hubConnection; - public List OrderData = new List(); + private SfGrid? grid; + private HubConnection? hubConnection; + private List orderData = new List(); + protected override async Task OnInitializedAsync() { - // Initialize SignalR connection. hubConnection = new HubConnectionBuilder() - .WithUrl(NavigationManager.ToAbsoluteUri("/chathub")) - .Build(); + .WithUrl(NavigationManager.ToAbsoluteUri("/chathub")) + .Build(); - // Set up a handler for receiving messages from the hub. hubConnection.On("ReceiveMessage", () => { - // Refresh grid on receiving a message. - CallLoadData(); + RefreshGrid(); }); - // Start SignalR connection. await hubConnection.StartAsync(); - await LoadData(); + await LoadDataAsync(); + } + + private async Task LoadDataAsync() + { + orderData = await OrderService.GetOrdersAsync(); + } + + private async Task RefreshGrid() + { + await grid!.Refresh(); } - // Handles CRUD (Create, Read, Update, and Delete) operations. - public async Task ActionComplete(ActionEventArgs Args) + private async Task ActionComplete(ActionEventArgs args) { - if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Save) + if (args.RequestType == Syncfusion.Blazor.Grids.Action.Save) { - await OrderService.UpdateAsync(Args.Data); - if (IsConnected) await Send(); + await OrderService.UpdateAsync(args.Data); + if (IsConnected) await SendMessageAsync(); } - if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Delete) + + if (args.RequestType == Syncfusion.Blazor.Grids.Action.Delete) { - OrderData = OrderService.DeleteAsync(Args.Data); - if (IsConnected) await Send(); + orderData = OrderService.DeleteAsync(args.Data); + if (IsConnected) await SendMessageAsync(); } } - private void CallLoadData() - { - Grid.Refresh(); - } - protected async Task LoadData() + + private async Task SendMessageAsync() { - OrderData = await OrderDetails.GetOrdersAsync(); + await hubConnection!.SendAsync("SendMessage"); } - // Send a message to SignalR hub to notify other clients. - async Task Send() => - await hubConnection.SendAsync("SendMessage"); + private bool IsConnected => hubConnection!.State == HubConnectionState.Connected; - // Property to check SignalR connection state. - public bool IsConnected => hubConnection.State == HubConnectionState.Connected; - - // Dispose the SignalR connection properly when component is disposed. - public async ValueTask DisposeAsync() + public async ValueTask DisposeAsync() + { + if (hubConnection is not null) { - if (hubConnection is not null) - { - await hubConnection.DisposeAsync(); - } + await hubConnection!.DisposeAsync(); } + } } {% endhighlight %} {% endtabs %} -The above code demonstrates how to connect to a SignalR hub and refresh the Grid data in real time when updates are received. +The above code demonstrates establishment of a connection to a SignalR hub and refresh of the Grid data in real time when updates are received. **Step 6:** Adding the `OrderService` reference: @@ -1424,15 +1413,15 @@ builder.Services.AddSingleton(); The following screenshot illustrates the addition, editing, and deletion operations performed, with changes reflected across all client sides. -![SignalR Data](../images/signalR.gif) +![SignalR real-time data updates in Blazor DataGrid](./images/signalR.gif) Please find the sample in this [GitHub location](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/SignalRDataGrid). ## Binding data from Excel document -The Syncfusion® Blazor DataGrid allows you to import data from Excel documents into your web application for display and manipulation within the Grid. This feature streamlines the process of transferring Excel data to a web-based environment. You can achieve this by using the [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event of the `SfFileUploader`. +The Syncfusion® Blazor DataGrid allows import of data from Excel documents into your web application for display and manipulation within the Grid. This feature streamlines the process of transferring Excel data to a web-based environment. Achieve this by using the [ValueChange](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Inputs.UploaderEvents.html#Syncfusion_Blazor_Inputs_UploaderEvents_ValueChange) event of the `SfFileUploader`. -To import Excel data into Grid, you can follow these steps: +To import Excel data into Grid: 1. Use the [SfFileUploader](https://blazor.syncfusion.com/documentation/file-upload/getting-started-with-web-app) to upload the Excel document. @@ -1442,136 +1431,142 @@ To import Excel data into Grid, you can follow these steps: 4. Bind the list to the Grid. -The following example demonstrates how an Excel document is uploaded, parsed, converted into a list of `ExpandoObject`, and then bound to the Grid: +The following example demonstrates upload of an Excel document, parsing, conversion into a list of `ExpandoObject`, and binding to the Grid: {% tabs %} {% highlight razor tabtitle="Index.razor" %} -@using Syncfusion.XlsIO; -@using System.IO; -@using Syncfusion.Blazor.Grids; -@using Syncfusion.Blazor.Inputs; -@using System.Data; -@using System.Dynamic; -@using Microsoft.AspNetCore.Hosting; +@using Syncfusion.XlsIO +@using System.IO +@using Syncfusion.Blazor.Grids +@using Syncfusion.Blazor.Inputs +@using System.Data +@using System.Dynamic +@using Microsoft.AspNetCore.Hosting @using Syncfusion.Blazor.Popups - + +
-@if (CustomerList != null && CustomerList.Count > 0) + +@if (customerList?.Count > 0) { - - + } + + @dialogContent @code { - SfGrid Grid; - SfDialog dialog; - public DataTable table = new DataTable(); - [Inject] private IWebHostEnvironment HostEnvironment { get; set; } + private SfGrid? grid; + private SfDialog? dialog; + private string[]? columns; + private List customerList = new(); + private string dialogContent = string.Empty; + private DataTable table = new(); + + [Inject] private IWebHostEnvironment? HostEnvironment { get; set; } - private async void OnChange(UploadChangeEventArgs args) + private async Task OnChange(UploadChangeEventArgs args) { if (args.Files[0].FileInfo.Type == "xlsx") { foreach (var file in args.Files) { var path = GetPath(file.FileInfo.Name); - ExcelEngine excelEngine = new ExcelEngine(); - IApplication application = excelEngine.Excel; - application.DefaultVersion = ExcelVersion.Excel2016; - // Create new file stream at the generated path. - FileStream openFileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write); + using var openFileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write); await file.File.OpenReadStream(long.MaxValue).CopyToAsync(openFileStream); - openFileStream.Close(); - // Open file stream from saved path. - FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - IWorkbook workbook = application.Workbooks.Open(fileStream); - IWorksheet worksheet = workbook.Worksheets[0]; + using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var excelEngine = new ExcelEngine(); + var application = excelEngine.Excel; + application.DefaultVersion = ExcelVersion.Excel2016; + + var workbook = application.Workbooks.Open(fileStream); + var worksheet = workbook.Worksheets[0]; table = worksheet.ExportDataTable(worksheet.UsedRange, ExcelExportDataTableOptions.ColumnNames); - GenerateListFromTable(table); - } + await GenerateListFromTable(table); + } } else { - dialog.Content = "Please upload only .xlsx format"; - dialog.ShowAsync(true); + dialogContent = "Please upload only .xlsx format"; + await dialog!.ShowAsync(); } } private async Task OnRemove(RemovingEventArgs args) { - CustomerList = new List(); // Clear data. - Columns = null; + customerList.Clear(); + columns = null; + await Task.CompletedTask; } private string GetPath(string filename) { - return Path.Combine(HostEnvironment.WebRootPath, filename); + return Path.Combine(HostEnvironment!.WebRootPath, filename); } - string[] Columns; - public List CustomerList = new List(); - - public void GenerateListFromTable(DataTable input) + private async Task GenerateListFromTable(DataTable input) { - // Check if at least one cell has meaningful data. bool hasData = input.Rows.Cast() .Any(row => row.ItemArray.Any(cell => cell != null && !string.IsNullOrWhiteSpace(cell.ToString()))); if (!hasData) { - dialog.Content = "The uploaded Excel file contains only blank rows or invalid data."; - dialog.ShowAsync(); - return; // Exit if the data is invalid. + dialogContent = "The uploaded Excel file contains only blank rows or invalid data."; + await dialog!.ShowAsync(); + return; } var list = new List(); - Columns = input.Columns.Cast() - .Select(x => x.ColumnName) - .ToArray(); + columns = input.Columns.Cast().Select(x => x.ColumnName).ToArray(); + foreach (DataRow row in input.Rows) { - System.Dynamic.ExpandoObject e = new System.Dynamic.ExpandoObject(); + var expando = new ExpandoObject() as IDictionary; foreach (DataColumn col in input.Columns) - e.TryAdd(col.ColumnName, row.ItemArray[col.Ordinal]); - list.Add(e); + { + expando![col.ColumnName] = row[col]; + } + list.Add((ExpandoObject)expando!); } - CustomerList = list; + + customerList = list; StateHasChanged(); + await Task.CompletedTask; } } {% endhighlight %} {% endtabs %} -[!Binding data from Excel document](./images/excel-import-data.gif) +![Binding data from Excel document](./images/excel-import-data.gif) -> You can find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/Binding_data_from_excel/Excel_Export). +> Find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/Binding_data_from_excel/Excel_Export). ## Observable collection -An Observable collection is a special type of collection in .NET that automatically notifies any subscribers (such as the UI or other components) when changes are made to the collection. This is particularly useful in data-binding scenarios, where you want the UI to reflect changes in the underlying data model without having to manually update the view. +An **Observable collection** is a special type of collection in .NET that automatically notifies subscribers (such as the UI or other components) when changes are made to the collection. This is particularly useful in data-binding scenarios, where you want the UI to reflect changes in the underlying data model without manually updating the view. -To achieve this, you can use the [ObservableCollection](https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netframework-4.8), a dynamic data collection that: +To achieve this, use the [ObservableCollection](https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netframework-4.8), a dynamic data collection that: * Provides notifications when items are added, removed, or moved. @@ -1587,49 +1582,60 @@ The following sample demonstrates how the Order class implements the **INotifyPr @using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Buttons @using System.Collections.ObjectModel -@using Observable_Collection.Components.Data; +@using BlazorApp3.Data -
- - - +
+ + +
- + - - - - - + + + + + - @code { - - public ObservableCollection GridData { get; set; } - public int Count = 32341; + private ObservableCollection? gridData; + private int count = 32341; protected override void OnInitialized() { - GridData = OrdersDetailsObserveData.GetRecords(); + gridData = OrdersDetailsObserveData.GetRecords(); } - public void AddRecords() + private void AddRecords() { - GridData.Add(new OrdersDetailsObserveData(Count++, "ALFKI", 4343, 2.3 * 43, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6")); + gridData!.Add(new OrdersDetailsObserveData( + count++, "ALFKI", 4343, 2.3 * 43, false, + new DateTime(1991, 5, 15), "Berlin", "Simons bistro", + "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6")); } - public void DelRecords() + private void DelRecords() { - GridData.Remove(GridData.First()); + if (gridData!.Any()) + { + gridData!.Remove(gridData.First()); + } } - public void UpdateRecords() + private void UpdateRecords() { - var a = GridData.First(); - a.CustomerID = "Update"; + var record = gridData!.FirstOrDefault(); + if (record != null) + { + record.CustomerID = "Updated"; + } } } @@ -1643,74 +1649,88 @@ using System.ComponentModel; namespace Observable_Collection.Components.Data { public class OrdersDetailsObserveData : INotifyPropertyChanged +{ + + public OrdersDetailsObserveData( + int orderID, string customerID, int employeeID, double freight, bool verified, + DateTime orderDate, string shipCity, string shipName, string shipCountry, + DateTime shippedDate, string shipAddress) { - public OrdersDetailsObserveData() - { - } - public OrdersDetailsObserveData(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress) - { - this.OrderID = OrderID; - this.CustomerID = CustomerId; - this.EmployeeID = EmployeeId; - this.Freight = Freight; - this.ShipCity = ShipCity; - this.Verified = Verified; - this.OrderDate = OrderDate; - this.ShipName = ShipName; - this.ShipCountry = ShipCountry; - this.ShippedDate = ShippedDate; - this.ShipAddress = ShipAddress; - } - public static ObservableCollection GetRecords() - { - ObservableCollection order = new ObservableCollection(); - int code = 10000; - for (int i = 1; i < 2; i++) - { - order.Add(new OrdersDetailsObserveData(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6")); - order.Add(new OrdersDetailsObserveData(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123")); - order.Add(new OrdersDetailsObserveData(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bol�var #65-98 Llano Largo")); - order.Add(new OrdersDetailsObserveData(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7")); - order.Add(new OrdersDetailsObserveData(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S.")); - code += 5; - } - return order; - } + OrderID = orderID; + CustomerID = customerID; + EmployeeID = employeeID; + Freight = freight; + Verified = verified; + OrderDate = orderDate; + ShipCity = shipCity; + ShipName = shipName; + ShipCountry = shipCountry; + ShippedDate = shippedDate; + ShipAddress = shipAddress; + } - public int OrderID { get; set; } - public string CustomerID + public static ObservableCollection GetRecords() + { + var orders = new ObservableCollection(); + int code = 10000; + + for (int i = 1; i < 2; i++) { - get { return customerID; } - set - { - customerID = value; - NotifyPropertyChanged("CustomerID"); - } - } - public string customerID { get; set; } - public int? EmployeeID { get; set; } - public double? Freight { get; set; } - public string ShipCity { get; set; } - public bool Verified { get; set; } - public DateTime? OrderDate { get; set; } + orders.Add(new OrdersDetailsObserveData(code + 1, "ALFKI", i + 0, 2.3 * i, false, + new DateTime(1991, 5, 15), "Berlin", "Simons bistro", "Denmark", + new DateTime(1996, 7, 16), "Kirchgasse 6")); - public event PropertyChangedEventHandler PropertyChanged; + orders.Add(new OrdersDetailsObserveData(code + 2, "ANATR", i + 2, 3.3 * i, true, + new DateTime(1990, 4, 4), "Madrid", "Queen Cozinha", "Brazil", + new DateTime(1996, 9, 11), "Avda. Azteca 123")); - public string ShipName { get; set; } + orders.Add(new OrdersDetailsObserveData(code + 3, "ANTON", i + 1, 4.3 * i, true, + new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", + new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo")); + + orders.Add(new OrdersDetailsObserveData(code + 4, "BLONP", i + 3, 5.3 * i, false, + new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", + new DateTime(1996, 12, 30), "Magazinweg 7")); + + orders.Add(new OrdersDetailsObserveData(code + 5, "BOLID", i + 4, 6.3 * i, true, + new DateTime(1953, 2, 18), "Tsawassen", "Hanari Carnes", "Switzerland", + new DateTime(1997, 12, 3), "1029 - 12th Ave. S.")); - public string ShipCountry { get; set; } + code += 5; + } + + return orders; + } - public DateTime ShippedDate { get; set; } - public string ShipAddress { get; set; } + public int OrderID { get; set; } - private void NotifyPropertyChanged(String propertyName) + private string customerID; + public string CustomerID + { + get => customerID; + set { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } + customerID = value; + NotifyPropertyChanged(nameof(CustomerID)); } } + int? EmployeeID { get; set; } + public double? Freight { get; set; } + public string ShipCity { get; set; } + public bool Verified { get; set; } + public DateTime? OrderDate { get; set; } + public string ShipName { get; set; } + public string ShipCountry { get; set; } + public DateTime ShippedDate { get; set; } + public string ShipAddress { get; set; } + + public event PropertyChangedEventHandler PropertyChanged; + + private void NotifyPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} } {% endhighlight %} @@ -1720,9 +1740,10 @@ The following screenshot represents the Grid with **Observable Collection**. ![Blazor DataGrid with ObservableCollection](./images/blazor-datagrid-observable.gif) -> You can find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/observable_collection/Observable_Collection). +N> * The Grid automatically reflects changes when records are added, removed, or updated. +* When updating the collection using external triggers (e.g., timers, events), call **StateHasChanged()** to refresh the UI. -N> While using an Observable collection, the added, removed, and changed records are reflected in the UI. But while updating the Observable collection using external actions like timers, events, and other notifications, you need to call the StateHasChanged method to reflect the changes in the UI. +> Find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/observable_collection/Observable_Collection). ### Add a range of items into ObservableCollection in Blazor DataGrid @@ -1741,17 +1762,17 @@ The Syncfusion® Blazor DataGrid supports bi By default, the `Add` method is used to insert a single item into the **ObservableCollection**. When multiple items are added one by one using a `foreach` loop, the Grid refreshes after each addition. This can lead to performance issues and UI flickering, especially when adding a large number of items. -To optimize performance when adding multiple items at once, you can extend the `ObservableCollection` class by implementing an `AddRange` method. By using this method, you can add a range of items and ensure that the `OnCollectionChanged` event is triggered only once, updating the Grid a single time for the entire batch operation. +To optimize performance when adding multiple items at once, extend the `ObservableCollection` class by implementing an `AddRange` method. By using this method, add a range of items and ensure that the `OnCollectionChanged` event is triggered only once, updating the Grid a single time for the entire batch operation. To implement this functionality, follow these steps: 1. **Create a Custom Collection Class** - Define a new class **SmartObservableCollection** that inherits from `ObservableCollection`. This allows you to customize the behavior of the collection. + Define a new class **SmartObservableCollection** that inherits from `ObservableCollection`. This allows customization of the behavior of the collection. 2. **Add a flag to control notifications** - Introduce a private boolean **flag _preventNotification** to temporarily disable collection change notifications while adding multiple items. + Introduce a private boolean **_preventNotification** to temporarily disable collection change notifications while adding multiple items. 3. **Override the OnCollectionChanged method** @@ -1766,7 +1787,7 @@ To implement this functionality, follow these steps: * Resetting **_preventNotification** to **false**. * Raising a single **NotifyCollectionChangedAction.Reset** notification to inform the Grid that the entire collection has changed. -The following example demonstrates how to use this approach in a Grid: +The following example demonstrates usage of this approach in a Grid: {% tabs %} {% highlight razor tabtitle="Index.razor" %} @@ -1775,41 +1796,50 @@ The following example demonstrates how to use this approach in a Grid: @using Syncfusion.Blazor.Buttons @using System.Collections.ObjectModel @using System.Collections.Specialized -@using ObservableCollection.Components.Data; +@using ObservableCollection.Components.Data
- Add Range of Items + Add Range of Items
- + + - - - - + + + + + @code { - SfGrid Grid; - public SmartObservableCollection GridData = new SmartObservableCollection(); - public void AddRangeItems() + private SfGrid? grid; + private SmartObservableCollection gridData = new(); + + private void AddRangeItems() { - GridData.AddRange(OrdersDetailsObserveData.GetAllRecords()); + gridData.AddRange(OrdersDetailsObserveData.GetAllRecords()); } public class SmartObservableCollection : ObservableCollection { - private bool _preventNotification = false; + private bool preventNotification; + protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - if (!_preventNotification) + if (!preventNotification) + { base.OnCollectionChanged(e); + } } + public void AddRange(IEnumerable list) { - _preventNotification = true; - foreach (T item in list) + preventNotification = true; + foreach (var item in list) + { Add(item); - _preventNotification = false; + } + preventNotification = false; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } @@ -1824,16 +1854,19 @@ namespace ObservableCollection.Components.Data public class OrdersDetailsObserveData { public int? OrderID { get; set; } - public string CustomerID { get; set; } + public string CustomerID { get; set; } = string.Empty; public DateTime? OrderDate { get; set; } public double? Freight { get; set; } public static IEnumerable GetAllRecords() { + var random = new Random(); + var customers = new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" }; + return Enumerable.Range(1, 10).Select(x => new OrdersDetailsObserveData { OrderID = 1000 + x, - CustomerID = (new[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)], + CustomerID = customers[random.Next(customers.Length)], Freight = Math.Round(2.1 * x, 2), OrderDate = DateTime.Now.AddDays(-x) }).ToList(); @@ -1848,8 +1881,9 @@ The following screenshot represents the Grid with **Observable Collection**. ![Blazor DataGrid with ObservableCollection](./images/Observable-collection-range.gif) -> You can find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/add_range_items_observableCollection/ObservableCollection). +> Find the complete sample on [GitHub](https://github.com/SyncfusionExamples/databinding-in-blazor-datagrid/tree/master/add_range_items_observableCollection/ObservableCollection). ## See also -* [How to clear all Data from Grid](https://www.syncfusion.com/forums/150965/how-to-clear-all-data-from-grid) +* [How to import data from Excel sheet and bind to Blazor Grid](https://support.syncfusion.com/kb/article/11560/how-to-import-data-from-excel-sheet-and-bind-to-blazor-grid) +* [How to clear all Data from Grid](https://www.syncfusion.com/forums/150965/how-to-clear-all-data-from-grid) \ No newline at end of file