Click here to Skip to main content
Email Password   helpLost your password?

TreeViewAdv in Multi-Column mode

Introduction

Working on several different projects, I was needed to display and edit hierarchical data. Of course, the first thing you will do is to use the standard .NET TreeView control. It works pretty well if you only need basic features. But learning this control to do something more complex is not an easy job. I could not find an alternative TreeView control which is free and fully meets my needs, so finally I decided to write my own.

The architecture of this control comes mainly from the Java Swing component, with some modifications. These are the key features of the TreeViewAdv control:

The following screenshots illustrate the TreeViewAdv features:

Drag&Drop highlighting

Multiselection

Using ComboBox to edit node

Model-View Architecture

I really like the Model-View pattern, and decided to use it in this control. The main idea of this pattern is to split the model (business object) from its visualization (control). If the model changes, it notifies the view by firing corresponding events. The view asks the model for details, if needed, and displays the changes. The model is described by ITreeModelInterface:

public interface ITreeModel
{
    IEnumerable GetChildren(TreePath treePath);
    bool IsLeaf(TreePath treePath);

    event EventHandler<TreeModelEventArgs> NodesChanged; 
    event EventHandler<TreeModelEventArgs> NodesInserted;
    event EventHandler<TreeModelEventArgs> NodesRemoved; 
    event EventHandler<TreePathEventArgs> StructureChanged;
}

It�s very simple, and you need to implement only two methods. GetChildren should return the list of child nodes of the specified parent (empty for root nodes). IsLeaf method tells TreeView whether it should try to read child nodes of the specified parent. If you wish TreeView to dynamically track model changes, you need to use one of several events of the ITreeModel interface. The most common is the StructureChanged event, which cause the TreeView to fully refresh the specified node (or empty, for the whole model). For example, see the default implementation of the ITreeModel interface � the TreeModel class.

To specify the exact node in the model, TreePath class is used. It stores the path from the root to the node, in the FullPath property.

public class TreePath
{
    public object[] FullPath{ get; }
    public object LastNode{ get; }
    public object FirstNode{ get; }
}

Using TreeView

In the source code, you can find two examples of how to use TreeViewAdv. The simplest way is to use TreeModel. All you need is to populate it with data and display it in the view:

_model = new TreeModel();
_model.Nodes.Add(new Node("Root"));
_tree.Model = _model;

The Node class, which is used in TreeModel, contains only the �Text� and �IsChecked� properties. If you need additional properties, you can create an ancestor of the Node class and use it in TreeModel.

But to use the full power of the TreeViewAdv, you should create your own realization of the ITreeModel interface. See the folder browser presented in the source code, for an example.

Customizing TreeView

There are a number of properties which help to customize the look and behavior of the TreeView. The main ones are:

NodeControls

The standard TreeView can display only one icon, CheckBox, and Label for each node. In TreeViewAdv, you can use any number of NodeControl. All controls must inherit from the �NodeControl� abstract class. Inherited classes should contain the code to draw the control and the code to respond on user actions � mouse and keyboard events.

NodeControl

This is the class diagram of all NodeControls provided by the library:

Class diagram

The BindableControl class provides a �DataPropertyName� which is used in the control to read and write data to the node. All that you need is to specify the name of the property of your class.

Terms and Conditions

The TreeViewAdv control is provided as free software with open source code. You can use it in your applications if the original copyright is kept.

The latest version of TreeViewAdv is always available here. Please feel free to add your comments and suggestions in the forum there.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionIs it possible to display bitmaps in columns?
usg
12:56 4 Mar '10  
Hi,

I have a multicolumn treeview application in mind, which should display a couple of small bitmaps (bmp, tif, jpg, whatever...) in columns 2..n (~ 5 columns) - one bitmap per column.
As far as I understand the control(just found it today), it does not provide this option. Columns 2..n are pure text. Right?

Do you plan a version 1.8 which supports that feature? Wink

Anyhow (although already stated many times): GREAT tool!!

U.
GeneralError in TreeViewAdv
blacksaifer
16:35 24 Feb '10  
Andrey Hi. Great control, thanks. I guess found 1 error in TreeViewAdv class:
event handler _model_StructureChanged

code

//-------------------------------------
var list = new Dictionary <object, object>();

SaveExpandedNodes(node, list);
ReadChilds(node);
RestoreExpandedNodes(node, list);
//-------------------------------------

I tried to "bind" this control to datatable as shown in SampleApp.DataTableTreeExample and found problem in redrawing nodes after drug and drop operation.

The eye of the problem:
1. SaveExpandedNodes(node, list);
First you save objects links into dictionary

2. ReadChilds(node);
Then you recreate child nodes list

