Skip to main content
All docs
V24.1

Bind Blazor TreeList to Data

  • 22 minutes to read

This document describes how to bind the Blazor TreeList component to data in different scenarios.

Flat Data

You can bind the TreeList component to a flat data source that implements the IEnumerable<T> or IListSource interface. To build a hierarchical structure of TreeList nodes, specify additional properties that define node relationships:

KeyFieldName
A field name that contains node unique identifiers.
ParentKeyFieldName
A field name that contains a node’s parent identifier. This field’s value is 0 or null for root nodes.

Follow the steps below to bind the TreeList component to a data source that contains flat data:

  1. Bind the Data parameter to a C# property.
  2. Populate this property with data in the OnInitialized or OnInitializedAsync lifecycle method.
  3. Assign names of data source fields to the KeyFieldName and ParentKeyFieldName properties.

The following example binds DxTreeList to a flat data source:

@inject EmployeeTaskService EmployeeTaskService

<DxTreeList Data="TreeListData" KeyFieldName="Id" ParentKeyFieldName="ParentId">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" Caption="Task" />
        <DxTreeListDataColumn FieldName="EmployeeName" />
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
    </Columns>
</DxTreeList>

@code {
    List<EmployeeTask> TreeListData { get; set; }

    protected override void OnInitialized() {
        TreeListData = EmployeeTaskService.GenerateData();
    }
}

Bind Blazor TreeList to Flat Data

Run Demo: TreeList - Flat Data

Hierarchical Data

You can bind the TreeList component to hierarchical data structures that implement the IEnumerable<T> interface.

Specify Node Children Declaratively

If each node in your data source includes a field with a list of child nodes, follow the steps below:

  1. Bind the Data parameter to a C# property.
  2. Populate this property with data in the OnInitialized or OnInitializedAsync lifecycle method.
  3. Assign the field name that stores children to the ChildrenFieldName property.

The following example binds the TreeList component to the SpaceObject collection. An object’s Satellites property stores child items.

@inject SpaceObjectDataProvider SpaceObjectDataProvider

<DxTreeList Data="TreeListData" ChildrenFieldName="Satellites">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" />
        <DxTreeListDataColumn FieldName="TypeOfObject" Caption="Type" />
        <DxTreeListDataColumn FieldName="Mass10pow21kg" Caption="Mass, kg" DisplayFormat="N2">
            <HeaderCaptionTemplate>Mass, 10<sup>21</sup> &#215; kg</HeaderCaptionTemplate>
        </DxTreeListDataColumn>
        <DxTreeListDataColumn FieldName="MeanRadiusInKM" Caption="Radius, km" DisplayFormat="N2"/>
        <DxTreeListDataColumn FieldName="Volume10pow9KM3" DisplayFormat="N2">
            <HeaderCaptionTemplate>Volume, 10<sup>9</sup> &#215; km<sup>3</sup></HeaderCaptionTemplate>
        </DxTreeListDataColumn>
        <DxTreeListDataColumn FieldName="SurfaceGravity" DisplayFormat="N2">
            <HeaderCaptionTemplate>Gravity, m/s<sup>2</sup></HeaderCaptionTemplate>
        </DxTreeListDataColumn>
    </Columns>
</DxTreeList>

@code {
    object TreeListData { get; set; }

    protected override async Task OnInitializedAsync() {
        TreeListData = SpaceObjectDataProvider.GenerateData();
    }
}

Bind Blazor TreeList to Hierarchical Data

Run Demo: TreeList - Hierarchical Data

Load Children in Code

If your data source does not include a field that stores child nodes, you can populate the TreeList component with data during component initialization. Follow the steps below to populate the component with data in code:

  1. Bind the Data parameter to a C# property.
  2. Populate this property with root data items in the OnInitialized or OnInitializedAsync lifecycle method.
  3. Handle the ChildrenLoading event. In the event handler, use the Parent event argument to determine the processed node and assign the node’s children to the Children collection.

Note

The TreeList component loads all data during the component initialization when you handle the ChildrenLoading event. Instead of this event, you can handle the ChildrenLoadingOnDemand event to load node children when the node is expanded for the first time. Refer to the following section for more information: Load Data on Demand.

The following example populates the TreeList component with data during the component initialization:

@inject SpaceObjectDataProvider SpaceObjectDataProvider

<DxTreeList Data="TreeListData" ChildrenLoading="OnChildrenLoading">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" />
        <DxTreeListDataColumn FieldName="TypeOfObject" Caption="Type" />
        <DxTreeListDataColumn FieldName="Mass10pow21kg" Caption="Mass, kg" DisplayFormat="N2">
            <HeaderCaptionTemplate>Mass, 10<sup>21</sup> &#215; kg</HeaderCaptionTemplate>
        </DxTreeListDataColumn>
        <DxTreeListDataColumn FieldName="MeanRadiusInKM" Caption="Radius, km" DisplayFormat="N2"/>
        <DxTreeListDataColumn FieldName="Volume10pow9KM3" DisplayFormat="N2">
            <HeaderCaptionTemplate>Volume, 10<sup>9</sup> &#215; km<sup>3</sup></HeaderCaptionTemplate>
        </DxTreeListDataColumn>
        <DxTreeListDataColumn FieldName="SurfaceGravity" DisplayFormat="N2">
            <HeaderCaptionTemplate>Gravity, m/s<sup>2</sup></HeaderCaptionTemplate>
        </DxTreeListDataColumn>
    </Columns>
