Describe the bug
The TreeView control has issues with rendering and UI interaction when it contains a large number of items. In such a scenario, node expansion doesn't behave correctly and scrolling is, for lack of a better word, wonky. The example below may seem contrived but it's the simplest repro for the behavior I've discovered in my own app. For my own app, I've had to give up on UWP TreeView because it's basically just unusable due to these performance issues. In my app, I was only dealing with about 1,500 nodes.
Steps to reproduce the bug
MainPage.xaml
<Page
x:Class="App.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TreeView Name="KeywordTreeView"
ItemsSource="{x:Bind Nodes}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="local:Node">
<TreeViewItem ItemsSource="{x:Bind ChildNodes}"
IsExpanded="True"
Content="{x:Bind Name}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App2
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Nodes = new ObservableCollection<Node>();
for (int i = 0; i < 100; i++)
{
this.Nodes.Add(CreateNode(i.ToString(), 5));
}
}
public ObservableCollection<Node> Nodes { get; }
private static Node CreateNode(string name, int depth)
{
Node node = new Node(name);
if (depth > 0)
{
depth--;
for (int i = 0; i < 10; i++)
{
node.ChildNodes.Add(CreateNode(name + i, depth));
}
}
return node;
}
}
public class Node
{
public Node(string name)
{
this.Name = name;
}
public string Name { get; }
public ObservableCollection<Node> ChildNodes { get; } = new ObservableCollection<Node>();
}
}
Expected behavior
All nodes should be expanded. Scrolling should be smooth and predictable. Expanding/collapsing nodes should work as expected.
Screenshots
Version Info
NuGet package version:
| Windows 10 version | Saw the problem? |
| :--------------------------------- | :-------------------- |
| Insider Build (xxxxx) | |
| May 2019 Update (18362) | YES |
| October 2018 Update (17763) | YES |
| April 2018 Update (17134) | |
| Fall Creators Update (16299) | |
| Creators Update (15063) | |
| Anniversary Update (14393) | |
| Device form factor | Saw the problem? |
| :-------------------- | :------------------- |
| Desktop | YES |
| Mobile | |
| Xbox | |
| Surface Hub | |
| IoT | |
Additional context
I can certainly believe this causes issues for TreeView in its current implementation. @mthalman, what is the scenario you're trying to build? I'm wondering if TreeView is the right tool or if maybe we should be trying to focus our energy on a Tree-DataGrid thing like #260 proposes.
@jevansaks - It's for a photo keywording hierarchy database, similar to how Adobe Lightroom has a keyword hierarchy: https://www.creative-photographer.com/hierarchical-keywords-lightroom/. In my case, I don't need have a need for a data grid; I only need to be showing one field (the keyword name).
@jevansaks I know other creative apps with layers for drawings, illustrations, diagrams, or UI (like Adobe XD) could have hundreds to thousands of nodes.
If you create a 'simple' component in a group with 10 objects (lines, outlines, boxes, text, images, etc...) and then copy that into a 10x10 grid for a page, you suddenly have a 1,000 objects. Then you copy that page 10 times to create a scenario flow and now you have 10,000 objects!
@michael-hawker @jevansaks
Or just look at the Live-Visual-Tree in VS. Depending on the UI you are inspecting you could end up with hundreds of treeview nodes as well.
Element count is a likely culprit here, which is why I wanted to understand the scenario. Having 10,000 tree view items expanded all from the start seems excessive. I would imagine we don't do a great job virtualizing in this scenario either (or perhaps we're trying to and that's contributing to the "wonkiness").
Yes, it's sort of a contrived example but it's the easiest way to illustrate what I've run into in my own app. In my case, I'm not starting out with all the nodes expanded. But I have a scenario where you can get a lot of nodes expanded very quickly. My app has a filter box intended to filter the nodes that you see in the tree. It will find all nodes that contain the text in the filter box and ensure those nodes are made visible/expanded. Other nodes not contributing to the hierarchy of the matching node are made hidden. So you can imagine what happens when the user types 'a'.
I see, that makes sense. We can take a look and see if there's any obvious perf improvements we can make here.
Most helpful comment
@michael-hawker @jevansaks
Or just look at the Live-Visual-Tree in VS. Depending on the UI you are inspecting you could end up with hundreds of treeview nodes as well.