Generic Tree Control





0/5 (0 vote)
An article on a generic tree collection
Introduction
This article shows how to make a generic tree collection base and implement specific trees based on the tree collection. The abstract tree type implements IEnumerable<TreeNode<TNodeValue>>
, ICollection<TreeNode<TNodeValue>>
, IList<TreeNode<TNodeValue>>
, IEnumerable
, ICollection
, IList
, ISerializable
. This gives you a number of scenarios of usage.
Using the Code
Ok, here we go.
The example below creates a tree called BasicTree
and lets it inherit from the base type Tree<TNodeValue>
. We also create a node type for the tree called BasicNodeValue
that inherits from the base type TreeNodeValue
.
This is the only thing we need to get started using the tree collection functionality.
public sealed class BasicTree : Tree<BasicNodeValue>
{
public BasicTree(BasicNodeValue nodeValue)
: base(nodeValue)
{
}
}
public sealed class BasicNodeValue : TreeNodeValue
{
public BasicNodeValue()
: base()
{
}
public BasicNodeValue(String name, String path) :
base(name, path)
{
}
}
Let's use the BasicTree
further in the next code sample below:
Here we instantiate the BasicTree
and manually populate the tree with nodes. Then we foreach
all nodes in the tree collection and print out the path.
//Instantiate the basic tree.
BasicTree tree = new BasicTree(new BasicNodeValue(@"Root", @"\"));
//Add the item 2 to the file basic tree root folder.
TreeNode<BasicNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new BasicNodeValue(@"2", @"\2"));
//Add the item x to the item 2.
tree.AddLast(parentNode1_2, new BasicNodeValue(@"1", @"\2\1"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"2", @"\2\2"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"3", @"\2\3"));
tree.AddLast(parentNode1_2, new BasicNodeValue(@"4", @"\2\4"));
//Print the path of the basic items.
foreach (TreeNode<BasicNodeValue> basicItem in tree)
{
String path = String.Format(@"{0}", basicItem.RelativePath);
Console.WriteLine(path);
}
Ok, this wasn't quite so exciting perhaps? Let's take this a bit further.
In the example below (also included in the downloadable demo code) we instantiate two trees of type FileSystemTree
and populate them manually (we can also build the index automatically by executing the tree.BuildIndex();
method). We have now created two trees that we can compare on whatever predicate we feel like. The example below compares to relative path but you can choose your own compare method, e.g. path combined with hash.
//Instantiate the file system tree.
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
//Add the folder 2 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
//Add the folder x to the folder 2.
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
//Instantiate the file system tree.
FileSystemTree tree2 = new FileSystemTree(new DirectoryInfo(@"c:\temp2"));
//Add the folder 2 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode2_2 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\2")));
//Add the folder 3 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode2_3 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3")));
//Add the folder 4 to the folder 2.
tree2.AddLast(parentNode2_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
//Add the folder x to the folder 2.
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\1")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\2")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\3")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\4")));
//Compare the trees.
CompareTree<FileSystemNodeValue> resultTree =
tree.CompareTo(tree2, FileSystemTreeManager.Predicates.HasSameName());
foreach (TreeNode<CompareNodeValue<FileSystemNodeValue>> compareNode in resultTree)
{
String result = String.Format(@"{0} {1}",
compareNode.RelativePath, compareNode.NodeValue.CompareResult);
Console.WriteLine(result);
}
We can also execute a LINQ query on the tree:
//Instantiate the file system tree.
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
//Add the folder 2 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
//Add the folder x to the folder 2.
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
var nodes = from TreeNode<FileSystemNodeValue> node in tree
where node.Name == @"2" || node.Name == @"3"
select node;
//Print the path of the file items.
foreach (TreeNode<FileSystemNodeValue> node in nodes)
{
Console.WriteLine(String.Format(@"Name: {0}", node.Name));
}
With the Tree<TNodeValue>
, we can also execute an action on the nodes if a predicate is true
. In the example below, we print out in the console how many nodes have the name '2
':
//Instantiate the file system tree.
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
//Add the folder 2 to the file system tree root folder.
TreeNode parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
//Add the folder x to the folder 2.
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
List nodeValues = new List ();
tree.ActionIf(
TreeManager .Predicates.HasName(@"2", StringComparison.OrdinalIgnoreCase),
TreeManager .Actions.GetList(nodeValues), false);
Console.WriteLine(String.Format(@"Count: {0}", nodeValues.Count));
The last example below shows how to merge two trees:
//Instantiate the file system tree.
FileSystemTree tree = new FileSystemTree(new DirectoryInfo(@"C:\temp"));
//Add the folder 2 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode1_2 = tree.AddLast
(tree.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2")));
//Add the folder 1 to the folder 2.
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\1")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\2")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\3")));
tree.AddLast(parentNode1_2, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp\2\4")));
//Instantiate the file system tree.
FileSystemTree tree2 = new FileSystemTree(new DirectoryInfo(@"c:\temp2"));
//Add the folder 2 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode2_2 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\2")));
//Add the folder 3 to the file system tree root folder.
TreeNode<FileSystemNodeValue> parentNode2_3 = tree2.AddLast
(tree2.RootNode, new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3")));
//Add the folder x to the folder 2.
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\1")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\2")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\3")));
tree2.AddLast(parentNode2_3,
new FileSystemNodeValue(new DirectoryInfo(@"C:\temp2\3\4")));
//Merge the trees.
Tree<FileSystemNodeValue> mergedTree = tree.Merge(tree2);
foreach (TreeNode<FileSystemNodeValue> mergedNode in mergedTree)
{
String result = String.Format(@"{0}", mergedNode.RelativePath);
Console.WriteLine(result);
}
Points of Interest
All code in the article is provided in the demo. If you find anything interesting or have some great ideas of how to improve the code, please send me a note!
May the source be with you! ;)
History
- 2009-02-07 Initial release