</DxTreeList>

@code {
    object TreeListData { get; set; }

    protected override async Task OnInitializedAsync() {
        TreeListData = SpaceObjectDataProvider.GenerateRootData();
    }

    void OnChildrenLoading(TreeListChildrenLoadingEventArgs e) {
        e.Children = SpaceObjectDataProvider.GetChildren((SpaceObject)e.Parent);
    }
}

Observable Data Collections

You can bind the Blazor TreeList 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 TreeList about relevant changes (when items are added or removed, when the entire collection is refreshed, and so on). The TreeList listens to these notifications and updates its data automatically.

To enable automatic data updates for individual cells, the item object should also implement the INotifyPropertyChanged interface.

Note

After you bind a dynamic data collection to the TreeList component, the collection sends notifications after each change. When you make a sequence of modifications (for instance, in a for loop), the TreeList is re-rendered on each notification.

Based on the collection’s structure, follow the steps described in the Flat Data or Hierarchical Data section to bind the TreeList component to this collection. The following code sample binds the component to an ObservableCollection that has a flat structure. The Update Progress button changes Status field values:

@using Observable.Services
@using System.Collections.ObjectModel
@inject EmployeeTaskService EmployeeTaskService

<DxTreeList Data="TreeListData" KeyFieldName="Id" ParentKeyFieldName="ParentId">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" Caption="Task" />
        <DxTreeListDataColumn FieldName="EmployeeName" />
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
        <DxTreeListDataColumn FieldName="Status" Caption="Progress" DisplayFormat="p0" />
    </Columns>
</DxTreeList>

<DxButton Click="OnClick" Text="Update Progress" />

@code {
    ObservableCollection<EmployeeTask> TreeListData { get; set; }
    Random fixRand;

    protected override void OnInitialized() {
        TreeListData = new ObservableCollection<EmployeeTask>(EmployeeTaskService.GenerateData());
        fixRand = new Random();
    }

    void OnClick() {
        foreach (EmployeeTask task in TreeListData) {
            if(task.Status != 1) {
                double change = fixRand.NextDouble();
                task.Status = (task.Status + change > 1) ? 1 : task.Status + change;
            }
        }
    }
}

Bind Blazor TreeList to an Observable Collection

Run Demo: TreeList - Observable Data Collections

Server-Side Data

You can use the GridDevExtremeDataSource<T> to bind the TreeList to a large flat dataset that implements the IQueryable interface. To build a hierarchical structure of TreeList nodes, specify additional properties that define node relationship:

KeyFieldName
A field name that contains node unique identifiers.
ParentKeyFieldName
A field name that contains a node’s parent identifier. This field’s value is 0 or null for root nodes.
HasChildrenFieldName
A field name that returns whether a node has children. The component uses this property to determine which nodes require expand buttons.

When you use this data source, the TreeList delegates data filtering operations to an underlying query provider (such as LINQ to Objects, EF Core, and so on). The TreeList initially loads only root nodes and loads a node’s children when the node is expanded for the first time. This technique optimizes overall performance 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 TreeList to a large flat dataset stored locally:

  1. Create a GridDevExtremeDataSource instance and pass your IQueryable data collection as the constructor parameter.
  2. Assign this instance to the TreeList’s Data property in the OnInitialized or OnInitializedAsync lifecycle method.
  3. Specify KeyFieldName, ParentKeyFieldName, and HasChildrenFieldName properties.

The following code sample example uses the Entity Framework Core data access technology to bind the TreeList component to an IQueryable<T> data collection:

@inject CitiesService CitiesService

<DxTreeList Data="@Data"
            KeyFieldName="ID" 
            ParentKeyFieldName="ParentID" 
            HasChildrenFieldName="HasChildren">
    <Columns>
        <DxTreeListDataColumn Caption="Location" FieldName="Name" />
        <DxTreeListDataColumn FieldName="CityType" />
        <DxTreeListDataColumn FieldName="Year" DisplayFormat="d"/>
        <DxTreeListDataColumn FieldName="RecordType" />
        <DxTreeListDataColumn FieldName="Population" />
    </Columns>
</DxTreeList> 

@code {
    object Data { get; set; }

    protected override async Task OnInitializedAsync() {
        var cities = await CitiesService.GetCitiesAsync();
        Data = new GridDevExtremeDataSource<Location>(cities.AsQueryable());
    }
}

