Skip to content

Commit 9c88b10

Browse files
ddjerqqddjerqqstsrkiDavid-Moreira
authored
TreeView: Add virtualization support (#5689)
* 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]>
1 parent f279562 commit 9c88b10

File tree

12 files changed

+630
-86
lines changed

12 files changed

+630
-86
lines changed

Demos/Blazorise.Demo/Pages/Tests/TreeViewPage.razor

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
</Field>
2727
</Column>
2828
<Column ColumnSize="ColumnSize.IsAuto">
29+
<Button Color="Color.Primary" Clicked="AddNode">Add</Button>
2930
<Button Color="Color.Primary" Clicked="ForceReload">Force Reload</Button>
3031
<Button Color="Color.Primary" Clicked="DisableRandomNode">Disable Random</Button>
3132
<Button Color="Color.Primary" Clicked="DisableAll">Disable All</Button>
@@ -41,12 +42,16 @@
4142
<Button Color="Color.Secondary" Clicked="@(()=>selectedNodes = Nodes.Take(3).ToList())">Select multiple nodes</Button>
4243
}
4344
</Column>
45+
<Column ColumnSize="ColumnSize.IsAuto">
46+
<Switch @bind-Checked=virtualize>Virtualize</Switch>
47+
</Column>
4448
</Row>
4549
</CardBody>
4650
<CardBody>
4751
<TreeView @ref="@treeViewRef"
4852
TNode="NodeInfo"
4953
Nodes="Nodes"
54+
Virtualize="@virtualize"
5055
SelectionMode="@selectionMode"
5156
@bind-SelectedNode="selectedNode"
5257
@bind-ExpandedNodes="expandedNodes"

