New to Telerik UI for Blazor? Download free 30-day trial

Databinding with the OnRead Event

This article presents the OnRead event and describes how to use it to data bind Telerik Blazor components.

Purpose and Benefits

The easiest way to provide data to a component is to set its Data parameter to IEnumerable<T>. This allows the component to have all data items and to perform all data operations internally (filtering, paging, sorting, etc.). However, this scenario is not always the most optimal.

There are two main reasons to use the OnRead event: performance and customization.

Performance

Large amounts of data require loading in chunks and on demand. This improves the performance of the database, backend, network, and the browser. When a component fires OnRead, it expects to receive only the data items to render. The exact number depends on the component's PageSize parameter.

OnRead also offloads data operations outside the component, for example on the remote server. This can improve WebAssembly application performance.

Customization

OnRead allows full control over the data operations. For example, it is possible to use custom sorting and filtering algorithms, if the built-in ones do not fit a given scenario. Here are just a few examples, but there are many more possible scenarios:

OnRead enables data binding to OData services.

OnRead also allows the application to know the exact data items, which the user is currently seeing.

Components with OnRead Event

The following Blazor components expose an OnRead event. To gain performance benefits, use the event together with paging or virtualization (also called virtual scrolling).

Each component name points to component-specific OnRead documentation and examples:

Component Supports Paging Supports Virtualization
AutoComplete - AutoComplete virtualization
ComboBox - ComboBox virtualization
DropDownList - DropDownList virtualization
Grid Grid paging Grid row virtualization
ListView ListView paging -
MultiSelect - MultiSelect virtualization

Components like the TreeList and the TreeView don't have an OnRead event. Instead, they load data on demand via OnExpand events.

Event Argument

The OnRead event handler receives an argument, which inherits from ReadEventArgs. The exact type depends on the component. For example, the Grid handler receives GridReadEventArgs. The ComboBox handler receives ComboBoxReadEventArgs, and so on.

The following properties of the event argument object are common for all components with an OnRead event. Other properties are discussed in component-specific articles.

Property Type Description
Request DataSourceRequest This object carries information about the requested data items. It will reveal the page index or virtual scroll offset, the sorting and filtering state, etc.
Data IEnumerable Set it to the chunk of data items, which the component will render.
Total int Set it to the total number of items. This value will help the component generate its pager or virtual scrollbar correctly.

Using DataSourceRequest properties

async Task GridReadHandler(GridReadEventArgs args)
{
    // What is the new page?
    // args.Request.Page

    // What is the page size (how many items to return)?
    // args.Request.PageSize

    // How many rows the user has scrolled in virtual scenarios?
    // args.Request.Skip
}

The DataSourceRequest object can be serialized and sent to the remote server. Use the System.Text.Json serializer.

ToDataSourceResult Method

The DataSourceRequest object provides information about the needed data. The question is how to retrieve this data most easily. Sometimes OnRead data binding is called "manual", but in most cases it doesn't have to be manual at all. The solution is ToDataSourceResult.

The ToDataSourceResult extension method is able to extract the requested data items from IEnumerable, IQueryable and DataTable. The method is part of the Telerik DataSource Extensions. It expects a DataSourceRequest argument.

ToDataSourceResult returns a DataSourceResult object. Its most important properties are:

Property Type Description
Data IEnumerable The chunk (page) of data items to render. All data operations are already applied (sorting, filtering, etc.)
Total int The total number of items in the datasource.

The Data and Total properties of the DataSourceRequest and DataSourceResult match, and allow easy value assignment:

Using ToDataSourceResult

IEnumerable<GridModel> AllGridData { get; set; }

async Task GridReadHandler(GridReadEventArgs args)
{
    DataSourceResult result = AllGridData.ToDataSourceResult(args.Request);

    args.Data = result.Data;
    args.Total = result.Total;
    args.AggregateResults = result.AggregateResults; // Grid only
}

ToDataSourceResultAsync is the awaitable (asynchronous) alternative of ToDataSourceResult.

It is possible to use DataSourceRequest, ToDataSourceResult and ToDataSourceResultAsync in scenarios, which are not related to a specific Telerik component.

Example