3. RestoreExpandedNodes(node, list);
After this you are trying to compare old objects with new objects like this
list.ContainsKey(node.Tag)

But objects are totaly different. Other objects, other links, list.ContainsKey(node.Tag) == False always.

To fix this error I change code in functions SaveExpandedNodes and RestoreExpandedNodes. New code:

private void RestoreExpandedNodes2(TreeNodeAdv node, Dictionary<object, object> list)
{
if (node.Tag != null && list.ContainsKey((node.Tag as Node).ToString()))
{
node.IsExpanded = true;
foreach (var child in node.Children)
RestoreExpandedNodes2(child, list);
}
}

private void SaveExpandedNodes2(TreeNodeAdv node, Dictionary<object, object> list)
{
if (node.IsExpanded && node.Tag != null)
{
list.Add((node.Tag as Node).ToString(), null);
foreach (var child in node.Children)
SaveExpandedNodes2(child, list);
}
}

I override ToString() method in my DataRowNode:Node class to "generate" unique node identifier (primary key in my circumstance is ok).
Of course you can fix this error in other way.
QuestionCheckboxes are not in the correct position when moving columns
Member 1678495
8:00 15 Jan '10  
Hello,

in the sample app I've moved the name column - afterwards the checkboxes are still on the left side.
Have a look at this screenshot:

http://s11b.directupload.net/images/100115/3iifkxb5.png[^]

Regards!
GeneralIsVisibleValueNeeded set to true but also want the Parents to be visible as well
turinreza
10:33 14 Jan '10  
i used IsVisibleValueNeeded and set it to true on one of the children..
(ie : _nodeTextBox.IsVisibleValueNeeded += CheckIndex; )

the treeView renders the child but the parents are not visible..

I want to show the parents visible of that child but i can't find a way
to make them visible again because their IsVisibleValueNeeded is set to false
because checkIndex sets them to false.

Seems like the only way to go is downward and checkIndex on everychild and
if there is a true condition.. this is quadratic solution.
I prefer to just walk back up the tree from that visible child and make
that Node and all it's controls visible.

any thoughts?
GeneralRe: IsVisibleValueNeeded set to true but also want the Parents to be visible as well
turinreza
11:13 14 Jan '10  
i tried this code in CheckIndex

TreeNodeAdv thisNode = e.Node;
TreeViewAdv parent = (sender as NodeControl).Parent;

parent.EnsureVisible(thisNode);

whenever i e.Value is set to true.

but i get stack overflow...
General[My vote of 1] How do I enable tri-state checkboxes?
Mike Martell
10:31 18 Dec '09  
It looks like this is supported via NodeCheckBox.ThreeState. However, I don't know how to enable it. Is there a way to turn this on in the FolderBrowser example?

Great control BTW.
Generalnodecontrol textinput box mapped to int value of datarow
turinreza
3:16 16 Dec '09  
i get object system.string cannot be converted to system.int32
in bindablecontrol.cs 's Value (setting a value)

the textbox comes in as a string but my model datarow
isn't even being hit before i can cast it to an int..

the code in bindablecontrol looks at the binding and determines it to be int32
and doesn't convert the value to an int when setting it..

Can we only bind strings?
Generalcheckbox cant check iwhen using Columns?
turinreza
13:42 14 Dec '09  
in the FolderBrowser example. unable to check the checkboxes
(using columns is different?)
GeneralAssociate a context menu with each NodeControl of a TreeViewAdv : possible ?
michel_page38
11:04 21 Nov '09  
In DataTableTreeExample.cs, there is an example of a context menu associated with a TreeViewAdv. However, I would like to have a context menu associated with each NodeControl of the TreeViewAdv. Is it possible to do that ?
GeneralLatest code no worky
mjanulaitis1234
8:21 19 Nov '09  
I got the latest code off Source Forge and it doesn't workFrown Well a portion of the code doesn't work. From the Folders Sample when you check a box, the box doesn't draw the check.
GeneralImproved GetRowAt(Point point)
YevgenBorodkin
1:05 18 Nov '09  
Display column header and click on the bottom part of the header.
First column will be selected instead of ColumnAutoWidth...

public int GetRowAt(Point point)
{
// Custom modification: (Bug) Selecting first row
if (point.Y <= _treeView.ColumnHeaderHeight)
return -1;
// Custom modification
point = new Point(point.X, point.Y + (_treeView.FirstVisibleRow * _rowHeight) - _treeView.ColumnHeaderHeight);
return point.Y / _rowHeight;
}

AnswerImproved AutoSizeColumn function
ub3rst4r
9:54 8 Nov '09  
Just in case anyone is looking for better code for the AutoSizeColumn, I have created a function that works a lot like the one that the listview has. This will accept the ColumnHeaderAutoResizeStyle so it can resize to the column header text and/or the node controls. You can also just use the AutoSizeColumns() function so you don't have to iterate through the columns.