Bind Blazor TreeList to Server-Side Data

Run Demo: TreeList - Large Dataset View Example: Bind Blazor TreeList to DevExtreme data source with Entity Framework Core

Queryable Collections as HTTP Services

Follow these steps to bind the TreeList to a large data collection published as an HTTP service:

  1. 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).
  2. Assign the GridDevExtremeDataSource class instance to the TreeList’s Data property in the OnInitialized or OnInitializedAsync lifecycle method.

  3. 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.
  4. Specify KeyFieldName, ParentKeyFieldName, and HasChildrenFieldName properties.
@inject HttpClient HttpClient

<DxTreeList Data="Data"
            KeyFieldName="Id"
            ParentKeyFieldName="ParentId"
            HasChildrenFieldName="HasChildren">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" Caption="Task"/>
        <DxTreeListDataColumn FieldName="EmployeeName" Caption="Assigned To"/>
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
        <DxTreeListDataColumn FieldName="Priority" />
        <DxTreeListDataColumn Caption="Progress" FieldName="Status" />
    </Columns>
</DxTreeList>

@code {
    object Data { get; set; }

    protected override async Task OnInitializedAsync() {
        var uri = new Uri("https://js.devexpress.com/Demos/NetCore/api/TreeListWebApi/EmployeeTasks");
        Data = new GridDevExtremeDataSource<EmployeeTask>(HttpClient, uri);
    }
}

Run Demo: TreeList - Large Dataset via HTTP Service

Custom HTTP Requests

You can also generate and send custom HTTP requests:

  1. 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.
  2. Create a GridDevExtremeDataSource class instance and pass two constructor parameters (the newly created function and the URL to the service’s controller action).

  3. Assign the GridDevExtremeDataSource instance to the TreeList’s Data property in the OnInitialized or OnInitializedAsync lifecycle method.
  4. Specify KeyFieldName, ParentKeyFieldName, and HasChildrenFieldName properties.

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/TreeListWebApi/EmployeeTasks");
        Data = new GridDevExtremeBasedDataSource<EmployeeTask>(ExecuteDataSourceHttpRequest, uri);
    }

    async Task<Stream> ExecuteDataSourceHttpRequest(Uri uri, CancellationToken cancellationToken) {
        using var request = new HttpRequestMessage(HttpMethod.Get, uri);
        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 TreeList features:

Load Data on Demand

DevExpress Blazor TreeList can initially load only root nodes and retrieve the node’s children when the node is expanded for the first time. This technique optimizes performance and reduces overall memory consumption when bound to a large dataset. Follow the steps below to enable on demand mode in the TreeList component:

  1. Bind the Data parameter to a C# property.
  2. Populate this property with root data items in the OnInitialized or OnInitializedAsync lifecycle method.
  3. Specify the HasChildrenFieldName property. The component uses this property to determine which nodes require expand buttons.
  4. Handle the ChildrenLoadingOnDemand event. In the event handler, use the Parent event argument to determine the processed node and assign the node’s children to the Children collection.

In the following example, the TreeList component displays the file structure:

@inject IFileSystemDataProvider FileSystemDataProvider

<DxTreeList Data="TreeListData"
            HasChildrenFieldName="HasChildren"
            ChildrenLoadingOnDemand="TreeList_ChildrenLoadingOnDemand"
            CustomizeCellDisplayText="TreeList_CustomizeCellDisplayText">
    <Columns>
        <DxTreeListDataColumn FieldName="Name" />
        <DxTreeListDataColumn FieldName="Type" Width="100" />
        <DxTreeListDataColumn FieldName="DateModified" Width="120" />
        <DxTreeListDataColumn FieldName="Size" Width="100" />
    </Columns>
</DxTreeList>

@code {
    object TreeListData { get; set; }

    protected override async Task OnInitializedAsync() {
        TreeListData = await FileSystemDataProvider.GetRootItemsAsync();
    }

    Task TreeList_ChildrenLoadingOnDemand(TreeListChildrenLoadingOnDemandEventArgs e) {
        var item = e.Parent as FileSystemDataItem;
        e.Children = item.Children;
        return Task.CompletedTask;
    }

    void TreeList_CustomizeCellDisplayText(TreeListCustomizeCellDisplayTextEventArgs e) {
        if(e.FieldName == "Size") {
            var item = (FileSystemDataItem)e.DataItem;
            e.DisplayText = GetSizeColumnDisplayText(item.Type, item.Size);
        }
    }

    string GetSizeColumnDisplayText(FileType fileType, long size) {
        if(fileType == FileType.Folder)
            return null;
        if(size >= 1024)
            return string.Format("{0:### ### ###} KB", size / 1024);
        return string.Format("{0} Bytes", size);
    }
}

Load Blazor TreeList Data on Demand

Run Demo: TreeList - Load on Demand

Limitations

The on demand data loading mode has the following specifics and limitations: