Tree (Blazor)
This article demonstrates how to use the Radzen Blazor Tree component. Check also the component guide and API reference.
Using in Radzen
Displaying Categories and Products
We will create a Tree component that displays the items from the Northwind Categories table. Expanding a category tree node will display the products from that category.
- Handle the
Load
event of the page and invoke thegetCategories
data source method. Set a property with Namecategories
and Value${result.Include(c => c.Products)}
. This will eagerly load the Products property of the Category (thus loading all products that belong to a category). - Drag and drop a Tree component. Set its Data property to
${categories}
. - Add an item in the Levels property. Set Schema to
Category
. This sets the type of the root nodes (level 0). Set TextProperty toCategoryName
. This specifies the property which the tree nodes from level 0 will dispay as their text. Set ChildrenProperty toProducts
. This specifies that the nodes from level 0 have child nodes populated from theProducts
property. - Add another item in the Levels property. It will be responsible for the child node configuration (level 1). Set Schema to
Product
, TextProperty toProductName
. A product node does not have children so we also set HasChildren to(product) => false
. This is a lambda function which returnfalse
for any product node.
When you run the application you should see this - a tree with a root node for every Northwind category (Beverages, Condiments etc.). Under every category are listed all products from that category.
Loading data on demand
In the previous example the Products of a category are eagerly loaded via Include(c => c.Products)
. This causes all products of all categories to be loaded in a single database query.
Sometimes it is better to load data only when it is needed - make a database query when the user expands a parent node.
Here is how to adapt the Categories and Products example to use load on demand.
- Modify the Set property action in the
Load
event - set Value to${result}
. If you now run the example and expand a category node a NullReference exception will occur because theProducts
property isnull
. - Delete the second item from the Levels property. Nodes loaded on demand do not use the Levels property and we need to configure their settings in the
Expand
event. Clear the Children property of the first level. - To populate the
Products
property we need to handle theExpand
event of the Tree component. Invoke thegetProducts
data source method. We need to return the products that belong the the category which the expanded node represents. Use the query builder and set a filter for CategoryID equals${(event.Value as Category).CategoryID}
. - In the Then event of the Invoke data source method action use Execute C# with Code
${event.Children.Data} = ${result}
. This assigns the children data of the current node to the result of the data source method. - Add two more
Execute C#
actions:${event.Children.TextProperty} = "ProductName"
and${event.Children.HasChildren} = (product) => false;
. Those two configure the child nodes - specify what property to use for their text and that they don’t have children of their own.
Now when you run the example the tree will load the products on demand - when the user expands a category node.
Sample application
The sample application used for this tutorial is available in the Radzen Github repository.
Using from code
Inline definition
The most straight forward way to define a RadzenTree is to specify its items. Here is an example:
<RadzenTree>
<RadzenTreeItem Text="BMW">
<RadzenTreeItem Text="M3" />
<RadzenTreeItem Text="M5" />
</RadzenTreeItem>
<RadzenTreeItem Text="Audi">
<RadzenTreeItem Text="RS4" />
<RadzenTreeItem Text="RS6" />
</RadzenTreeItem>
<RadzenTreeItem Text="Mercedes">
<RadzenTreeItem Text="C63 AMG" />
<RadzenTreeItem Text="S63 AMG" />
</RadzenTreeItem>
</RadzenTree>
Data-binding
Often you would like to data-bind the RadzenTree to a collection of items. For example a collection of categories that has related products.
public class Category
{
public string CategoryName { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product
{
public string ProductName { get; set; }
}
In this case you need to set the Data
property of the RadzenTree to your collection and add a few RadzenTreeLevel
s that specify how each level of items should be data-bound.
<RadzenTree Data="@Northwind.Categories.Include(c => c.Products)">
<RadzenTreeLevel TextProperty="CategoryName" ChildrenProperty="Products" />
<RadzenTreeLevel TextProperty="ProductName" HasChildren="@((product) => false)" />
</RadzenTree>
The first RadzenTreeLevel
says that the first (root) level of items have their Text
set to the CategoryName
property of the data. They also have children that are data-bound to the Products
property.
The second RadzenTreeLevel
says that the second level of items have their Text
set to the ProductName
property of the data. The don’t have children (specified via HasChildren
).
Note how this example uses the
Include
Entity Framework method to load the related products of a category. If this isn’t done the children items won’t populate.
Load on demand
The previous example loads all tree items instantly. Sometimes you would like to have full control over when children data is loaded. In that case you should use the Expand
event.
<RadzenTree Data="@Northwind.Categories" Expand="@OnExpand">
<RadzenTreeLevel TextProperty="CategoryName"/>
</RadzenTree
@code {
void OnExpand(TreeExpandEventArgs args)
{
var category = args.Value as Category;
args.Children.Data = category.Include(c => c.Products).Products;
args.Children.TextProperty = "ProductName";
args.Children.HasChildren = (product) => false;
}
}
Here we have only one level (for the categories). Children are populated in the OnExpand
method that handles the Expand
event.
To populate items on demand you need to configure the Children
property of the args
- set Data
, TextProperty
and HasChildren
.
Load mixed data
The examples so far dealt with trees that had the same type of node per level - first level was categories and second level was products.
Here is how to have mixed types of nodes per level - files and directories in this case.
<RadzenTree Data="@entries" Expand="@LoadFiles">
<RadzenTreeLevel Text="@GetTextForNode" />
</RadzenTree
@code {
IEnumerable<string> entries = null;
protected override void OnInitialized()
{
entries = Directory.GetDirectories(HostEnvironment.ContentRootPath);
}
string GetTextForNode(object data)
{
return Path.GetFileName((string)data);
}
void LoadFiles(TreeExpandEventArgs args)
{
var directory = args.Value as string;
args.Children.Data = Directory.EnumerateFileSystemEntries(directory);
args.Children.Text = GetTextForNode;
args.Children.HasChildren = (path) => Directory.Exists((string)path);
}
}
Templates
To customize the way a tree item appears (e.g. add icons, images or other custom markup) you can use the Template
option.
Templates in inline definition
Here is an example how to define a tree item template using inline definition.
<RadzenTree>
<RadzenTreeItem Text="BMW">
<Template>
<strong>@context.Text</strong>
</Template>
<ChildContent>
<RadzenTreeItem Text="M3" />
<RadzenTreeItem Text="M5" />
</ChildContent>
</RadzenTreeItem>
</RadzenTree>
The context
is the current RadzenTreeItem
. You can use its properties as you see fit.
Important: Defining children requires a
ChildContent
element when a template is specified.
Templates in data-binding
The RadzenTreeLevel
also supports templates. Here is an example.
<RadzenTree Data="@Northwind.Categories.Include(c => c.Products)">
<RadzenTreeLevel TextProperty="CategoryName" ChildrenProperty="Products">
<Template>
<strong>@(context as Category).CategoryName</strong>
</Template>
</RadzenTreeLevel>
<RadzenTreeLevel TextProperty="ProductName" HasChildren="@((product) => false)" />
</RadzenTree>
Again the current RadzenTreeItem
is represented as the context
variable. Use the Value
property to get the
current data item.
Templates in load on demand
One can specify templates even in load on demand scenarios. The template should either be a custom Blazor component, or you should use a
<RadzenTree Data="@entries" Expand="@LoadFiles">
<RadzenTreeLevel Text="@GetTextForNode" Template="@FileOrFolderTemplate" />
</RadzenTree
@code {
IEnumerable<string> entries = null;
protected override void OnInitialized()
{
entries = Directory.GetDirectories(HostEnvironment.ContentRootPath);
}
string GetTextForNode(object data)
{
return Path.GetFileName((string)data);
}
RenderFragment<RadzenTreeItem> FileOrFolderTemplate = (context) => builder =>
{
string path = context.Value as string;
bool isDirectory = Directory.Exists(path);
// Add a RadzenIcon to the template
builder.OpenComponent<RadzenIcon>(0);
builder.AddAttribute(1, "Icon", isDirectory ? "folder" : "insert_drive_file");
// Set some margin if the current data item is a file (!isDirectory)
if (!isDirectory)
{
builder.AddAttribute(2, "Style", "margin-left: 24px");
}
builder.CloseComponent();
// Append the current item text
builder.AddContent(3, context.Text);
};
void LoadFiles(TreeExpandEventArgs args)
{
var directory = args.Value as string;
args.Children.Data = Directory.EnumerateFileSystemEntries(directory);
args.Children.Text = GetTextForNode;
args.Children.HasChildren = (path) => Directory.Exists((string)path);
// Propagate the Template to the children
args.Children.Template = FileOrFolderTemplate;
}
}
Selection
You can control the selection state of the RadzenTree component via its Value
property. The tree item whose Value property is equal to the specified value will be selected.
IMPORTANT!!! The type of the value you set must be
object
. The RadzenTree component can be bound to items of different type has its Value needs to be anobject
.
<RadzenTree Data="@categories" @bind-Value=@selectedCategory Change=@OnChange>
<RadzenTreeLevel TextProperty="CategoryName"/>
</RadzenTree>
@code {
IEnumerable<Category> categories;
// Not that selectedCategory is object and not Category.
object selectedCategory = null;
protected override void OnInitialized()
{
categories = NorthwindDataContext.Categories;
// Select the first category (if available)
selectedCategory = categories.FirstOrDefault();
}
void OnChange()
{
// The selectedCategory field will contain the Category instance which the selected tree item represents
var categoryName = (selectedCategory as Category).CategoryName;
}
}
To clear the current selection set the Value property to null
.
<RadzenTree Data="@categories" @bind-Value=@selectedCategory Change=@OnChange>
<RadzenTreeLevel TextProperty="CategoryName"/>
<RadzenButton Click=@ClearSelection Text="Clear selection" />
</RadzenTree>
@code {
IEnumerable<Category> categories;
object selectedCategory = null;
void ClearSelection()
{
selectedCategory = null;
}
API
Properties
Name | Type | Default | Description |
---|---|---|---|
Data | IEnumerable | null | Used during data-binding to populate the tree items. |
Value | object | null | Get or set (via @bind-Value) the value of the Tree. This will select the tree item whose Value is equal to the specified value. |
AllowCheckBoxes | boolean | false | Are CheckBoxes allowed. |
AllowCheckChildren | boolean | true | Should check children when parent is checked. |
AllowCheckParents | boolean | true | Should check parents when children are checked. |
CheckedValues | IEnumerable | empty | Collection with checked values. |
<RadzenTree Data="@categories" @bind-Value="@selectedCategory">
</RadzenTree>
@code {
object selectedCategory = null;
}
Events
Name | Argument Type | Description |
---|---|---|
Change | TreeEventArgs | Fired when the user selects a tree item. |
Expand | TreeExpandEventArgs | Fired when the user expands a tree item. |
<RadzenTree Change="@OnChange" Expand="@OnExpand">
</RadzenTree>
@code {
void OnChange(TreeEventArgs args)
{
}
void OnExpand(TreeExpandEventArgs args)
{
}
}
TreeEventArgs properties
Name | Type | Default | Description |
---|---|---|---|
Text | string | string.Empty | The Text of the tree item that triggered the event. |
Value | object | null | The Value of the tree item that triggered the event. |
TreeExpandEventArgs properties
Name | Type | Description |
---|---|---|
Text | string | The Text of the tree item that triggered the event. |
Value | object | The Value of the tree item that triggered the event. |
Children | TreeItemSettings | Various settings that control how the children of the tree item are populated. |
TreeItemSettings properties
Name | Type | Default | Description |
---|---|---|---|
Text | Func<object, string> | null | A function that returns the text of child items depending on the data. |
TextProperty | string | null | Specifies the name of the property which determines the text of the child items. |
Template | RenderFragment |
null | Use to customize the display of the item. |
HasChildren | Func<object, bool> | (data) => false | Set this property to specify if a child item has children or not. |
Data | IEnumerable | null | The data from which children are created. Set this property to provide data for the children of the current tree item. |
RadzenTreeItem Properties
Name | Type | Default | Description |
---|---|---|---|
Text | string | string.Empty | The text displayed by the tree item. |
Value | object | null | The value associated with the tree item. |
Expanded | bool | false | Specifies whether the item is expanded or not. |
RadzenTreeLevel Properties
Name | Type | Default | Description |
---|---|---|---|
Text | Func<object, string> | null | A function that returns the text of child items depending on the data. |
TextProperty | string | null | Specifies the name of the property which determines the text of the child items. |
HasChildren | Func<object, bool> | (data) => true | Set this property to specify if a child item has children or not. |
Template | RenderFragment |
null | Use to customize the display of the items created from this level. |
ChildrenProperty | string | null | Specifies the name of the property which returns child data. The value returned by that property should be IEnumerable |