Demos/Blazorise.Demo/Pages/Tests/TreeViewPage.razor.cs

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
34
using System.Linq;
45
using System.Security;
56
using System.Threading.Tasks;
@@ -12,30 +13,31 @@ namespace Blazorise.Demo.Pages.Tests;
1213
public partial class TreeViewPage : ComponentBase
1314
{
1415
TreeView<NodeInfo> treeViewRef;
15-
private IList<NodeInfo> expandedNodes = new List<NodeInfo>();
16-
private IList<NodeInfo> selectedNodes = new List<NodeInfo>();
16+
private IList<NodeInfo> expandedNodes = new ObservableCollection<NodeInfo>();
17+
private IList<NodeInfo> selectedNodes = new ObservableCollection<NodeInfo>();
1718
private NodeInfo selectedNode;
1819
private TreeViewSelectionMode selectionMode;
20+
private bool virtualize;
1921

2022
public class NodeInfo
2123
{
2224
public string Text { get; set; }
23-
public IEnumerable<NodeInfo> Children { get; set; }
25+
public ObservableCollection<NodeInfo> Children { get; set; }
2426
public bool Disabled { get; set; }
2527
}
2628

27-
private IEnumerable<NodeInfo> Nodes = new[]
29+
private ObservableCollection<NodeInfo> Nodes = new ObservableCollection<NodeInfo>()
2830
{
2931
new NodeInfo { Text = "NodeInfo 1" },
3032
new NodeInfo
3133
{
3234
Text = "NodeInfo 2",
33-
Children = new []
35+
Children = new ObservableCollection<NodeInfo>()
3436
{
3537
new NodeInfo { Text = "NodeInfo 2.1" },
3638
new NodeInfo
3739
{
38-
Text = "NodeInfo 2.2", Children = new []
40+
Text = "NodeInfo 2.2", Children = new ObservableCollection<NodeInfo>()
3941
{
4042
new NodeInfo { Text = "NodeInfo 2.2.1" },
4143
new NodeInfo { Text = "NodeInfo 2.2.2" },
@@ -51,12 +53,12 @@ public class NodeInfo
5153
new NodeInfo
5254
{
5355
Text = "NodeInfo 4",
54-
Children = new []
56+
Children = new ObservableCollection<NodeInfo>()
5557
{
5658
new NodeInfo { Text = "NodeInfo 4.1" },
5759
new NodeInfo
5860
{
59-
Text = "NodeInfo 4.2", Children = new []
61+
Text = "NodeInfo 4.2", Children = new ObservableCollection<NodeInfo>()
6062
{
6163
new NodeInfo { Text = "NodeInfo 4.2.1" },
6264
new NodeInfo { Text = "NodeInfo 4.2.2" },
@@ -72,6 +74,17 @@ public class NodeInfo
7274
new NodeInfo { Text = "NodeInfo 6" }
7375
};
7476

77+
78+
int count = 0;
79+
private async Task AddNode()
80+
{
81+
count++;
82+
selectedNode.Children ??= new ObservableCollection<NodeInfo>();
83+
selectedNode.Children.Add( new NodeInfo()
84+
{
85+
Text = selectedNode.Text + count,
86+
} );
87+
}
7588
private async Task ForceReload()
7689
{
7790
await treeViewRef.Reload();

Documentation/Blazorise.Docs/Models/Snippets.generated.cs

+40
Original file line numberDiff line numberDiff line change
@@ -10970,6 +10970,46 @@ public class Item
1097010970

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

10973+
public const string TreeViewVirtualizationExample = @"<TreeView Nodes=""Items""
10974+
GetChildNodes=""@(item => item.Children)""
10975+
HasChildNodes=""@(item => item.Children?.Any() == true)""
10976+
@bind-SelectedNode=""selectedNode""
10977+
@bind-ExpandedNodes=""expandedNodes""
10978+
Virtualize>
10979+
<NodeContent>
10980+
<Icon Name=""IconName.Folder"" />
10981+
@context.Text
10982+
</NodeContent>
10983+
</TreeView>
10984+
10985+
@code {
10986+
public class Item
10987+
{
10988+
public string Text { get; set; }
10989+
public IEnumerable<Item> Children { get; set; }
10990+
}
10991+
10992+
protected override void OnInitialized()
10993+
{
10994+
Items = Enumerable.Range( 1, 4 ).Select( rootIndex => new Item
10995+
{
10996+
Text = $""Root Node {rootIndex}"",
10997+
Children = Enumerable.Range( 1, 100 ).Select( childIndex => new Item
10998+
{
10999+
Text = $""Root {rootIndex} - Child {childIndex}"",
11000+
Children = Enumerable.Empty<Item>() // No children for the child nodes in this example
11001+
} )
11002+
} ).ToList();
11003+
11004+
base.OnInitialized();
11005+
}
11006+
11007+
IEnumerable<Item> Items;
11008+
11009+
IList<Item> expandedNodes = new List<Item>();
11010+
Item selectedNode;
11011+
}";
11012+
1097311013
public const string BasicVideoExample = @"<Video Source=""@(""http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"")"" />";
1097411014

1097511015
public const string DRMVideoExample = @"<Video Source=""@(""https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd"")""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div class="blazorise-codeblock">
2+
<div class="html"><pre>
3+
<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>
4+
<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>
5+
<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>
6+
<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>
7+
<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>
8+
<span class="htmlAttributeName">Virtualize</span><span class="htmlTagDelimiter">&gt;</span>
9+
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">NodeContent</span><span class="htmlTagDelimiter">&gt;</span>
10+
<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>
11+
<span class="atSign">&#64;</span>context.Text
12+
<span class="htmlTagDelimiter">&lt;/</span><span class="htmlElementName">NodeContent</span><span class="htmlTagDelimiter">&gt;</span>
13+
<span class="htmlTagDelimiter">&lt;/</span><span class="htmlElementName">TreeView</span><span class="htmlTagDelimiter">&gt;</span>
14+
</pre></div>
15+
<div class="csharp"><pre>
16+
<span class="atSign">&#64;</span>code {
17+
<span class="keyword">public</span> <span class="keyword">class</span> Item
18+
{
19+
<span class="keyword">public</span> <span class="keyword">string</span> Text { <span class="keyword">get</span>; <span class="keyword">set</span>; }
20+
<span class="keyword">public</span> IEnumerable&lt;Item&gt; Children { <span class="keyword">get</span>; <span class="keyword">set</span>; }
21+
}
22+
23+
<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> OnInitialized()
24+
{
25+
Items = Enumerable.Range( <span class="number">1</span>, <span class="number">4</span> ).Select( rootIndex =&gt; <span class="keyword">new</span> Item
26+
{
27+
Text = $<span class="string">&quot;Root Node {rootIndex}&quot;</span>,
28+
Children = Enumerable.Range( <span class="number">1</span>, <span class="number">100</span> ).Select( childIndex =&gt; <span class="keyword">new</span> Item
29+
{
30+
Text = $<span class="string">&quot;Root {rootIndex} - Child {childIndex}&quot;</span>,
31+
Children = Enumerable.Empty&lt;Item&gt;() <span class="comment">// No children for the child nodes in this example</span>
32+
} )
33+
} ).ToList();
34+
35+
<span class="keyword">base</span>.OnInitialized();
36+
}
37+
38+
IEnumerable&lt;Item&gt; Items;
39+
40+
IList&lt;Item&gt; expandedNodes = <span class="keyword">new</span> List&lt;Item&gt;();
41+
Item selectedNode;
42+
}
43+
</pre></div>
44+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@namespace Blazorise.Docs.Docs.Examples
2+
3+
<TreeView Nodes="Items"
4+
GetChildNodes="@(item => item.Children)"
5+
HasChildNodes="@(item => item.Children?.Any() == true)"
6+
@bind-SelectedNode="selectedNode"
7+
@bind-ExpandedNodes="expandedNodes"
8+
Virtualize>
9+
<NodeContent>
10+
<Icon Name="IconName.Folder" />
11+
@context.Text
12+
</NodeContent>
13+
</TreeView>
14+
15+
@code {
16+
public class Item
17+
{
18+
public string Text { get; set; }
19+
public IEnumerable<Item> Children { get; set; }
20+
}
21+
22+
protected override void OnInitialized()
23+
{
24+
Items = Enumerable.Range( 1, 4 ).Select( rootIndex => new Item
25+
{
26+
Text = $"Root Node {rootIndex}",
27+
Children = Enumerable.Range( 1, 100 ).Select( childIndex => new Item
28+
{
29+
Text = $"Root {rootIndex} - Child {childIndex}",
30+
Children = Enumerable.Empty<Item>() // No children for the child nodes in this example
31+
} )
32+
} ).ToList();
33+
34+
base.OnInitialized();
35+
}
36+
37+
IEnumerable<Item> Items;
38+
39+
IList<Item> expandedNodes = new List<Item>();
40+
Item selectedNode;
41+
}

Documentation/Blazorise.Docs/Pages/Docs/Extensions/TreeView/TreeViewPage.razor

+33
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,36 @@
159159
<DocsPageSectionSource Code="TreeViewContextMenuExample" />
160160
</DocsPageSection>
161161

162+
<DocsPageSection>
163+
<DocsPageSectionHeader Title="Virtualization">
164+
<Paragraph>
165+
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.
166+
</Paragraph>
167+
<Paragraph>
168+
For virtualization to function correctly, it is essential to specify both the <Strong>Height</Strong> and <Strong>Overflow</Strong> properties
169+
</Paragraph>
170+
<OrderedList>
171+
<OrderedListItem>
172+
<Paragraph>
173+
<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.
174+
</Paragraph>
175+
</OrderedListItem>
176+
<OrderedListItem>
177+
<Paragraph>
178+
<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.
179+
</Paragraph>
180+
</OrderedListItem>
181+
</OrderedList>
182+
<Paragraph>
183+
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.
184+
</Paragraph>
185+
</DocsPageSectionHeader>
186+
<DocsPageSectionContent Outlined FullWidth>
187+
<TreeViewVirtualizationExample />
188+
</DocsPageSectionContent>
189+
<DocsPageSectionSource Code="TreeViewVirtualizationExample" />
190+
</DocsPageSection>
191+
162192
<DocsPageSubtitle>
163193
API
164194
</DocsPageSubtitle>
@@ -192,6 +222,9 @@
192222
<DocsAttributesItem Name="AutoExpandAll" Type="bool" Default="false">
193223
Defines if the treenode should be automatically expanded. Note that it can happen only once when the tree is first loaded.
194224
</DocsAttributesItem>
225+
<DocsAttributesItem Name="Virtualize" Type="bool" Default="false">
226+
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>.
227+
</DocsAttributesItem>
195228
<DocsAttributesItem Name="ExpandedNodes" Type="List<TNode>">
196229
List of currently expanded TreeView items (child nodes).
197230
</DocsAttributesItem>

Documentation/Blazorise.Docs/Pages/News/2024-10-15-release-notes-170.razor

+8
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@
196196
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.
197197
</Paragraph>
198198

199+
<Heading Size="HeadingSize.Is3">
200+
TreeView Virtualization
201+
</Heading>
202+
203+
<Paragraph>
204+
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.
205+
</Paragraph>
206+
199207
<Heading Size="HeadingSize.Is3">
200208
Optimizations
201209
</Heading>

0 commit comments

Comments
 (0)