Click here to Skip to main content
Click here to Skip to main content

Generic Tree Control

, 8 Feb 2009
Rate this:
Please Sign up or sign in to 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<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")));

List<filesystemnodevalue /> nodeValues = new List<filesystemnodevalue />();

tree.ActionIf(
    TreeManager<filesystemnodevalue />.Predicates.HasName(@"2", StringComparison.OrdinalIgnoreCase),
    TreeManager<filesystemnodevalue />.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! Wink | ;)

History

  • 2009-02-07 Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Martin Dahlstedt
Software Developer (Senior) B3IT
Sweden Sweden
No Biography provided
Follow on   Twitter

Comments and Discussions

 
GeneralConfusing Title PinmemberGLLNS9-Feb-09 8:08 
GeneralRe: Confusing Title PinmemberSympthom99-Feb-09 9:36 
Yes, definetly, I've just submitted a change for the title.
 
Thanks.
 
/Martin

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 8 Feb 2009
Article Copyright 2009 by Martin Dahlstedt
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid