In this post, we will explore the Telerik TreeView control for Blazor, which allows you to display hierarchies very easily in your Blazor-based applications, such as file explorers, organizational hierarchies, etc. Let’s get started!
The Blazor TreeView control from Progress Telerik UI for Blazor lets you display data in a hierarchical tree-like structure. The data we add to the control can be flat or hierarchical, and it is also possible to navigate through the items and their children. Let’s see how to integrate it into a Blazor project.
The first step to use the TreeView component is to prepare your application by following the Telerik Blazor component installation guide. In my case, I am using the Blazor Web App template with an Interactive render mode set to Server and an Interactivity location set to Global.
Let’s assume we are creating an application to read ebooks, and we need to display a hierarchy like Chapter | Section | Page. To solve this, I have created a new page-type component to implement the book hierarchy, starting with the addition of the following code:
@page "/book-treeview"
<PageTitle>TreeView Demo</PageTitle>
<div>
<div class="row">
<div class="col-md-4">
<TelerikCard ThemeColor="light">
<CardHeader>
<h3>Table of Contents</h3>
</CardHeader>
<CardBody>
<TelerikTreeView>
</TelerikTreeView>
</CardBody>
</TelerikCard>
</div>
<div class="col-md-8">
</div>
</div>
</div>
In the code above, you can see I have implemented the TelerikTreeView
component, but if you run the application now, it won’t be displayed because it is still empty. We will resolve this in the next step.
The TreeView control supports two types of parent-child relationships. The first is called Flat Data, which means that the complete collection of TreeView items is available at a single level, e.g., List<BookItem>
. The second type is called Hierarchical Data, where it is expected that a property provided by the parent item, usually named Items
, exists. This property can also be assigned using the ItemsField
property.
For our practical example, we will choose the Flat Data approach by defining the BookItem
class, which will allow us to represent a specific chapter, section or page. If you want to follow the example, it is important to name the properties as indicated because their significance will be explained later:
public class BookItem
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public int? ParentId { get; set; }
public bool HasChildren { get; set; }
public ISvgIcon Icon { get; set; } = SvgIcon.Book;
public string Description { get; set; } = string.Empty;
public int PageNumber { get; set; }
}
Once the data model is ready, we can bind it using the Data
property. So that this property has data when the application starts, I have opted to create a service that simulates retrieving data from a database:
public class BookService
{
public List<BookItem> GetBookStructure()
{
List<BookItem> bookStructure =
[
new BookItem
{
Id = 1,
Title = "Chapter 1: Introduction",
ParentId = null,
HasChildren = true,
Icon = SvgIcon.Book,
Description = "An introduction to the main concepts of the book.",
PageNumber = 1
},
new BookItem
{
Id = 2,
Title = "Chapter 2: Core Concepts",
ParentId = null,
HasChildren = true,
Icon = SvgIcon.Book,
Description = "Exploring the fundamental concepts and theories.",
PageNumber = 15
},
...
];
return bookStructure;
}
}
You can add this service to your application’s service collection in Program.cs
.
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddScoped<BookService>();
var app = builder.Build();
...
}
}
Next, we inject a reference to the service and populate the Data
property of the TreeView component by creating and initializing a property called BookItems
using the created service:
@page "/book-treeview"
@using BlazorTreeViewTests.Models
@using BlazorTreeViewTests.Services
@inject BookService BookService
<PageTitle>TreeView Demo</PageTitle>
<div>
<div class="row">
<div class="col-md-4">
<TelerikCard ThemeColor="light">
<CardHeader>
<h3>Table of Contents</h3>
</CardHeader>
<CardBody>
<TelerikTreeView Data="@BookItems">
</TelerikTreeView>
</CardBody>
</TelerikCard>
</div>
<div class="col-md-8">
</div>
</div>
</div>
@code{
private List<BookItem> BookItems { get; set; } = new();
protected override void OnInitialized()
{
BookItems = BookService.GetBookStructure();
}
}
With the above code, you can see that the TreeView component is displayed in the graphical interface:
At this point, you might be wondering why the application is displaying the tree icons but not its content. To understand this, we need to learn about TreeViewBindings, which we will discuss next.
Something important to know when working with the TreeView component is that it has a set of TreeViewBinding
tags that enable model property mapping. These have certain default values, which are as follows:
Id
: A unique identifier, required for mapping flat data. Corresponds to the IdField
TreeViewBinding with a default value of Id
.Text
: Plain text content of the item. Corresponds to the TextField
TreeViewBinding with a default value of Text
.HasChildren
: Indicates whether the item has children, required for binding to flat data. Corresponds to the HasChildrenField
TreeViewBinding with a default value of HasChildren
.ParentId
: Identifies the parent of the item, required for binding to flat data. It should be null for root elements. Corresponds to the ParendIdField
TreeViewBinding with a default value of ParentId
.Icon
: Defines a TelerikFont
and SvgIcon
. Corresponds to the IconField
TreeViewBinding with a default value of Icon
.This means that if you’re mapping models where the property names match the default values, the binding will be automatic. In our current example, the project works “partially,” because most of the properties in the model align with the TreeViewBindings’ default values except for Text
. If we want everything to work correctly, we need to explicitly define the TreeViewBinding
TextField
to point to the Title
property as follows:
<TelerikTreeView Data="@BookItems">
<TreeViewBindings>
<TreeViewBinding TextField="Title"
ParentIdField="ParentId"
HasChildrenField="HasChildren"
IconField="Icon" />
</TreeViewBindings>
</TelerikTreeView>
The change above results in the correct display of the control:
Another feature of the Telerik TreeView component is that it allows for a read-only mode, single selection or multiple selection. This can be specified using the SelectionMode
property with the values None
, Single
or Multiple
. Since, in our example, we want to display one book page at a time, we will specify the value Single
as shown below:
<TelerikTreeView Data="@BookItems"
SelectionMode="@TreeViewSelectionMode.Single">
...
</TelerikTreeView>
Once we have defined how to select TreeView items, we can retrieve these items using the SelectedItems
property, performing the appropriate cast to the correct model type. In our example, we can use the retrieved information to display the content of the selected page, along with its child sections if they exist, as shown below:
@page "/book-treeview"
@using BlazorTreeViewTests.Models
@using BlazorTreeViewTests.Services
@inject BookService BookService
<PageTitle>TreeView Demo</PageTitle>
<div>
<div class="row">
<div class="col-md-4">
<TelerikCard ThemeColor="light">
<CardHeader>
<h3>Table of Contents</h3>
</CardHeader>
<CardBody>
<TelerikTreeView Data="@BookItems"
SelectionMode="@TreeViewSelectionMode.Single"
@bind-SelectedItems="@SelectedItems"
OnItemClick="@OnItemClick">
<TreeViewBindings>
<TreeViewBinding TextField="Title"
ParentIdField="ParentId"
HasChildrenField="HasChildren"
IconField="Icon" />
</TreeViewBindings>
</TelerikTreeView>
</CardBody>
</TelerikCard>
</div>
<div class="col-md-8">
<TelerikCard>
<CardHeader>
<h3>@(SelectedItem == null ? "Select a chapter or section" : SelectedItem.Title)</h3>
</CardHeader>
<CardBody>
@if (SelectedItem != null)
{
<div class="selected-content">
<p><strong>Page Number:</strong> @SelectedItem.PageNumber</p>
<p><strong>Description:</strong> @SelectedItem.Description</p>
@if (ChildItems.Any())
{
<div class="subsections mt-4">
<h4>Subsections:</h4>
<ul class="list-group">
@foreach (var child in ChildItems)
{
<li class="list-group-item d-flex align-items-center">
<TelerikSvgIcon Icon="@child.Icon" />
<span class="ml-2">@child.Title (page @child.PageNumber)</span>
</li>
}
</ul>
</div>
}
</div>
}
else
{
<div class="text-center text-muted p-5">
<p>Select a chapter or section from the tree view to see its details.</p>
</div>
}
</CardBody>
</TelerikCard>
</div>
</div>
</div>
@code{
private List<BookItem> BookItems { get; set; } = new();
private IEnumerable<object> SelectedItems { get; set; } = new List<BookItem>();
private BookItem? SelectedItem => SelectedItems.FirstOrDefault() as BookItem;
private List<BookItem> ChildItems { get; set; } = new();
protected override void OnInitialized()
{
BookItems = BookService.GetBookStructure();
}
private void OnItemClick(TreeViewItemClickEventArgs args)
{
var selectedItem = args.Item as BookItem;
if (selectedItem != null)
{
ChildItems = BookItems.Where(x => x.ParentId == selectedItem.Id).ToList();
}
}
}
In the code above, we bind the selected elements to @bind-SelectedItems
, which in turn updates the SelectedChapter
property with the selected item from the tree. This finally populates the book’s page content section. Similarly, the OnItemClick
event is used to get the child elements if they exist, populating the ChildItems
property. The final result is as follows:
The result is simply spectacular.
In the previous section, we demonstrated how to select a tree item by expanding its content. Let’s improve the functionality further by making the book chapters expand automatically when launching the application. We can achieve this by using the ExpandedItems
parameter, which needs to be bound to a list containing the items we want to expand. Below is the implementation in our application:
@page "/book-treeview"
@using BlazorTreeViewTests.Models
@using BlazorTreeViewTests.Services
@inject BookService BookService
<PageTitle>TreeView Demo</PageTitle>
<div>
<div class="row">
<div class="col-md-4">
<TelerikCard ThemeColor="light">
<CardHeader>
<h3>Table of Contents</h3>
</CardHeader>
<CardBody>
<TelerikTreeView Data="@BookItems"
SelectionMode="@TreeViewSelectionMode.Single"
@bind-SelectedItems="@SelectedItems"
@bind-ExpandedItems="@ExpandedItems"
OnItemClick="@OnItemClick">
<TreeViewBindings>
<TreeViewBinding TextField="Title"
ParentIdField="ParentId"
HasChildrenField="HasChildren"
IconField="Icon" />
</TreeViewBindings>
</TelerikTreeView>
</CardBody>
</TelerikCard>
</div>
<div class="col-md-8">
...
</div>
</div>
</div>
@code{
...
private IEnumerable<object> ExpandedItems { get; set; } = new List<BookItem>();
protected override void OnInitialized()
{
BookItems = BookService.GetBookStructure();
ExpandedItems = BookItems.Where(x => x.ParentId == null).ToList();
}
...
}
In the code above, the ExpandedItems
property is created, and it is populated during application initialization with all nodes that do not have a parent node, that is, the root nodes. Similarly, this property is bound to the @bind-ExpandedItems
parameter of the TreeView, which results in the following behavior when the application starts:
This enhances the user experience by allowing quicker navigation among the book’s chapters.
Throughout this post, you’ve seen the power of the Telerik TreeView component for Blazor. We explored how to integrate the component into a Blazor application and configure it to load data in a tree format. Now it’s your turn to easily create hierarchies using the Blazor TreeView component. Telerik UI for Blazor comes with a free 30-day trial!
Héctor Pérez is a Microsoft MVP with more than 10 years of experience in software development. He is an independent consultant, working with business and government clients to achieve their goals. Additionally, he is an author of books and an instructor at El Camino Dev and Devs School.