Skip to content

Adding a node directly to the DataStore model of a TreeGridView can be very very slow #1460

Description

@msasso69

In my application I noticed that adding nodes to a TreeGridView at runtime was sometimes very slow, and after adding more that 1000 nodes the insertion of a node was slow, enough to make my UI completely unresponsive, so I put a limit of maximum 500 nodes while populating a tree live.
I then recently found out that only when I add a node directly to the model the insertion is very slow (my target is to load up to 500000 nodes). This is caused by the fact that when I add a node to the model, if I want it to show up, I need to reload it, but since it is not in the cachedItems Dictionary of the TreeGridViewHandler (it's a new node, so obviously is not there), the code calls a ReloadData() that reloads all the expanded nodes (notice that in my application I keep all my nodes expanded).
As a workaround I'm now adding a fake root node to the model at the very beginning, and later on I dynamically add my nodes to the root node or to one of its children, in this way when I reload the parent node this latter is always in the cachedItems and the addition is always very fast.

But this fake root node means nothing to me, and I would like to get rid of it ;)

I guess that the solution is probably to somehow add the node that I want to add to the model into the cachedItems in order to avoid the ReloadData(), but every my attempt in this direction failed miserably, that's why I ended up in adding a fake root node so that I never have to add nodes to the model.

Expected Behavior

I would like that adding a node to the tree would always take few milliseconds

Actual Behavior

Adding a node directly to the model causes a call to ReloadData() because the node that I try to reload is not in the cachedItems dictionary of the TreeGridViewHandler, and I cannot avoid to call the ReloadItem(node) for the node just inserted if I want it to show up.

Steps to Reproduce the Problem

  1. run the repro
  2. click on the "Add to node" button
    image
    as you can see the node is added in few msec
  3. click on the "Add to model" button
    image
    as you can see adding the node takes now almost two seconds (around 600 times slower)

Code that Demonstrates the Problem

using System;
using Eto;
using Eto.Drawing;
using Eto.Forms;
using System.Diagnostics;

namespace VisualSEO.EtoFormGui
{
	public class Program
	{
		[STAThread]
		public static void Main(string[] args)
		{
			new Application(Platform.Detect).Run(new MyForm());
		}

		public class MyForm : Form
		{
			TreeGridItemCollection model;
			TreeGridView tree;
			static GridColumn column;
			TreeGridItem node1, node2;

			public MyForm()
			{
				this.Width = 500;
				this.Height = 250;
				tree = new TreeGridView();
				node1 = new TreeGridItem();
				node2 = new TreeGridItem();

				if (Platform.Supports<DrawableCell>())
				{
					var drawableCell = new DrawableCell();
					drawableCell.Paint += (sender, e) =>
					{
						string text = "sfsgh rthysdgs rsths wetd shrswe tsdvwsdwe dsgewwe dsr";
						e.Graphics.DrawText(SystemFonts.Label(), Colors.Black, (float)0, (float)-8, text);
					};
					tree.Columns.Add(column = new GridColumn
					{
						DataCell = drawableCell,
						AutoSize = false,
						Width = 600
					});
				}

				model = new TreeGridItemCollection();
				tree.DataStore = model;
				model.Add(node1);
				for (int i = 0; i < 10000; i++)
					node1.Children.Add(new TreeGridItem());
				node1.Expanded = true;
				model.Add(node2);
				tree.ReloadData();

				tree.Columns[0].Width = 200;

				TextBox textBox = new TextBox();

				Button button1 = new Button() { Text = "Add to node" };
				button1.Click += (sender, e) =>
				{
					TreeGridItem node = new TreeGridItem();
					node2.Children.Add(node);
					Stopwatch stopWatch = new Stopwatch();
					stopWatch.Start();
					double start = stopWatch.Elapsed.TotalMilliseconds;
					tree.ReloadItem(node2);
					double end = stopWatch.Elapsed.TotalMilliseconds;
					double diff = end - start;
					textBox.Text = "node added in  " + diff.ToString() + " msec";
				};

				Button button2 = new Button() { Text = "Add to model" };
				button2.Click += (sender, e) =>
				{
					TreeGridItem node = new TreeGridItem();
					model.Add(node);
					Stopwatch stopWatch = new Stopwatch();
					stopWatch.Start();
					double start = stopWatch.Elapsed.TotalMilliseconds;
					tree.ReloadItem(node);
					double end = stopWatch.Elapsed.TotalMilliseconds;
					double diff = end - start;
					textBox.Text = "node added in  " + diff.ToString() + " msec";
				};

				Content = new StackLayout
				{
					HorizontalContentAlignment = HorizontalAlignment.Stretch,
					Items =
					{
						new StackLayout
						{
							Orientation = Orientation.Horizontal,
							HorizontalContentAlignment = HorizontalAlignment.Stretch,
							Items = { button1, button2, new StackLayoutItem(textBox, true) }
						},
						new StackLayoutItem(new StackLayout
						{
							HorizontalContentAlignment = HorizontalAlignment.Stretch,
							VerticalContentAlignment = VerticalAlignment.Stretch,
							Items = { new StackLayoutItem(tree, true) }
						}, true)
					}
				};
			}
		}
	}
}

Specifications

  • Version: commit ab680e6 (13 June 2019)
  • Platform(s): XamMac2
  • Operating System(s): macOS 10.13

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions