6
votes

I have a program that contains a TreeView. All of my nodes except for the root and two nodes under the root are loaded from a database.

When the user adds data to the database it must be automatically added to the TreeView. I can successfully do this by clearing all the nodes, add the default nodes and add all the data including the new one to my TreeView, but all nodes of the new TreeView are collapsed.

Our client wants to retain all expanded nodes but still add the new data he just added. Is there any way to know all expanded nodes and expand it again once collapsed or refreshed? Thank you for any response.

6
Pa-post po ng codes. Sample codes. :pdevrox
Why do you need to clear and re-add instead of just adding/changing a single node? If you have thousands of nodes your program is going to...Alvin Wong
What exactly you need to know you want to save you tree view node collection before clear it up and then returning it's appearance....saeed

6 Answers

5
votes

Hi As i understand you want to save you tree view map after you refresh tree view (by adding new data or even remove some) you want to expand all expanded nodes the other are collapsed by default. Solution is:

1) save expanded tree view nodes before refresh

2) refresh tree view data (notice that if you are removing a node , remove it from saved list as well)

3) set tree view map which saved before

saving tree view map (only expanded nodes)-> This Code Look through a tree view node collection and saves expanded nodes Names in a string list

List<string> collectExpandedNodes(TreeNodeCollection Nodes)
        {
            List<string> _lst = new List<string>();
            foreach (TreeNode checknode in Nodes)
            {
                if (checknode.IsExpanded)
                    _lst.Add(checknode.Name);
                if (checknode.Nodes.Count > 0)
                    _lst.AddRange(collectExpandedNodes(checknode.Nodes));
            }
            return _lst;
        }

Now you have collected expanded nodes name in a list and you want to gain back your tree view appearance you need 2 functions a function which retrieves node by a name and a function which expand selected node and it's parents the following codes does :

This function retrieves a pointer to selected node Name if node is exist in Tree Node Collection

TreeNode FindNodeByName(TreeNodeCollection NodesCollection , string Name)
        {
          TreeNode returnNode = null; // Default value to return
          foreach (TreeNode checkNode in NodesCollection)
            {
                if (checkNode.Name == Name)  //checks if this node name is correct
                    returnNode = checkNode;
                else if (checkNode.Nodes.Count > 0 ) //node has child
                {
                    returnNode = FindNodeByName(checkNode.Nodes , Name);
                }

              if (returnNode != null) //check if founded do not continue and break
              {
                  return returnNode;
              }

            }
            //not found
            return returnNode;
        }

and This Function Expand node and it's parents

void expandNodePath(TreeNode node)
        {
            if (node == null)
                return;
            if (node.Level != 0) //check if it is not root
            {
                node.Expand();
                expandNodePath(node.Parent);
            }
            else
            {
                node.Expand(); // this is root 
            }

        }

and the following show you the usage of the functions

private void button4_Click(object sender, EventArgs e)
        {
            //saving expanded nodes
            List<string> ExpandedNodes = new List<string>();
            ExpandedNodes = collectExpandedNodes(treeView1.Nodes);
            //resetting tree view nodes status to colapsed
            treeView1.CollapseAll();

            //Restore it back
            if (ExpandedNodes.Count > 0)
            {
                TreeNode IamExpandedNode;
                for (int i = 0; i < ExpandedNodes.Count;i++ )
                {
                    IamExpandedNode = FindNodeByName(treeView1.Nodes, ExpandedNodes[i]);
                    expandNodePath(IamExpandedNode);
                }

            }

        }
3
votes

For expanding all the nodes use below code

treeView1.ExpandAll();

For expanding selected node use below code

treeView1.SelectedNode.ExpandAll();

For expanding particular node use below code

treeView1.Nodes[Index].Expand();
2
votes

Assume the Nodename is unique.

  • Using an Database the nodename can be the unique rowid of an table
  • The state of the tree (List) can simply saved with an Formatter (e.g. BinaryFormatter)
    • if User want to save status

Only save the expandedstate

private List<string> SaveTreeState(TreeNodeCollection nodes)
{
  List<string> nodeStates = new List<string>();
  foreach (TreeNode node in nodes)
  {
    if (node.IsExpanded) nodeStates.Add(node.Name);
    nodeStates.AddRange(SaveTreeState(node.Nodes));
  }
  return (nodeStates);
}

Let the treeview do the work finding the nodes for restore

private void RestoreTreeState(TreeView tree, List<string> treeState)
{
  foreach (string NodeName in treeState)
  {
    TreeNode[] NodeList = treeView1.Nodes.Find(NodeName, true);
    if (NodeList.Length > 0) // only if node after reload is avail
      NodeList[0].Expand(); 
  }
}

Using:

List<string> StateList = SaveTreeState(treeView1.Nodes);
... // reload
RestoreTreeState(treeView1, StateList);
1
votes

To simply expand nodes you can try this following code

 private void button1_Click(object sender, EventArgs e)
{
  treeView1.Nodes.Add(new TreeNode("New Node",
    new TreeNode[2] { new TreeNode("Node1"), new TreeNode("Node2") }));
  treeView1.Nodes[1].Expand();
}

Hope that helps

1
votes

It's very simple. Below, you can see my recursive version:

//List of storage ids of expanded nodes
List<int> expandedNodeIds = new List<int>();
//call recursive fun for our tree
CollectExpandedNodes(tree.Nodes);
//recursive fun for collect expanded node ids
private void CollectExpandedNodes(TreeListNodes nodes)
{
   foreach (TreeListNode node in nodes)
   {
      if (node.Expanded) expandedNodeIds.Add(node.Id);
      if (node.HasChildren) CollectExpandedNodes(node.Nodes);
   }
}
1
votes

I know this post is old but if the tree is deep it might not be a good idea to use a recursive traversal of the tree. Since I have not seen any anwser using a non-recursive way here is a solution for doing getting expanded nodes without impacting performance.

public static IEnumerable<TreeNodeAdv> CollectExpandedNodes(this TreeNodeAdv root)
{
    Stack<TreeNodeAdv> s = new Stack<TreeNodeAdv>();
    s.Push(root);
    while (s.Count > 0)
    {
        TreeNodeAdv n = s.Pop();

        if (n.IsExpanded)
            yield return n;

        foreach (var child in n.Children.ToArray().Reverse())
        {
            s.Push(child);
        }
    }
}

To use this method, you can do the following:

foreach (TreeNodeAdv expandedNode in yourTreeView.Root.CollectExpandedNodes())
{
    //Do processing on the expanded node or add in list.
}

This extension method uses Deep-First traversal in post-order along with the yield keyword to generate an IEnumerable collection.