Bind Blazor Grid to Data
- 17 minutes to read
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.
Synchronous Data Binding
You can bind the Grid to a data collection available during synchronous component initialization.
- Bind the Data parameter to a C# field or property.
- Populate this field or property with data in the OnInitialized lifecycle method.
@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();
}
}
Asynchronous Data Binding
The Grid can also use a data collection that becomes available after asynchronous component initialization.
- Bind the Data parameter to a C# field or property.
- Populate this field or property with data in the OnInitializedAsync lifecycle method (use the await operator).
@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();
}
}
Observable Data Collections
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)
});
}
}
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.
Large Data (Server Mode Sources)
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.
Data Source Types
The Grid includes two types of Server Mode data sources:
- Server Mode Data Source
- A synchronous data source that locks the UI and does not respond to user actions while data is retrieved.
- Instant Feedback Data Source
- An 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 |
---|---|---|
How to Use Server Mode
Follow these steps to use a Server Mode data source in the Grid:
- Add a reference to a data source’s namespace (for instance, DevExpress.Data.Linq for EF Core data sources).
- Create a data source instance and specify its parameters. For more information about data sources and their settings, refer to the topics in the table above.
- Bind the data source instance to the Grid’s Data parameter.
- Implement the IDisposable interface on the Razor page (use the @implements directive). Within the page’s Dispose method, check the data source instance for
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:
- KeyExpression
- Identifies 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.
- QueryableSource
- Defines a queryable data source from an EF Core data context (for instance, DbSet<T>).
For more information on how to use EF Core, refer to the following topic: Bind Components to Data with Entity Framework Core.
@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();
}
}
Common Specifics and Limitations
The Grid imposes the following specifics and limitations when you use Server Mode data sources:
- Server Mode data sources do not work in Blazor WebAssembly applications.
- Custom sorting is not supported.
- Grouping by display text is not supported.
- Custom grouping is not supported.
- Custom summary calculation is limited. The CustomSummary event fires only once when the SummaryStage event argument is set to
Finalize
. - Unbound columns are not supported if their values are populated in the UnboundColumnData event handler. You can use unbound columns whose values are calculated based on the UnboundExpression.
- Search, filter, and sorting by display text is not supported. The Grid searches, filters, and sorts data by cell values.
- A Server Mode data source can be reloaded automatically when it requests a portion of data from a database and finds an inconsistency (for instance, if an external process made changes to the database).
- Actions that affect all Grid rows (for instance, expand all groups) can cause performance issues and UI freezes.
- To call the SetFocusedDataItemAsync(Object) method, specify the KeyFieldName or KeyFieldNames property value.
AllPages
mode of the Select All checkbox is not supported.- The second call to the SelectAllAsync(Boolean) or DeselectAllAsync() method cancels the operation initiated by the previously called method.
- Sorting, filtering, or grouping data during the select all or deselect all operation processing, cancels the operation.
Instant Feedback Source Specifics
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.
Large Data (Queryable Collections)
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.
Local Queryable Collections
Follow the steps below to bind the Grid to a large IQueryable data collection stored locally:
- Create a
GridDevExtremeDataSource
class instance and pass yourIQueryable<T>
data collection as the constructor parameter. - Assign this instance to the Grid’s Data property.
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();
}
}
Queryable Collections as HTTP Services
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:- An HttpClient object that sends HTTP requests and receives HTTP responses from the specified URL.
- A URL to the service’s controller action that processes HTTP requests (see step 3).
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.
@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);
}
}
Custom HTTP Requests
You can also generate and send custom HTTP requests:
Implement an asynchronous function that returns a
Task<Stream>
object and accepts two parameters:- A URL to the service’s controller action.
- A CancellationToken object that propagates a cancellation notification.
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();
}
}
Limitations
The GridDevExtremeDataSource
imposes the following limitations on Grid features that are available when you assign a data collection to the Data property directly:
- Custom sorting is not supported.
- Interval grouping, custom grouping, and grouping by display text are not supported.
- Custom summary calculation is not supported.
- Unbound columns are not supported.
- Criteria operators applied by the SetFieldFilterCriteria(String, CriteriaOperator) or SetFilterCriteria(CriteriaOperator) methods are limited. For more information, see method descriptions.
- To call the SetFocusedDataItemAsync(Object) method, you should specify the KeyFieldName or KeyFieldNames property value.
- Search, filter, and sorting by display text is not supported. When you use the
GridDevExtremeDataSource
, the Grid searches, filters, and sorts data by cell values. AllPages
mode of the Select All checkbox is not supported.- The SelectAllAsync(Boolean) and DeselectAllAsync() methods load all data to the grid and can reduce overall performance and increase memory consumption.
- The second call to the SelectAllAsync(Boolean) or DeselectAllAsync() method cancels the operation initiated by the previously called method.
- Sorting, filtering, or grouping data during the select all or deselect all operation processing cancels the operation.
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).
Unbound Columns
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:
- Specify the UnboundExpression property that calculates unbound column values. An expression might consist of field names, constants, operators, and functions.
- Handle the UnboundColumnData event to supply column values based on custom logic or obtain column values from a custom/external data source.
For more information and examples, refer to the following topic: Create an Unbound Column.