blazor-403737-components-grid-bind-to-data.md
This document describes how to bind the Blazor Grid to data in different scenarios.
Use the Data parameter to specify a data source. To display data within the Grid, declare DxGridDataColumn objects in the Columns template and use each object’s FieldName property to assign data fields. Note that the FieldName property value must be unique for each data column.
View Example: How to bind the Web API Service
View Example: Display an error message from the Web API Service
You can bind the Grid to a data collection available during synchronous component initialization.
@inject WeatherForecastService ForecastService
<DxGrid Data="@Data">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Caption="@("Temp. (\x2103)")" Width="120px" />
<DxGridDataColumn FieldName="TemperatureF" Caption="@("Temp. (\x2109)")" Width="120px" />
<DxGridDataColumn FieldName="Forecast" />
<DxGridDataColumn FieldName="CloudCover" />
</Columns>
</DxGrid>
@code {
object Data { get; set; }
protected override void OnInitialized() {
Data = ForecastService.GetForecast();
}
}
using System;
public class WeatherForecast {
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public double TemperatureF => Math.Round((TemperatureC * 1.8 + 32), 2);
public string Forecast { get; set; }
public string CloudCover { get; set; }
public bool Precipitation { get; set; }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class WeatherForecastService {
private List<WeatherForecast> Forecast { get; set; }
private static string[] CloudCover = new[] {
"Sunny", "Partly cloudy", "Cloudy", "Storm"
};
Tuple<int, string>[] ConditionsForForecast = new Tuple<int, string>[] {
Tuple.Create( 22 , "Hot"),
Tuple.Create( 13 , "Warm"),
Tuple.Create( 0 , "Cold"),
Tuple.Create( -10 , "Freezing")
};
public WeatherForecastService() {
Forecast = CreateForecast();
}
private List<WeatherForecast> CreateForecast() {
var rng = new Random();
DateTime startDate = DateTime.Now;
return Enumerable.Range(1, 15).Select(index => {
var temperatureC = rng.Next(-10, 30);
return new WeatherForecast {
Date = startDate.AddDays(index),
TemperatureC = temperatureC,
CloudCover = CloudCover[rng.Next(0, 4)],
Precipitation = Convert.ToBoolean(rng.Next(0, 2)),
Forecast = ConditionsForForecast.First(c => c.Item1 <= temperatureC).Item2
};
}).ToList();
}
public IEnumerable<WeatherForecast> GetForecast() {
return Forecast.ToArray();
}
// ...
}
// ...
builder.Services.AddSingleton<WeatherForecastService>();
Run Demo: Grid - Data Binding
View Example: Bind the Grid to a DataTable object
The Grid can also use a data collection that becomes available after asynchronous component initialization.
@inject WeatherForecastService ForecastService
<DxGrid Data="@Data">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Caption="@("Temp. (\x2103)")" Width="120px" />
<DxGridDataColumn FieldName="TemperatureF" Caption="@("Temp. (\x2109)")" Width="120px" />
<DxGridDataColumn FieldName="Forecast" />
<DxGridDataColumn FieldName="CloudCover" />
</Columns>
</DxGrid>
@code {
object Data { get; set; }
protected override async Task OnInitializedAsync() {
Data = await ForecastService.GetForecastAsync();
}
}
using System;
public class WeatherForecast {
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public double TemperatureF => Math.Round((TemperatureC * 1.8 + 32), 2);
public string Forecast { get; set; }
public string CloudCover { get; set; }
public bool Precipitation { get; set; }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class WeatherForecastService {
private List<WeatherForecast> Forecast { get; set; }
private static string[] CloudCover = new[] {
"Sunny", "Partly cloudy", "Cloudy", "Storm"
};
Tuple<int, string>[] ConditionsForForecast = new Tuple<int, string>[] {
Tuple.Create( 22 , "Hot"),
Tuple.Create( 13 , "Warm"),
Tuple.Create( 0 , "Cold"),
Tuple.Create( -10 , "Freezing")
};
public WeatherForecastService() {
Forecast = CreateForecast();
}
private List<WeatherForecast> CreateForecast() {
var rng = new Random();
DateTime startDate = DateTime.Now;
return Enumerable.Range(1, 15).Select(index => {
var temperatureC = rng.Next(-10, 30);
return new WeatherForecast {
Date = startDate.AddDays(index),
TemperatureC = temperatureC,
CloudCover = CloudCover[rng.Next(0, 4)],
Precipitation = Convert.ToBoolean(rng.Next(0, 2)),
Forecast = ConditionsForForecast.First(c => c.Item1 <= temperatureC).Item2
};
}).ToList();
}
// ...
public Task<WeatherForecast[]> GetForecastAsync(CancellationToken ct = default) {
return Task.FromResult(Forecast.ToArray());
}
// ...
builder.Services.AddSingleton<WeatherForecastService>();
Run Demo: Grid - Asynchronous Data BindingView Example: Bind the Grid to data with Entity Framework Core
You can bind the Grid to a data collection that implements the INotifyCollectionChanged or IBindingList interface. For instance, you can use the standard ObservableCollection<T> or BindingList<T> objects. These collections notify the Grid about changes, such as add, remove, collection refresh operations, and so on. The Grid updates its data automatically to reflect these changes.
Each item in such collections should also implement the INotifyPropertyChanged interface to notify the Grid when a property value changes.
The following sample binds the Grid to an ObservableCollection and adds new items to this collection on button clicks. The INotifyPropertyChanged interface is not implemented because properties of collection items do not change.
@using System.Collections.ObjectModel
<DxButton Text="Add New Day"
Click="(e) => AddNewForecast()" />
<p/>
<DxGrid Data="@WeatherForecastData">
<Columns>
<DxGridDataColumn FieldName="Date" DisplayFormat="D" />
<DxGridDataColumn FieldName="TemperatureC" Caption="@("Temp. (\x2103)")" />
<DxGridDataColumn FieldName="TemperatureF" Caption="@("Temp. (\x2109)")" />
</Columns>
</DxGrid>
@code {
int DayCount { get; set; } = 0;
ObservableCollection<WeatherForecast> WeatherForecastData { get; set; }
static readonly Random Rnd = new Random();
protected override void OnInitialized() {
WeatherForecastData = new ObservableCollection<WeatherForecast>();
foreach (var date in Enumerable.Range(1, 5).Select(i => DateTime.Now.Date.AddDays(i))) {
AddNewForecast();
}
}
void AddNewForecast() {
WeatherForecastData.Add(new WeatherForecast() {
Date = DateTime.Now.Date.AddDays(++DayCount),
TemperatureC = Rnd.Next(10, 20)
});
}
}
using System;
public class WeatherForecast {
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public double TemperatureF => Math.Round((TemperatureC * 1.8 + 32), 2);
public string Forecast { get; set; }
public string CloudCover { get; set; }
public bool Precipitation { get; set; }
}
Note
After you bind a dynamic data collection to the Grid, the collection sends notifications after each change separately. When you make a sequence of modifications (for instance, in a for loop), the Grid is re-rendered after each iteration.
Run Demo: Grid - Observable Data Collections
In Blazor Server applications, the Grid supports Server Mode data sources designed to work with large data collections.
When you assign a data collection to the Data property directly , the Grid stores all data records in memory. This increases memory consumption if the collection is large. All data processing operations (sort, group, filter, and so on) are performed within the Blazor application and can cause lags.
When you use Server Mode data sources , the Grid loads data in small portions on demand (instead of the entire dataset). This helps reduce memory consumption. All data shaping operations are delegated to underlying services (such as EF Core, XPO, and so on). These services process operations more efficiently and enhance overall performance. Note that these data sources have specifics and limitations.
The Grid includes two types of Server Mode data sources:
Server Mode Data SourceA synchronous data source that locks the UI and does not respond to user actions while data is retrieved.Instant Feedback Data SourceAn asynchronous data source that loads data in a background thread and does not freeze the UI. The Grid displays skeletons and data load indicators when data is not retrieved.
The following table lists cross-platform Server Mode data sources compatible with the Blazor Grid:
|
Data Access Technology
|
Server Mode Data Source
|
Instant Feedback Data Source
| | --- | --- | --- | |
|
|
| |
|
|
| |
|
|
| |
|
|
|
Follow these steps to use a Server Mode data source in the Grid:
null and dispose of it.The following code snippet uses an EntityInstantFeedbackSource in the Grid. Create a data source instance and specify two required parameters:
KeyExpressionIdentifies the name of the entity model’s key property. This property should correspond to the primary key from an underlying database. If the database does not contain the primary key, you cannot use this database as a Server Mode data source.QueryableSourceDefines a queryable data source from an EF Core data context (for instance, DbSet<T>).
For additional information on how to use EF Core, refer to the following topic: Bind Components to Data with Entity Framework Core.
Run Demo: Grid - Large Data (Instant Feedback Source) View Example: Bind to an Instant Feedback Data Source
@using InstantFeedback.Models;
@using Microsoft.EntityFrameworkCore
@using DevExpress.Data.Linq
@inject IDbContextFactory<NorthwindContext> NorthwindContextFactory
@implements IDisposable
<DxGrid Data="InstantFeedbackSource"
KeyFieldName="OrderId">
<Columns>
<DxGridDataColumn FieldName="ShipName" />
<DxGridDataColumn FieldName="ShipCity" />
<DxGridDataColumn FieldName="ShipCountry" />
<DxGridDataColumn FieldName="Freight" />
<DxGridDataColumn FieldName="OrderDate" />
<DxGridDataColumn FieldName="ShippedDate" />
</Columns>
</DxGrid>
@code {
EntityInstantFeedbackSource InstantFeedbackSource { get; set; }
NorthwindContext Northwind { get; set; }
protected override void OnInitialized() {
Northwind = NorthwindContextFactory.CreateDbContext();
InstantFeedbackSource = new EntityInstantFeedbackSource(e => {
e.KeyExpression = "OrderId";
e.QueryableSource = Northwind.Orders;
});
}
public void Dispose() {
InstantFeedbackSource?.Dispose();
Northwind?.Dispose();
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace InstantFeedback.Models {
public partial class Order {
public int OrderId { get; set; }
[Required]
public string ShipName { get; set; }
[Required]
public string ShipCity { get; set; }
[Required]
public string ShipCountry { get; set; }
public decimal? Freight { get; set; }
[Range(typeof(DateTime), "1/1/2010", "1/1/2024",
ErrorMessage = "OrderDate must be between {1:d} and {2:d}")]
public DateTime? OrderDate { get; set; }
[Range(typeof(DateTime), "1/1/2010", "1/1/2024",
ErrorMessage = "ShippedDate must be between {1:d} and {2:d}")]
public DateTime? ShippedDate { get; set; }
}
}
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace InstantFeedback.Models;
public partial class NorthwindContext : DbContext {
public NorthwindContext() {}
public NorthwindContext(DbContextOptions<NorthwindContext> options) : base(options) {}
public virtual DbSet<Order> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");
modelBuilder.Entity<Order>(entity => {
entity.HasIndex(e => e.OrderDate, "OrderDate");
entity.HasIndex(e => e.ShippedDate, "ShippedDate");
entity.Property(e => e.OrderId).HasColumnName("OrderID");
entity.Property(e => e.Freight)
.HasColumnType("money")
.HasDefaultValueSql("((0))");
entity.Property(e => e.OrderDate).HasColumnType("datetime");
entity.Property(e => e.ShipCity).HasMaxLength(15);
entity.Property(e => e.ShipCountry).HasMaxLength(15);
entity.Property(e => e.ShipName).HasMaxLength(40);
entity.Property(e => e.ShippedDate).HasColumnType("datetime");
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
using Microsoft.EntityFrameworkCore;
// ...
builder.Services.AddDbContextFactory<NorthwindContext>((sp, options) => {
var env = sp.GetRequiredService<IWebHostEnvironment>();
var dbPath = Path.Combine(env.ContentRootPath, "Northwind.db");
options.UseSqlite("Data Source=" + dbPath);
});
The Grid imposes the following specifics and limitations when you use Server Mode data sources:
Finalize.AllPages mode of the Select All checkbox is not supported.An Instant Feedback data source loads data asynchronously in small portions (instead of the entire dataset). Call the WaitForRemoteSourceRowLoadAsync(Int32) method to ensure that the specified data row is loaded. For instance, call this method before methods that accept a row’s visible index as a parameter (SelectRow, StartEditRowAsync, and so on).
If a data source’s AreSourceRowsThreadSafe option is set to false (the default value), you cannot cast a data item and use the {DataItem.FieldName} notation. Call the GetDataItemValue(Object, String) method to obtain the data item’s field value. Note that this method does not provide access to the data source object and you cannot handle events or call methods related to the object.
In Blazor Server and Blazor WebAssembly applications, you can use the GridDevExtremeDataSource<T> to bind the Grid to a large IQueryable data collection.
When you use this data source, the Grid delegates data processing operations to an underlying query provider (such as LINQ to Objects, EF Core, and so on). The Grid only loads data required to display a screen. This helps enhance overall performance and application responsiveness, and reduces memory consumption. Data source implementation is based on the DevExpress DevExtreme.AspNet.Data library.
Follow the steps below to bind the Grid to a large IQueryable data collection stored locally:
GridDevExtremeDataSource class instance and pass your IQueryable<T> data collection as the constructor parameter.The following code snippet uses the Entity Framework Core technology to bind the Grid to an IQueryable<T> data collection:
@using Microsoft.EntityFrameworkCore
@inject IDbContextFactory<NorthwindContext> NorthwindContextFactory
@implements IDisposable
<DxGrid Data="GridDataSource">
<Columns>
<DxGridDataColumn FieldName="OrderDate" DisplayFormat="d" />
<DxGridDataColumn FieldName="CustomerName" />
<DxGridDataColumn FieldName="Country" />
<DxGridDataColumn FieldName="Freight" DisplayFormat="n2" />
<DxGridDataColumn FieldName="ExtendedPrice" DisplayFormat="c" />
</Columns>
</DxGrid>
@code {
object GridDataSource { get; set; }
NorthwindContext Northwind { get; set; }
protected override void OnInitialized() {
Northwind = NorthwindContextFactory.CreateDbContext();
GridDataSource = new GridDevExtremeDataSource<Invoice>(Northwind.Invoices);
}
public void Dispose() {
Northwind?.Dispose();
}
}
using System;
#nullable disable
namespace Grid.Northwind {
public partial class Invoice {
public string ShipName { get; set; }
public string ShipAddress { get; set; }
public string ShipCity { get; set; }
public string ShipRegion { get; set; }
public string ShipPostalCode { get; set; }
public string ShipCountry { get; set; }
public string CustomerId { get; set; }
public string CustomerName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string Salesperson { get; set; }
public int OrderId { get; set; }
public DateTime? OrderDate { get; set; }
public DateTime? RequiredDate { get; set; }
public DateTime? ShippedDate { get; set; }
public string ShipperName { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public short Quantity { get; set; }
public float Discount { get; set; }
public decimal? ExtendedPrice { get; set; }
public decimal? Freight { get; set; }
}
}
using Microsoft.EntityFrameworkCore;
#nullable disable
namespace Grid.Northwind {
public partial class NorthwindContext : DbContext {
public NorthwindContext(DbContextOptions<NorthwindContext> options)
: base(options) {
}
// ...
public virtual DbSet<Invoice> Invoices { get; set; }
// ...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
if(!optionsBuilder.IsConfigured) {
optionsBuilder.UseSqlServer("Server=.\\sqlexpress;Database=Northwind;Integrated Security=true");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS");
// ...
modelBuilder.Entity<Invoice>(entity => {
entity.HasNoKey();
entity.ToView("Invoices");
entity.Property(e => e.Address).HasMaxLength(60);
entity.Property(e => e.City).HasMaxLength(15);
entity.Property(e => e.Country).HasMaxLength(15);
entity.Property(e => e.CustomerId)
.HasMaxLength(5)
.HasColumnName("CustomerID")
.IsFixedLength(true);
entity.Property(e => e.CustomerName)
.IsRequired()
.HasMaxLength(40);
entity.Property(e => e.ExtendedPrice).HasColumnType("money");
entity.Property(e => e.Freight).HasColumnType("money");
entity.Property(e => e.OrderDate).HasColumnType("datetime");
entity.Property(e => e.OrderId).HasColumnName("OrderID");
entity.Property(e => e.PostalCode).HasMaxLength(10);
entity.Property(e => e.ProductId).HasColumnName("ProductID");
entity.Property(e => e.ProductName)
.IsRequired()
.HasMaxLength(40);
entity.Property(e => e.Region).HasMaxLength(15);
entity.Property(e => e.RequiredDate).HasColumnType("datetime");
entity.Property(e => e.Salesperson)
.IsRequired()
.HasMaxLength(31);
entity.Property(e => e.ShipAddress).HasMaxLength(60);
entity.Property(e => e.ShipCity).HasMaxLength(15);
entity.Property(e => e.ShipCountry).HasMaxLength(15);
entity.Property(e => e.ShipName).HasMaxLength(40);
entity.Property(e => e.ShipPostalCode).HasMaxLength(10);
entity.Property(e => e.ShipRegion).HasMaxLength(15);
entity.Property(e => e.ShippedDate).HasColumnType("datetime");
entity.Property(e => e.ShipperName)
.IsRequired()
.HasMaxLength(40);
entity.Property(e => e.UnitPrice).HasColumnType("money");
});
// ...
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
using Microsoft.EntityFrameworkCore;
// ...
builder.Services.AddDbContextFactory<NorthwindContext>((sp, options) => {
var dbPath = Path.Combine(env.ContentRootPath, "Northwind-5e44b51f.mdf");
options.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Integrated Security=true;AttachDbFileName=" + dbPath);
});
Run Demo: Grid - Large Data (Queryable)View Example: Bind to a DevExtreme Data Source
Follow these steps to bind the Grid to a large data collection published as an HTTP service:
Create a GridDevExtremeDataSource class instance and pass two constructor parameters:
Assign the GridDevExtremeDataSource class instance to the Grid’s Data property.
On the service side, implement an API controller. Create action methods that use the DevExtreme.AspNet.Data library’s DataSourceLoader class to create a LoadResult object based on load options. You should also implement a custom model binder. Refer to the following link for additional information and an example: Server Side Configuration.
@inject HttpClient HttpClient
<DxGrid Data="@Data">
<Columns>
<DxGridDataColumn FieldName="CustomerID" />
<DxGridDataColumn FieldName="OrderDate" DisplayFormat="d" />
<DxGridDataColumn FieldName="Freight" DisplayFormat="n2" />
<DxGridDataColumn FieldName="ShipCountry" />
</Columns>
</DxGrid>
@code {
object Data { get; set; }
public class Order {
public string CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public decimal Freight { get; set; }
public string ShipCountry { get; set; }
}
protected override async Task OnInitializedAsync() {
var uri = new Uri("https://js.devexpress.com/Demos/NetCore/api/DataGridWebApi/Orders");
Data = new GridDevExtremeDataSource<Order>(HttpClient, uri);
}
}
// ...
builder.Services.AddHttpClient();
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
// ...
[Route("api /[controller] /[action]")]
public class DataGridWebApiController : Controller
{
NorthwindContext _nwind;
[HttpGet]
public object Orders(DataSourceLoadOptions loadOptions) {
return DataSourceLoader.Load(_nwind.Orders, loadOptions);
}
// ...
}
Run Demo: Grid - Large Data (Queryable as HTTP Service)
You can also generate and send custom HTTP requests:
Implement an asynchronous function that returns a Task<Stream> object and accepts two parameters:
Create a GridDevExtremeDataSource class instance and pass two constructor parameters (the newly created function and the URL to the service’s controller action).
Assign the GridDevExtremeDataSource instance to the Grid’s Data property.
The following code snippet adds an authorization header to HTTP requests. Note that you should process authorization information in the service’s controller.
@*...*@
@code {
// ...
protected override async Task OnInitializedAsync() {
var uri = new Uri("https://js.devexpress.com/Demos/NetCore/api/DataGridWebApi/Orders");
Data = new GridDevExtremeBasedDataSource<Product>(ExecuteDataSourceHttpRequest, url);
}
async Task<Stream> ExecuteDataSourceHttpRequest(Uri url, CancellationToken cancellationToken) {
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "...");
var response = await HttpClient.SendAsync(request);
return await response.Content.ReadAsStreamAsync();
}
}
The GridDevExtremeDataSource imposes the following limitations on Grid features that are available when you assign a data collection to the Data property directly:
GridDevExtremeDataSource, the Grid searches, filters, and sorts data by cell values.AllPages mode of the Select All checkbox is not supported.The GridDevExtremeDataSource loads data asynchronously in small portions (instead of the entire dataset). Call the WaitForRemoteSourceRowLoadAsync(Int32) method to ensure that the specified data row is loaded. For instance, call this method before methods that accept a row’s visible index as a parameter (SelectRow, StartEditRowAsync, and so on).
The Grid allows you to add unbound columns whose values are not stored in the assigned data collection. You can calculate column values in two ways:
For additional information and examples, refer to the following topic: Create an Unbound Column.