TL;DR: Enhance the performance of the Syncfusion Blazor TreeView component with the new virtualization feature added in the 2024 volume 2 release. Learn how to implement and benefit from this feature!
Syncfusion Blazor TreeView is a UI component that displays hierarchical data such as a table of contents, code examples, and file directories in a tree-like structure. It is a feature-rich component, supporting data binding, load-on-demand, multiple selection, drag and drop, node editing, checkboxes, templates, and more in both Blazor WebAssembly (WASM) and Blazor Server apps.
The load-on-demand (lazy-load) feature is enabled by default to reduce bandwidth usage when consuming large amounts of data. Only first-level nodes are loaded initially, and then child nodes are loaded when their parent node is expanded.
If a parent node has multiple sub-levels of child nodes, the load-on-demand feature will load only the child nodes of the corresponding parent when expanded. In this scenario, the child nodes of collapsed parent nodes will not be loaded.
However, in cases where a large number of parent nodes are rendered, or a single parent node with many child nodes is rendered, a more efficient solution than the load-on-demand feature is needed. This is where virtualization becomes essential.
Virtualization involves rendering nodes for the current viewport alone and avoiding rendering off-screen items. This technique ensures optimal performance when dealing with huge datasets.
The Blazor TreeView’s UI virtualization dynamically loads the nodes when the user scrolls the container scroller. The virtualization process is intelligently managed based on the Height property of the TreeView component. This support has been included in the latest Essential Studio® 2024 Volume 2 release.
This blog will examine virtualization’s benefits and how to implement it in the Blazor TreeView component.
Let’s explore the benefits that virtualization brings to the Blazor TreeView component!
Dealing with a multitude of DOM elements often leads to performance challenges. Browser engines may struggle to manage large DOM structures effectively, even when a component optimizes data. The challenge lies in ensuring seamless webpage performance while interacting with many DOM elements.
In Blazor TreeView, the UI virtualization feature comes into play when you need to display thousands of nodes in a single TreeView. Initially, the component retrieves the entire data source for the component and renders only the number of nodes that adapt to the current viewport. The rest of the nodes will be loaded on demand through scrolling. Keyboard scrolling is supported as well.
Virtualization also helps us reduce the time taken for the initial rendering of the Blazor TreeView component bound to a large data set, which will speed up the app.
Follow these steps to enable the virtualization feature in the Blazor TreView component:
@using Syncfusion.Blazor.Navigations @using Syncfusion.Blazor.Data <div class="control_wrapper"> @*Initialize the TreeView component*@ <SfTreeView TValue="NodeResult" EnableVirtualization=true Height="400px"> <TreeViewFieldsSettings TValue="NodeResult" Id="Id" Text="Name" ParentID="Pid" HasChildren="HasChild" Expanded="Expanded" Query="TreeViewQuery TreeViewQuery "> <SfDataManager Url="api/Nodes" CrossDomain="true" Adaptor="Syncfusion.Blazor.Adaptors.WebApiAdaptor"></SfDataManager> </TreeViewFieldsSettings> <TreeViewTemplates TValue="NodeResult"> <NodeTemplate> <div>@context.Name</div> </NodeTemplate> </TreeViewTemplates> </SfTreeView> </div> @code { public Query TreeViewQuery = new Query(); public class NodeResult { public int? Id { get; set; } public string Name { get; set; } public int? Pid { get; set; } public bool HasChild { get; set; } public bool Expanded { get; set; } public bool Checked { get; set; } public bool Selected { get; set; } } }Refer to the code for the Web API controller.
using Microsoft.AspNetCore.Mvc; namespace TreeView.Controllers { [Route("api/[controller]")] [ApiController] public class NodesController : ControllerBase { [HttpGet] public IEnumerable<NodeResult> Get() { var queryString = Request.Query; if (queryString.Keys.Contains("$filter")) { // Load the child data based on the expanded parent. string filter = string.Join("", queryString["$filter"].ToString().Split(' ').Skip(2)); // Get filter from querystring. int parentId = int.Parse(filter); List<NodeResult> TreeViewData = GetItemsFromId(parentId); return TreeViewData; } else { // Get first-level tree data alone during the initial rendering. return GetRootData(); } } private static List<NodeResult> GetRootData() { return new List<NodeResult>() { new NodeResult() { Id = 1, Name = "Software Developers", HasChild = true, Expanded = true,Checked = false, Selected= true }, new NodeResult() { Id = 2, Name = "UX/UI Designers", HasChild = true, Expanded = false,Checked = false, Selected= true }, new NodeResult() { Id = 3, Name = "Quality Testers", HasChild = true, Expanded = false,Checked = false, Selected= true }, new NodeResult() { Id = 4, Name = "Technical Support", HasChild = true, Expanded = false,Checked = false, Selected= true }, new NodeResult() { Id = 5, Name = "Network Engineers", HasChild = true, Expanded = false,Checked = false, Selected= true } }; } private List<NodeResult> GetItemsFromId(int id) { List<NodeResult> DefaultData = new List<NodeResult>() { new NodeResult() { Name = "Nancy" }, new NodeResult() { Name = "Andrew" }, new NodeResult() { Name = "Janet" }, new NodeResult() { Name = "Margaret" }, new NodeResult() { Name = "Steven" }, new NodeResult() { Name = "Laura" }, new NodeResult() { Name = "Robert" }, new NodeResult() { Name = "Michael" }, new NodeResult() { Name = "Albert" }, new NodeResult() { Name = "Nolan" }, new NodeResult() { Name = "Jennifer" }, new NodeResult() { Name = "Carter" }, new NodeResult() { Name = "Allison" }, new NodeResult() { Name = "John" }, new NodeResult() { Name = "Susan" }, new NodeResult() { Name = "Lydia" }, new NodeResult() { Name = "Kelsey" }, new NodeResult() { Name = "Jessica" }, new NodeResult() { Name = "Shelley" }, new NodeResult() { Name = "Jack" } }; int count = 10000 * id; List<NodeResult> TreeViewData = Enumerable.Range(0, 2000) .Select(j => { count++; return new NodeResult { Id = count, Name = DefaultData[j % DefaultData.Count].Name + " - " + count.ToString(), Pid = id, Checked = false, Selected = true }; }).ToList(); return TreeViewData; } public class NodeResult { public int? Id { get; set; } public string Name { get; set; } public int? Pid { get; set; } public bool HasChild { get; set; } public bool Expanded { get; set; } public bool Checked { get; set; } public bool Selected { get; set; } } } }
Let’s see the performance difference between the TreeView rendered with and without virtualization on the Blazor server and WebAssembly (WASM) apps. This is the status as per the 2024 Volume 2 release.
Nodes count | Server without virtualization | Server with virtualization | WASM without virtualization | WASM with virtualization |
10 k data | 40730 ms | 920 ms | 120000 ms | 7750 ms |
10 K data (All nodes are selected and rendered using templates) | 90000 ms | 950 ms | 138000 ms | 8400 ms |
The above table proves that the virtualization feature considerably enhances the performance of the Blazor TreeView.
For more details, refer to the Virtualization in Blazor TreeView demo and documentation.
Thanks for reading! This article provides a clear and straightforward guide for implementing virtualization in the Blazor TreeView component. We hope you found it helpful. If you have any questions, feel free to share them in the comments section below.
You can also check out all the other features rolled out in the 2024 volume 2 release on our Release Notes and What’s New pages.
Try out these features and share your feedback as comments on this blog. You can also reach us through our support forums, support portal, or feedback portal.