Skip to content

Commit

Permalink
TreeView: Add virtualization support (#5689)
Browse files Browse the repository at this point in the history
* added virtualization support to treeview

* Replaced a regular html anchor tag with a blazorise Anchor component.

Added the relevant logs to the current changelog's optimization section.

Updated the summary string documentation for the Virtualize Parameter of TreeView

* formating

* Mention default value

* Fix virtualization

* Add Virtualization example

* Don't pass styling parameters further from root element

* Use defaults when Virtualize is enabled

* release notes

* Change description

* Docs notes

* testing treeview virtualize with dynamic data

* Properly fallback to default Height and Overflow values

* Remove extra parenthesis

* Fix duplicate nodes

---------

Co-authored-by: ddjerqq <[email protected]>
Co-authored-by: Mladen Macanovic <[email protected]>
Co-authored-by: Mladen Macanovic <[email protected]>
Co-authored-by: David Moreira <[email protected]>
  • Loading branch information
5 people authored Oct 22, 2024
1 parent f279562 commit 9c88b10
Show file tree
Hide file tree
Showing 12 changed files with 630 additions and 86 deletions.
5 changes: 5 additions & 0 deletions Demos/Blazorise.Demo/Pages/Tests/TreeViewPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
</Field>
</Column>
<Column ColumnSize="ColumnSize.IsAuto">
<Button Color="Color.Primary" Clicked="AddNode">Add</Button>
<Button Color="Color.Primary" Clicked="ForceReload">Force Reload</Button>
<Button Color="Color.Primary" Clicked="DisableRandomNode">Disable Random</Button>
<Button Color="Color.Primary" Clicked="DisableAll">Disable All</Button>
Expand All @@ -41,12 +42,16 @@
<Button Color="Color.Secondary" Clicked="@(()=>selectedNodes = Nodes.Take(3).ToList())">Select multiple nodes</Button>
}
</Column>
<Column ColumnSize="ColumnSize.IsAuto">
<Switch @bind-Checked=virtualize>Virtualize</Switch>
</Column>
</Row>
</CardBody>
<CardBody>
<TreeView @ref="@treeViewRef"
TNode="NodeInfo"
Nodes="Nodes"
Virtualize="@virtualize"
SelectionMode="@selectionMode"
@bind-SelectedNode="selectedNode"
@bind-ExpandedNodes="expandedNodes"
Expand Down
29 changes: 21 additions & 8 deletions Demos/Blazorise.Demo/Pages/Tests/TreeViewPage.razor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Security;
using System.Threading.Tasks;
Expand All @@ -12,30 +13,31 @@ namespace Blazorise.Demo.Pages.Tests;
public partial class TreeViewPage : ComponentBase
{
TreeView<NodeInfo> treeViewRef;
private IList<NodeInfo> expandedNodes = new List<NodeInfo>();
private IList<NodeInfo> selectedNodes = new List<NodeInfo>();
private IList<NodeInfo> expandedNodes = new ObservableCollection<NodeInfo>();
private IList<NodeInfo> selectedNodes = new ObservableCollection<NodeInfo>();
private NodeInfo selectedNode;
private TreeViewSelectionMode selectionMode;
private bool virtualize;

public class NodeInfo
{
public string Text { get; set; }
public IEnumerable<NodeInfo> Children { get; set; }
public ObservableCollection<NodeInfo> Children { get; set; }
public bool Disabled { get; set; }
}

private IEnumerable<NodeInfo> Nodes = new[]
private ObservableCollection<NodeInfo> Nodes = new ObservableCollection<NodeInfo>()
{
new NodeInfo { Text = "NodeInfo 1" },
new NodeInfo
{
Text = "NodeInfo 2",
Children = new []
Children = new ObservableCollection<NodeInfo>()
{
new NodeInfo { Text = "NodeInfo 2.1" },
new NodeInfo
{
Text = "NodeInfo 2.2", Children = new []
Text = "NodeInfo 2.2", Children = new ObservableCollection<NodeInfo>()
{
new NodeInfo { Text = "NodeInfo 2.2.1" },
new NodeInfo { Text = "NodeInfo 2.2.2" },
Expand All @@ -51,12 +53,12 @@ public class NodeInfo
new NodeInfo
{
Text = "NodeInfo 4",
Children = new []
Children = new ObservableCollection<NodeInfo>()
{
new NodeInfo { Text = "NodeInfo 4.1" },
new NodeInfo
{
Text = "NodeInfo 4.2", Children = new []
Text = "NodeInfo 4.2", Children = new ObservableCollection<NodeInfo>()
{
new NodeInfo { Text = "NodeInfo 4.2.1" },
new NodeInfo { Text = "NodeInfo 4.2.2" },
Expand All @@ -72,6 +74,17 @@ public class NodeInfo
new NodeInfo { Text = "NodeInfo 6" }
};


int count = 0;
private async Task AddNode()
{
count++;
selectedNode.Children ??= new ObservableCollection<NodeInfo>();
selectedNode.Children.Add( new NodeInfo()
{
Text = selectedNode.Text + count,
} );
}
private async Task ForceReload()
{
await treeViewRef.Reload();
Expand Down
40 changes: 40 additions & 0 deletions Documentation/Blazorise.Docs/Models/Snippets.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10970,6 +10970,46 @@ public class Item

public const string TreeViewResourcesExample = @"<link href=""_content/Blazorise.TreeView/blazorise.treeview.css"" rel=""stylesheet"" />";

public const string TreeViewVirtualizationExample = @"<TreeView Nodes=""Items""
GetChildNodes=""@(item => item.Children)""
HasChildNodes=""@(item => item.Children?.Any() == true)""
@bind-SelectedNode=""selectedNode""
@bind-ExpandedNodes=""expandedNodes""
Virtualize>
<NodeContent>
<Icon Name=""IconName.Folder"" />
@context.Text
</NodeContent>
</TreeView>

@code {
public class Item
{
public string Text { get; set; }
public IEnumerable<Item> Children { get; set; }
}

protected override void OnInitialized()
{
Items = Enumerable.Range( 1, 4 ).Select( rootIndex => new Item
{
Text = $""Root Node {rootIndex}"",
Children = Enumerable.Range( 1, 100 ).Select( childIndex => new Item
{
Text = $""Root {rootIndex} - Child {childIndex}"",
Children = Enumerable.Empty<Item>() // No children for the child nodes in this example
} )
} ).ToList();

base.OnInitialized();
}

IEnumerable<Item> Items;

IList<Item> expandedNodes = new List<Item>();
Item selectedNode;
}";

public const string BasicVideoExample = @"<Video Source=""@(""http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"")"" />";

public const string DRMVideoExample = @"<Video Source=""@(""https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd"")""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<div class="blazorise-codeblock">
<div class="html"><pre>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">TreeView</span> <span class="htmlAttributeName">Nodes</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Items</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">GetChildNodes</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>(item =&gt; item.Children)</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">HasChildNodes</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>(item =&gt; item.Children?.Any() == true)</span><span class="quot">&quot;</span>
<span class="htmlAttributeName"><span class="atSign">&#64;</span>bind-SelectedNode</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">selectedNode</span><span class="quot">&quot;</span>
<span class="htmlAttributeName"><span class="atSign">&#64;</span>bind-ExpandedNodes</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">expandedNodes</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">Virtualize</span><span class="htmlTagDelimiter">&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">NodeContent</span><span class="htmlTagDelimiter">&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">Icon</span> <span class="htmlAttributeName">Name</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="enum">IconName</span><span class="enumValue">.Folder</span><span class="quot">&quot;</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="atSign">&#64;</span>context.Text
<span class="htmlTagDelimiter">&lt;/</span><span class="htmlElementName">NodeContent</span><span class="htmlTagDelimiter">&gt;</span>
<span class="htmlTagDelimiter">&lt;/</span><span class="htmlElementName">TreeView</span><span class="htmlTagDelimiter">&gt;</span>
</pre></div>
<div class="csharp"><pre>
<span class="atSign">&#64;</span>code {
<span class="keyword">public</span> <span class="keyword">class</span> Item
{
<span class="keyword">public</span> <span class="keyword">string</span> Text { <span class="keyword">get</span>; <span class="keyword">set</span>; }
<span class="keyword">public</span> IEnumerable&lt;Item&gt; Children { <span class="keyword">get</span>; <span class="keyword">set</span>; }
}

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnInitialized()
{
Items = Enumerable.Range( <span class="number">1</span>, <span class="number">4</span> ).Select( rootIndex =&gt; <span class="keyword">new</span> Item
{
Text = $<span class="string">&quot;Root Node {rootIndex}&quot;</span>,
Children = Enumerable.Range( <span class="number">1</span>, <span class="number">100</span> ).Select( childIndex =&gt; <span class="keyword">new</span> Item
{
Text = $<span class="string">&quot;Root {rootIndex} - Child {childIndex}&quot;</span>,
Children = Enumerable.Empty&lt;Item&gt;() <span class="comment">// No children for the child nodes in this example</span>
} )
} ).ToList();

<span class="keyword">base</span>.OnInitialized();
}

IEnumerable&lt;Item&gt; Items;

IList&lt;Item&gt; expandedNodes = <span class="keyword">new</span> List&lt;Item&gt;();
Item selectedNode;
}
</pre></div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@namespace Blazorise.Docs.Docs.Examples

<TreeView Nodes="Items"
GetChildNodes="@(item => item.Children)"
HasChildNodes="@(item => item.Children?.Any() == true)"
@bind-SelectedNode="selectedNode"
@bind-ExpandedNodes="expandedNodes"
Virtualize>
<NodeContent>
<Icon Name="IconName.Folder" />
@context.Text
</NodeContent>
</TreeView>

@code {
public class Item
{
public string Text { get; set; }
public IEnumerable<Item> Children { get; set; }
}

protected override void OnInitialized()
{
Items = Enumerable.Range( 1, 4 ).Select( rootIndex => new Item
{
Text = $"Root Node {rootIndex}",
Children = Enumerable.Range( 1, 100 ).Select( childIndex => new Item
{
Text = $"Root {rootIndex} - Child {childIndex}",
Children = Enumerable.Empty<Item>() // No children for the child nodes in this example
} )
} ).ToList();

base.OnInitialized();
}

IEnumerable<Item> Items;

IList<Item> expandedNodes = new List<Item>();
Item selectedNode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,36 @@
<DocsPageSectionSource Code="TreeViewContextMenuExample" />
</DocsPageSection>

<DocsPageSection>
<DocsPageSectionHeader Title="Virtualization">
<Paragraph>
This example demonstrates how to use virtualization in a Blazorise <Code Tag>TreeView</Code> component to efficiently render large hierarchical data sets. Virtualization improves performance by only rendering the visible nodes in the viewport, rather than all nodes in the tree. This is particularly useful when dealing with large numbers of nodes, as it reduces the DOM size and enhances responsiveness.
</Paragraph>
<Paragraph>
For virtualization to function correctly, it is essential to specify both the <Strong>Height</Strong> and <Strong>Overflow</Strong> properties
</Paragraph>
<OrderedList>
<OrderedListItem>
<Paragraph>
<Strong>Height</Strong>: Defines the fixed height of the TreeView component. Without a specified height, the tree would expand indefinitely, defeating the purpose of virtualization since all nodes would be rendered at once.
</Paragraph>
</OrderedListItem>
<OrderedListItem>
<Paragraph>
<Strong>Overflow</Strong>: Ensures that the tree's content is scrollable. This scrollable area allows for dynamic loading of nodes as the user scrolls, effectively utilizing virtualization to render only the nodes currently in view.
</Paragraph>
</OrderedListItem>
</OrderedList>
<Paragraph>
By default, when <Code>Virtualize</Code> is enabled, we will define <Strong>Height</Strong> and <Strong>Overflow</Strong> for you, if they are not already explicitly defined.
</Paragraph>
</DocsPageSectionHeader>
<DocsPageSectionContent Outlined FullWidth>
<TreeViewVirtualizationExample />
</DocsPageSectionContent>
<DocsPageSectionSource Code="TreeViewVirtualizationExample" />
</DocsPageSection>

<DocsPageSubtitle>
API
</DocsPageSubtitle>
Expand Down Expand Up @@ -192,6 +222,9 @@
<DocsAttributesItem Name="AutoExpandAll" Type="bool" Default="false">
Defines if the treenode should be automatically expanded. Note that it can happen only once when the tree is first loaded.
</DocsAttributesItem>
<DocsAttributesItem Name="Virtualize" Type="bool" Default="false">
Controls if the child nodes, which are currently not expanded, are visible. See <Anchor To="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/virtualization">docs for Virtualization</Anchor>.
</DocsAttributesItem>
<DocsAttributesItem Name="ExpandedNodes" Type="List<TNode>">
List of currently expanded TreeView items (child nodes).
</DocsAttributesItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@
After our community has convinced us that these parameters are still useful, we have decided to undeprecate them. We have undeprecated the <Code>CellClass</Code> and <Code>CellStyle</Code> parameters in the <Code>DataGridColumn</Code> component. These parameters allow you to define the class and style for the cell based on the cell item value.
</Paragraph>

<Heading Size="HeadingSize.Is3">
TreeView Virtualization
</Heading>

<Paragraph>
We have added the ability to virtualize the TreeView component. This can be useful when you have a large number of nodes and you want to improve the performance of the TreeView component. The virtualization feature allows you to render only the visible nodes, which can significantly reduce the number of DOM elements and improve the overall performance of the TreeView component.
</Paragraph>

<Heading Size="HeadingSize.Is3">
Optimizations
</Heading>
Expand Down
Loading

0 comments on commit 9c88b10

Please sign in to comment.