NOTE: To install the code, open TreeViewAdv.Draw.cs and add the code to the top of the file. Then simply call TreeViewAdv.AutoSizeColumns(ColumnHeaderAutoResizeStyle) respectively, through your windows form code.

        public void AutoSizeColumns(ColumnHeaderAutoResizeStyle headerAutoSize)
{
if (this.Columns.Count <= 0)
throw new Exception("Treeviewadv doesn't contain any columns");

foreach (TreeColumn tc in this.Columns)
this.AutoSizeColumn(tc, headerAutoSize);
}

public void AutoSizeColumn(TreeColumn col, ColumnHeaderAutoResizeStyle headerAutoSize)
{
if (!Columns.Contains(col))
throw new ArgumentException("column is not a part of treeviewadv", "col");

if (headerAutoSize == ColumnHeaderAutoResizeStyle.None)
return;

foreach (TreeNodeAdv tna in this.VisibleNodes)
{
foreach (NodeControlInfo nci in this.GetNodeControls(tna))
{
if (nci.Control.ParentColumn == col)
{
int nWidth = 0;

Size sizeCntrl = nci.Control.GetActualSize(tna, this._measureContext);

if (col.Index == 0)
nWidth += nci.Bounds.X;

if (!sizeCntrl.IsEmpty)
col.Width = Math.Max(col.Width, (sizeCntrl.Width + nWidth));

if (headerAutoSize == ColumnHeaderAutoResizeStyle.HeaderSize)
{
Size sizeText = TextRenderer.MeasureText(col.Header, this._measureContext.Font);

if (!sizeText.IsEmpty)
col.Width = Math.Max(col.Width, sizeText.Width);
}
}
}
}
}

GeneralRe: Improved AutoSizeColumn function
Steve Towner
18:15 14 Nov '09  
Thanks. Just what I needed.

I did change to the following so that the columns shrink to fit the data.

Thanks,

Steve

        public void AutoSizeColumn(TreeColumn col, ColumnHeaderAutoResizeStyle headerAutoSize)
{
if (!Columns.Contains(col))
throw new ArgumentException("column is not a part of treeviewadv", "col");

if (headerAutoSize == ColumnHeaderAutoResizeStyle.None)
return;

int TempWidth = 0; // Steve - added variable
foreach (TreeNodeAdv tna in this.VisibleNodes)
{
foreach (NodeControlInfo nci in this.GetNodeControls(tna))
{
if (nci.Control.ParentColumn == col)
{
int nWidth = 0;

Size sizeCntrl = nci.Control.GetActualSize(tna, this._measureContext);

if (col.Index == 0)
nWidth += nci.Bounds.X;

if (!sizeCntrl.IsEmpty)
TempWidth = Math.Max(TempWidth, (sizeCntrl.Width + nWidth)); // Steve - modified
if (headerAutoSize == ColumnHeaderAutoResizeStyle.HeaderSize)
{
Size sizeText = TextRenderer.MeasureText(col.Header, this._measureContext.Font);

if (!sizeText.IsEmpty)
TempWidth = Math.Max(TempWidth, sizeText.Width); // Steve - modified
}
}
}
}
col.Width = TempWidth; // Steve - added
}

GeneralRe: Improved AutoSizeColumn function
YevgenBorodkin
0:49 18 Nov '09  
Added for sort mark textWidth += col.SortMarkSize.Width + 8; Used textWidth + 3 for excluding "..." in column name

if (headerAutoSize == ColumnHeaderAutoResizeStyle.HeaderSize)
{
Size sizeText = TextRenderer.MeasureText(col.Header, _measureContext.Font);

if (!sizeText.IsEmpty)
{
int textWidth = sizeText.Width;
if (col.SortOrder != SortOrder.None)
textWidth += col.SortMarkSize.Width + 8; //SortOrderMarkMargin
tempWidth = Math.Max(tempWidth, textWidth + 3);
}
}

QuestionIs it possible to make tree column frozen?
Marcin Smolka
5:07 29 Oct '09  
Is it possible to make tree column frozen? To not scroll it. Similar to datagridview behavior?

Kind Regards
Marcin
GeneralMore Documentation - Cannot get text of node to display
Killme007007
19:41 13 Oct '09  
I have read another message regarding this but it does not apply/work for me.

All I want to do is add some nodes and see them with a three-state checkbox. I get the checkbox (by adding it to NodeControls) but I cannot check/uncheck it. Plus I do not see the text of the node.

I follow what I see in the Simple example to no success.

