Treeview Refresh Questions - c#

I have a Treeview that the user cannot edit. The way I'm trying to refresh is clearing the entire tree and re-adding all the nodes (and children). I'm accomplishing this by the following lines of code:
treeView.BeginUpdate(); //Freeze drawing
treeView.Nodes.Clear(); //Empty Tree
addAllNodes(); //This adds the nodes for the tree and sets their name/text property
treeView.EndUpdate(); //Unfreeze drawing
I've tried adding the Update and Refresh method before I call addAllNodes but hasn't made a difference. Doing the above gets me an error:
System.ArgumentException: Cannot add or insert the item 'NodeNameHere' in more than one place. You must first remove it from its current location or clone it
My first question is, what am I doing to cause this error and how can I properly refresh my tree?
My second question is, after the refresh is there any way I can restore the user's expanded nodes? (so that everything does not end up collapsed)

each node has a path (called something like .Path or .FullPath; dont have VS open right now). So you can walk your nodes before you clear them and record all the expanded nodes, then walk them again after refresh and see if the node's path matches a stored one.. if it does, expand it.

Related

Show records belonging to treeview's selected last node

I have a treeview that displays a few nodes, say 5 or so. When the final node is selected, I want to display records belonging to that final node (linked via a foreign key) inside of a listbox.
So the structure would be
Treeview Listbox
-1 -Object belonging to 5
--2 -Object belonging to 5
---3
----4
-----5
My question is if such a comperation between these two controls is even possible and if I'm going about it the smartest way.
I can't find anything about it (getting actual data from the last selected node in the treeview is already pretty hard on how to find a how to). Any tips in the right direction would be very appreciated.
It is possible to do this exactly the way you're trying to by using attached properties, but it's a bit of a clumsy way of going about it. What you really should be doing is using data binding.
Your TreeView is, presumably, bound to some kind of data structure in your view model (data context), and what class that is should (again, presumably) be able to easily ascertain whether or not a particular given item is the deepest/last one in the tree. So create a property in that class and bind the TreeView's SelectedItem to that, so that it gets updated whenever the user selects an item in the Tree. Next, create another property for your ListBox to bind to. When your first property gets set, it either sets this second property to the currently selected item if it's the last in the list, or sets it to null if it isn't.
By doing this you de-couple your logic from your view and you make something that's much easier to debug, test and modify in future.

Search items in a treeview C#

I was looking for a way to search for an item in a treeview in C#. I used the following question/answer TreeView search and it works great. The only issue I'm having now is that I can only look through nodes that have been expanded at least once. So I added a ExpandAll() and Collapse() to the Load event of my form so that I can find any item anywhere in the tree.
But now, I'm dealing with very big hierarchies and it takes 45+ seconds to load my dialog box because of that expandAll() call. Is there a way to do that search without calling expandAll() first?
Thanks a lot.

C# tree node show expand option even if node has no children

I want to show the expand option against a tree node even if the node has no children. Is this possible?
(The expand option being the little plus sign in a box to the left of the node.)
If your purpose is to dynamically load children nodes when expanding the best solution is to add a fake child nodes to all the leaf nodes. Then replace the fake node with real ones when needed.
Try it with property TreeNode.PopulateOnDemand = true; - this is ideal for dynamically created trees and it adds plus sign icon also to node with no children.
Sorry this is not a direct answer to your question, but I do feel it is relevant.
Why would you want to do this? It is confusing for the user.
(I have just been through exactly the same process in another environment and user feedback was "This item is broken and won't let me see what's underneath like the other ones do").
In short, I would recommend evaluating the requirement carefully before you proceed.

Empty expandable treenode in C#

I want to create an expandable empty treenode in C#, i.e. a treenode which is empty and has the [+] sign beside it. The reason is because initially it is empty, but once a node is clicked, I want to populate it with many child nodes.
The only problem I am facing is that empty treenodes aren't expandable, so I don't know what to do. Is there a way to solve this problem, or are there any workarounds?
You have to redraw the tree itself, or create an empty node and simply remove it when the parent node is expanded.
Personally, I'd go for option b). I've done this before, a while ago and thanks to the events raised by the TreeView it pretty easy to accomplish.
You can give the empty node a value like 'Loading...' so it gives some feedback to the user as well. :)
Add a dummy child node, and remove it when you expand.
Have a look at Josh Smiths excellent tutorial on treeviews. It allows lazy loading of child tree nodes by having a dummy node that is removed upon expansion.

Slow treeview in C#

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.
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.
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.
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 (Divelements SandGrid), that might (emphasis on might) support your desired mode of operation.
NOTE: The SandGrid is not supported by Divelements as of the end of July 2013.
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.
There is one way to make the TreeView perform much better and that is to create all sub-nodes and hook them together 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.
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.
This works for me (CSharp):
Visible = false;
...
Visible = true;
In my case(2000 nodes), it takes only 1~2 seconds to load the tree, which is much more quicker than any other ways.
It might work well in C++.

Categories