views:

4262

answers:

7

I have a legacy application that is written in C# and it displays a very complex treeview with 10 to 20 thousand elements.

In the past I encountered a similar problem (but in C++) that i solved with the OWNERDATA capability offered by the Win32 API.

Is there a similar mechanism in C#?

EDIT: The plan is to optimize the creation time as well as browsing time. The method available through Win32 API is excellent in both of these cases as it reduce initialization time to nothing and the number of requests for elements are limited to only the ones visible at any one time. Joshl: We are actually doing exactly what you suggest already, but we still need more efficiency.

+1  A: 

How do you fill the TreeView? Do you use a "BeginUpdate()" & "EndUpdate" ?

TcKs
+10  A: 

One technique for improving performance is to load TreeNodes as the user expands the treeview. Normally a user will not require 20,000 nodes to be open on their screen at once. Only load the level that the user needs to see, along with whatever child information you need to properly display affordances to the user (expand icon if children exist, counts, icons, etc). As the user expands nodes, load children just in time.

Helpful hint from Keith: With the winforms TreeView you need to have at least one child node or it won't show the expand [+], but then you handle the TreeNodeExpanded event to remove that dummy node and populate the children.

JoshL
Delay loading... I believe this is called. Nice answer.
Gishu
This answer is obviously good as well as it was already something that was implemented on my side.
Fabien Hure
With the winforms TreeView you need to have at least one child node or it won't show the expand [+], but then you handle the TreeNodeExpanded event to remove that dummy node and populate the children.
Keith
+4  A: 

I don't believe the .NET TreeView supports what you want, although this type of model is supported by .NET's DataGridView (see DataGridView's VirtualMode property). The TreeView will let you draw your own nodes but it won't let you populate them from some virtual store.

If possible, you might want to consider the use of a DataGridView for your application. If not, managing the nodes manually (like joshl mentions above) might work if you can get around some issues with refreshing the screen properly when nodes are expanded. Outside of that, you might want to check out some of the third party vendors, like this one, that might (emphasis on might) support your desired mode of operation.

Ken Wootton
The VirtualMode of the DataGridView seems to fit perfectly the bill. Thanks very much for the quick answer.
Fabien Hure
A: 

There is one way to make the TreeView perform much better and that is to create all sub-nodes and hook them togheter and then add the nodes to the TreeView. If it's the graphical performance we are talking about.

TreeView tree = new TreeView();
TreeNode root = new TreeNode("Root");
PopulateRootNode(root); // Get all your data
tree.Nodes.Add(root);

Otherwise, load them node by node using OnTreeNodeExpanded.

Seb Nilsson
+1  A: 

NOTE: This answer is invalidated by an edit by the questioner saying he already does this kind of thing, but I decided to still post it for future reference by others searching on this topic

When I've done similar things in the past, I've tended to opt for the naive lazy-loading style.

  • Use the TreeNode.Tag property to hold a reference that you can use to look-up the children
  • Use the TreeView.BeforeExpand event to populate the child nodes
  • Optionally use the TreeView.AfterCollapse event to remove them.
  • To get the [+]/[-] boxes to appear, the best way I've found is to create a singleton dummy TreeNode that gets added as a child to all unpopulated Nodes, and you check for its existence before populating with BeforeExpand.
Alex Lyman
A: 

For large data in Windows C# programming, whether it be in WPF or WinForms, I have traditionally added nodes dynamically. I load the initial tree root + children + grandchildren deep. When any node is expanded, I load the tree nodes that would represent the grandchildren of the expanding node, if any.

This pattern also works well with data retrieval. If you are truly loading data from a source of thousands or millions of records you probably don't want to load those all up front. No user wants to wait for that to be loaded, and there is not reason to load data that may never be viewed.

I have typically loaded the grandchildren or the great-grandchildren node data as needed on a background thread, then marshal those data back to the UI thread and create and add the nodes. This leaves the UI responsive. You can visually decorate tree nodes to indicate they are still loading for the case where a user gets ahead of your IO to the data store.

Jason Jackson
A: 

In our main WinForm app, we have a treeview loaded all in one shot:

  • BeginUpdate()
  • Load 20.000 nodes
  • EndUpdate()

and so far the performance is still nice. It is actually one of the few components we are not replacing with third party ones.

The TreeView performance, in my experience, gets slow when you load nodes (in one shot, or on demand) without calling Begin/EndUpdate(), especially if your nodes are sorted, but if you call Begin/EndUpdate() correctly, you shouldn't really get performance issues related to the component itself.

Filini