Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Horizontal Tree Control

4.57/5 (7 votes)
12 Jan 2008CPOL5 min read 1   1.2K  
A Vista Explorer like implementation of a control that represents hierachical data.

Introduction

This a custom tree control that tries to solve the problem of vertical display of hierarchical data.

Image 1

Background

I use Vista, and although I don't like many things about it, I adore the control that in Windows Explorer that helps parse the folder structure of a disk.

Image 2

When the user wants to select something from the hidden tree, a custom listbox appears like this:

Image 3

As you can see, for the explorer implementation, the control represents folders or special folders like My Documents, each with its custom icon. Extra graphics are also used, which I tried to mimic, but due to my lack of artistic skills, I could not exactly copy it; so if anyone has any suggestions, I would be very interested.

Control Dissection

Inner Data Representation

The inner representation of the hierarchical data is a tree like collection of NodeBase objects. NodeBase is abstract, and these are its valid implementations:

Image 4

Node is used for data. It holds the DisplayedText, the related Image, and a Tag property that holds the reference to the actual object for the node. This object is used to manipulate the activation event of the node. At this point, only one special node class is provided, the NodeSeperator. Special node classes will never appear in the bar. Their place is only in the selectors which display the sub nodes of each node.

Visual Representation

The visual representation of the above data is accomplished by displaying a sequence of controls. Each control basically derives from NodeBaseControl, which is abstract and has two separate implementations. NodeBaseControl ultimately holds a NodeBase object.

Image 5

NodeLinksControl is used only once in the bar, that is the tree, and it is the left most visible object always. It has an image which displays the image of the current active node, and can show a selection of various nodes, which may not be part of the hierarchical data, for example, a link.

NodeControl is used to represent every other node. The root node is a NodeControl. It displays a text and an image to show the selector. If there are no subnodes, then there is no need for a selector, so no image is displayed.

When many nodes are added, the horizontal tree hides like in the Vista's Explorer, closest to the root, and adds them to the NodeLinksControl selection. It also changes the image to show the fact that the nodes are hidden.

At any single point, when no node is selected, just by clicking on it or selecting it from a selector, the node becomes the last visible node, gets activated, and any subnode is removed from the visual tree. The Selector is a custom ListBox that displays the subnodes of a node. A separator node cannot be selected or highlighted. When a selector is requested, the tree is in:

C#
SelectionMode==true

until a node is selected, or the mouse is clicked outside a selector or a NodeBaseControl. In order to accomplish this, the selector is added to the Controls of the ParentForm and brought to the front. It also captures the mouse and redirects the required messages.

NodeBaseControl Dissection

Each NodeBaseControl is displayed by two side by side PartBase controls. PartBase is abstract, and its children are:

Image 6

Each part effectively knows the other part, and basically has a State that drives its appearance. Each of these part controls handles all the input functionality from the mouse, taking in regard the tree status or its other parts' States.

Using the Code

You can drag and drop the control into a form (in my demo, it is named horizontalTreeControl1). Full design time support is not implemented because I do not have the time to create it and because I'm not interested in learning how, because I believe the future lies with WPF. In the demo project, a typed dataset is built to simulate a folder structure similar to the one found in the disk. To fill the LinksControl, you use something like this:

C#
this.horizontalTreeControl1.LinksNode.Suspend();
this.horizontalTreeControl1.LinksNode.AddNode("Link1", 
                            Properties.Resources.Link1, null);
this.horizontalTreeControl1.LinksNode.AddSeperator();
this.horizontalTreeControl1.LinksNode.AddNode("Link2", 
                            Properties.Resources.Link2, null);
Node n = new Node();
this.horizontalTreeControl1.LinksNode.Resume();

Suspend and Resume are used in order to suppress the Paint event. Each node has a:

  • Displayed text
  • Image
  • An object reference that basically is used as the Tag property in Windows Forms controls

To implement the RootNode of the bar, use something like this:

C#
n = new Node();
n.DisplayText = ds.Folder[0].Name;
n.Tag = ds.Folder[0];
n.Image = Properties.Resources.Root;
this.horizontalTreeControl1.RootNode = n;

Now, there are two ways to add the subnodes in each node. You either use the AddNode in each node like above for the LinksNode, or capture the NodeActivated event to dynamically add the nodes at the specified time. This event is called for a node when it is activated. This is very useful when there is no reason to add the entire data to the tree, or the data changes over a period of time. Capture the event like this:

C#
this.horizontalTreeControl1.NodeActivated += 
    new Sarafian.Framework.ClientSide.UI.Resources.HorizontalTree.
    DelegateEnum.DelegateNodeActivated(horizontalTreeControl1_NodeActivated);

...and implement it like this:

C#
void horizontalTreeControl1_NodeActivated(Node node)
{
    if (node.Tag is DataRow)
    {
        node.ClearNodes();
        DsTest.FolderRow row = (DsTest.FolderRow)node.Tag;
        node.Suspend();
        foreach (DsTest.FolderRow subRow in 
                 row.GetChildRows(this.ds.Folder.ChildRelations[0]))
        {
            node.AddNode(subRow.Name, Properties.Resources.Folder, subRow);
        }
        node.Resume();
    }
    else
    {
        //MessageBox.Show(node.DisplayText);
    }
}

As you can see, AddNode is again used. This event is also used to know when a node is activated. For example, if the node was a folder and you wanted its files. The Tag property holds the actual data of the node, which can be any object.

Points of Interest

This control does not fully implement the path selector of Windows Vista Explorer. Another control with a TextBox and an auto-fill which will be activated in front of the horizontal tree control will be needed. This control can display any type of hierarchical data.

At development time, many mistakes where made from my part that led to several refactoring attempts, so the code is not perfect. There are some files that have been added to the class library from other class libraries, I imported them here to make the solution simpler. These files are:

  • Percent.cs
  • Rectangle.cs
  • Point.cs

More info is available at my post in my blog.

History

  • Version 1.0 creation.

License

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