Anyone have any cohesive documentation or an example to get this control to work?
AnswerRe: More Documentation - Cannot get text of node to display [modified]
masonmccuskey
15:40 28 Feb '10  
To get text to show up, add a NodeTextBox to NodeControls in the designer, then derive from Aga.Controls.Tree.Node and go crazy in the ToString() override (or just return Text if you want to display the text of the node).

This is generally a good control, but it's not a drop-in. To really get it you have to study its architecture, as well as the designer side of the simple example. For the impatient: derive a class from Node, for every column you want, implement a property, say, public string MyColumn { get { ... } }, then in the designer, put "MyColumn" in the DataPropertyName of the nodeControl, and set up the Column # for that NodeControl. To get icons on your tree items, use a NodeIcon, and point its DataPropertyName at a property of your derived Node class that returns an Image.

To the author: if you put everything in code, even what you'd usually set up inside the designer, it may be a bit easier for people to learn. First time I used it, I didn't even know about the central role that NodeControls play until I had stepped into the drawing code!

(To be fair though, I hadn't read the whole article carefully. My usual approach with code from here is "drop in and see if it works first, read the article for specifics later").
modified on Sunday, February 28, 2010 9:00 PM

GeneralPerhaps a content bug in the TreeView control (SMALL)
LimitedAtonement
7:36 5 Oct '09  
Dear Mr. Gliznetsov,

I see in the BindableControl.cs file (id est Abstract Aga.Controls.Tree.NodeControls.BindableControl (: NodeControl), the first property, DataPropertyName set method says if the property name is null, change it to empty, then assign to value:
if (_propertyName == null)
_propertyName = string.Empty;
_propertyName = value;
But I can't imagine this being the intended code. Surely you meant:
_propertyName = value;
if (_propertyName == null)
_propertyName = String.Empty;
, right? Of course, the `lazy' way to do this would be
get { return (_propertyName ?? String.Empty); }
set { _propertyName = value; }
Good work on the control! Really great work. Implementing it is a little difficult because of the spartan use of comments (that is no comments), but the code is clear enough to step through and figure out what has happened. I'm not using the designer, so that NodeControls was hard to find. I was thinking, "How the heck do I get the multiple columns, then once gotten, how the heck to I get my text in there!!?" But I'm getting it by looking VERY CLOSELY at the example. Perhaps I could help you comment some of the code?

In Christ,
Aaron Laws

modified on Tuesday, November 17, 2009 2:29 PM

GeneralHow to disable the default tooltip of expandable node in asp.net 2.0
hetak\lkumar
3:03 30 Sep '09  
How to disable the default tooltip of expandable node in asp.net 2.0
GeneralPerformance Problem with 10.000 or More Nodes
Michael Biener
3:47 28 Sep '09  
Hello,

I have a Problem I have 10.000 Nodes in the Treeview, each row has between 5 and 10 Subnodes. On Expanding the a Node it thakes up to 8 Seconds. I did some time measurement. the Most time is used in the Loop in CreateRowmap Method of TreeViewAdv class.

I did some Debugoutput to the Method and Submethods/Properties like ExpandedNodes and NextNode and noticed there is sometimes a pause of 10 milliseconds, where the process seams to be standing. The Pause occurs on different Codeparts. how more the loop goes forward, then moretime the pause occurs. Wenn Starting the loop the pause occurs every 50 rows, when processed more then 1.000 rows the pause ocours all 25 rows and so on.

has somebody any idea?

Kind Regards
AnswerRe: Performance Problem with 10.000 or More Nodes
yetibrain
1:54 30 Sep '09  
hi Michael,

you might consider to use a TreeListView running in virtual mode, in order to view a huge amount of data. Please have a look at the VirtualModeTreeListView.
There is an example model that collapses a line with 65535 childlines in less than a second.

yetibrain

GeneralAdvTreeView Multi Line Header
areB
5:17 19 Sep '09  
Can someone help Please.
How is it possible to have a Multi line Header for each columns for the tree view ?

regards
GeneralTreeView used in DBSourceTools
NathanRozentals
4:23 17 Sep '09  
Hi Andrey,
Just wanted to let you know that your Advanced Tree View was just what I was looking for in my project,
DBSourceTools Thanks mate,
- Nathan.
QuestionCannot set model for inserted root Node.
YevgenBorodkin
7:19 20 Aug '09  
I need to insert RootNode.
So I'd like to do as TreeModel does. (Having _root collection as a storage for 'real' root nodes)
But I cannot initialize Model property for root node cause it is internal.
Should Model be just public? Have I missed something?

ps: I cannot derive from TreeModel because GetChildren is not virtual.
AnswerRe: Cannot set model for inserted root Node.
dan_bock
6:34 7 Oct '09  
I am having the same problem for my model class that implements ITreeModel. I am thinking of just making it public unless there is another way to set the model.


Last Updated 10 Jul 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010