This a custom tree control that tries to solve the problem of vertical display of hierarchical data.
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.
When the user wants to select something from the hidden tree, a custom listbox appears like this:
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.
Inner Data Representation
The inner representation of the hierarchical data is a tree like collection of
NodeBase is abstract, and these are its valid implementations:
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.
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
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:
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 is displayed by two side by side
PartBase is abstract, and its children are:
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'
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:
Node n = new Node();
Resume are used in order to suppress the Paint event. Each node has a:
- Displayed text
- 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:
n = new Node();
n.DisplayText = ds.Folder.Name;
n.Tag = ds.Folder;
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:
...and implement it like this:
void horizontalTreeControl1_NodeActivated(Node node)
if (node.Tag is DataRow)
DsTest.FolderRow row = (DsTest.FolderRow)node.Tag;
foreach (DsTest.FolderRow subRow in
node.AddNode(subRow.Name, Properties.Resources.Folder, subRow);
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:
More info is available at my post in my blog.