If you use the Node.Name attribute to set the actual path of the element, then merging is somewhat simple.
First, create a TreeNodeCollection extension similar to this (this is necessary to use the Case Sensitive Find () method for TreeNodeCollection, which ensures that a unique path can be unique to Case as well):
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace TreeViewApp { public static class TreeNodeCollectionExtensions { public static TreeNode[] FindExact(this TreeNodeCollection coll, string keytofind) { TreeNode[] retval; if (String.IsNullOrWhiteSpace(keytofind) || coll == null) { retval = new TreeNode[0]; } else { TreeNode[] badfinds = coll.Find(keytofind, true); List<TreeNode> goodfinds = new List<TreeNode>(); foreach (TreeNode bad in badfinds) { if (bad.Name == keytofind) goodfinds.Add(bad); } retval = goodfinds.ToArray(); } return retval; } } }
Secondly, fill the tree structure with your source nodes ...
Thrid, fill the tree structure with target nodes ...
Fourth, create an empty tree.
and then, it's that simple:
private void btn_Merge_Click(object sender, EventArgs e) { //first merge foreach (TreeNode sourceNode in this.treeview_Source.Nodes) { FindOrAdd(sourceNode, ref this.treeview_Merged); } //second merge foreach (TreeNode targetNode in this.treeview_Target.Nodes) { FindOrAdd(targetNode, ref this.treeview_Merged); } } private void FindOrAdd(TreeNode FindMe, ref TreeView InHere) { TreeNode[] found = InHere.Nodes.FindExact(FindMe.Name); //if the node is not found, add it at the proper location. if (found.Length == 0) { if (FindMe.Parent != null) { TreeNode[] foundParent = InHere.Nodes.FindExact(FindMe.Parent.Name); if (foundParent.Length == 0) InHere.Nodes.Add((TreeNode)FindMe.Clone()); else foundParent[0].Nodes.Add((TreeNode)FindMe.Clone()); } else InHere.Nodes.Add((TreeNode)FindMe.Clone()); } else { //if the item was found, check all children. foreach (TreeNode child in FindMe.Nodes) FindOrAdd(child, ref InHere); } }
Again, this solution only works if you have unique paths ... with the extension, it also takes into account uniqueness at the case level.
I posted it here in the hope of helping someone who, like me, had to look for a solution for several days in a row without success and had to create my own.