Let's imagine that our datasource contains 1,000 items, and we want to send only one page of items to a Grid.

  1. Import the Telerik.DataSource.Extensions namespace.
  2. Set the Grid's TItem parameter to the model type. (Some components require a TValue parameter to define the value type, but not the Grid. Use TValue with the AutoComplete, ComboBox, DropDownList, and MultiSelect.)
  3. Subscribe to the OnRead event. Use async Task and not async void. The event handler receives a GridReadEventArgs argument. Let's name it args.
  4. Use args.Request and ToDataSourceResult() to get one page of Grid data. The data may be filtered and sorted, based on the user's actions.
  5. Set args.Data to the data items to render.
  6. Set args.Total to the total number of data items (1000).

Using the OnRead event

@using Telerik.DataSource.Extensions

<TelerikGrid TItem="@SampleModel"
             OnRead="@OnGridRead"
             AutoGenerateColumns="true"
             Sortable="true"
             Pageable="true"
             FilterMode="@GridFilterMode.FilterRow"
             Height="400px" />

<p> OnGridRead fired at: @LastOnRead </p>

@code {
    private List<SampleModel> GridData { get; set; }

    private string LastOnRead { get; set; }

    // always use async Task, and not async void
    private async Task OnGridRead(GridReadEventArgs args)
    {
        var result = GridData.ToDataSourceResult(args.Request);
        args.Data = result.Data;
        args.Total = result.Total;

        var now = DateTime.Now;
        LastOnRead = now.ToLongTimeString() + "." + now.Millisecond;
    }

    protected override void OnInitialized()
    {
        GenerateData();

        base.OnInitialized();
    }

    private void GenerateData()
    {
        GridData = new List<SampleModel>();

        for (int i = 1; i <= 1000; i++)
        {
            GridData.Add(new SampleModel() { Id = i, Text = $"Grid Text {i}" });
        }
    }

    public class SampleModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }
}

Refresh Data

The components fire an OnRead event when the user performs an action, such as paging, sorting, virtual scrolling, etc. Calling the OnRead handler manually will not have effect, because the component will not be tracking the method arguments.

All components with an OnRead event have a Rebind method as well. To refresh the component data programmatically, call this method. It will force the component to fire OnRead and receive new data.

Rebind DropDownList and Grid when using OnRead

@using Telerik.DataSource.Extensions

<TelerikDropDownList @ref="@TheDropDown"
                     TItem="@SampleModel"
                     TValue="@int"
                     OnRead="@OnDropDownRead"
                     @bind-Value="@DropDownValue"
                     ValueField="@nameof(SampleModel.Id)"
                     TextField="@nameof(SampleModel.Text)"
                     Width="200px">
    <ValueTemplate>
        @context.Id : @context.Text
    </ValueTemplate>
    <ItemTemplate>
        @context.Id : @context.Text
    </ItemTemplate>
</TelerikDropDownList>

<TelerikButton ThemeColor="@ThemeConstants.Button.ThemeColor.Primary"
               OnClick="@RebindComponents">Rebind Components</TelerikButton>

<br /><br />

<TelerikGrid @ref="@TheGrid"
             TItem="@SampleModel"
             OnRead="@OnGridRead"
             AutoGenerateColumns="true"
             Sortable="true"
             Pageable="true"
             PageSize="5" />

@code {
    TelerikGrid<SampleModel> TheGrid { get; set; }
    TelerikDropDownList<SampleModel, int> TheDropDown { get; set; }

    List<SampleModel> GridData { get; set; }
    List<SampleModel> DropDownData { get; set; }

    int DropDownValue { get; set; } = 1;

    void RebindComponents()
    {
        GenerateData(); // simulate change in the data

        TheGrid.Rebind();
        TheDropDown.Rebind();
    }

    async Task OnGridRead(GridReadEventArgs args)
    {
        var result = GridData.ToDataSourceResult(args.Request);
        args.Data = result.Data;
        args.Total = result.Total;
    }

    async Task OnDropDownRead(DropDownListReadEventArgs args)
    {
        var result = DropDownData.ToDataSourceResult(args.Request);
        args.Data = result.Data;
        args.Total = result.Total;
    }

    protected override void OnInitialized()
    {
        GenerateData();

        base.OnInitialized();
    }

    void GenerateData()
    {
        GridData = new List<SampleModel>();
        DropDownData = new List<SampleModel>();

        var rnd = new Random();

        for (int i = 1; i <= 10; i++)
        {
            GridData.Add(new SampleModel() { Id = i, Text = $"Text {rnd.Next(1, 100)}" });
            DropDownData.Add(new SampleModel() { Id = i, Text = $"Text {rnd.Next(1, 100)}" });
        }
    }

    public class SampleModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }
}

See Also

In this article