Windows Forms • November 2019

Keep nodes expanded when reloading a tree view.

How do you refresh a tree view and keep the expanded nodes expanded? Google searches seemed to say, "you cannot." Rubbish! Here's how.

The problem:

In .net, a treeview is a class like everything else. This class contains a list of nodes, and each node itself contains a list of nodes. This continues as far down as you care to go. Building a tree view is pretty easy and there are lots of web sites to tell you how to do that.

Each of these nodes has a member that's true if the node is expanded. The trouble comes as the first thing you do when refreshing a list is clear the list(s) of nodes. This deletes the members and the information about expanded nodes is lost forever and ever amen.

So all you need to do is keep some sort of list of the expanded nodes and some unique identifier for each node. A dictionary is a very handy way to keep information that you will want to look up later. There are two things we might use as the key to this list, one might be the tag of the node, assuming you've stored a primary key or something there for other purposes. The other is a handy member called "fullpath," that contains the text of all the parent nodes as well as the text of this node.

The dictionary

Dictionary to store expanded node list:
										
Dictionary<string, int> NodesExpanded = new Dictionary<string, int>();
										
									

We'll just keep a list of expanded nodes in this dictionary so we can look nodes up later to see if we should expand them. It doesn't really matter what we store in this list since we're only interested in the presence or absense of the key. I put int's, but boolean's might take less memory.

.net gives us events for expanding and contracting nodes. All we need to do is maintain the dictionary.

Expand and Contract events
										
private void treeView1_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
{
  if (NodesExpanded.ContainsKey(fp))
	  NodesExpanded.Remove(e.Node.FullPath);
}

private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
	string fp = e.Node.FullPath;
	if (NodesExpanded.ContainsKey(fp) == false)
		NodesExpanded.Add(fp, 1);
}
										
									

Now, we have a dictionary that always lists the expanded nodes.

So loading the tree view is pretty easy. Just clear the list of nodes and load it from your database or wherever the data comes from, checking the dictionary to see if the node needs to be expanded.

Otherwise, you could quickly loop through the nodes of the reloaded tree. This is a simple recursive call, though you could cleverly make it iterative if you're worried about the depth of the tree.

Loop through nodes to see if they should expand:
										
// a little method to call on each node
private void ExpandNode(TreeNode pNode)
{
	if (NodesExpanded.ContainsKey(pNode.FullPath)) pNode.Expand();
	foreach (TreeNode tn in pNode.Nodes) ExpandNode(tn);
}

// Call the method on each node after you've reloaded the list
foreach (TreeNode tn in treeView1.Nodes) ExpandNode(tn);