Introduction
The TreeView
control as implemented by Microsoft in the .NET 1.1 Framework does support checkboxes, but those checkboxes allow only two states: checked or unchecked.
This control is built on top of the TreeView
control, and provides some additional functionality that might be useful.
- Tri-state checkboxes, checked / unchecked / indeterminate state.
TriStateTreeView
supports custom images for all three states. When not using custom images, default tri-state images are provided. Custom images are configured the same way as the "ImageIndex
" and the "SelectedImageIndex
" properties, so state images can be added to the ImageList
bound to the TreeView
using the ImageList
property.- Checkboxes can be switched off on a per-node basis.
- The
TreeView
recognizes two different types of TreeNode
s: Containers and Items. They behave differently when child nodes are checked / unchecked.
Background
Some of this code, mostly the wndproc
hooks, comes from another sample found on this site. It was a VB.NET sample developed by Carlos J. Quintero dealing with the node fonts: http://www.codeproject.com/KB/cpp/CustomDrawTreeview.aspx. I translated that sample and added my own stuff. Thanks Carlos for sharing your code with us.
Using the code
The sources contain two classes: TriStateTreeView
and TriStateTreeNode
.
The TriStateTreeNode
class exposes an internal property, NodeLineType
, which is read by the TriStateTreeView
control when it is about to draw the node. When a checkbox has been disabled for a certain node, a dotted line should be drawn to replace that checkbox, in the way the original TreeView
would do it.
Enum NodeLineType
The property NodeLineType
returns a value of type NodeLineType
, which can have three different values:
None
- No line needs to be drawnStraight
- Only a straight line has to be drawn where normally a checkbox would be drawnWithChildren
- A straight line must be drawn, and a connecting line to the node's children
internal enum NodeLineType
{
None,
Straight,
WithChildren
}
Class TriStateTreeView
The class TriStateTreeView
has been derived from System.Windows.Forms.TreeView
, and exposes the additional properties listed below:
UseCustomImages
- Design or runtime editable. Determines if the TreeView
draws the images configured in the ImageList
(custom), or the default images.CheckedImageIndex
- Index of the image to display when the node is in the Checked
state (UseCustomImages
must be true
).UncheckedImageIndex
- Index of the image to display when the node is in the Unchecked
state (UseCustomImages
must be true
).IndeterminateImageIndex
- Index of the image to display when the node is in the Indeterminate
state (UseCustomImages
must be true
).
The above four properties have all been placed in a single category in the property browser, named CheckState
.
Class TriStateTreeNode
The class TriStateTreeNode
has been derived from System.Windows.Forms.TreeNode
, and exposes the extra functionality described below:
- It hides the original
Checked
property, and returns true
when its CheckState
property is either Checked
or Indeterminate
. The property returns Unchecked
when CheckState
is Unchecked
. - Additional read-only property
CheckState
which returns either Checked/Unchecked/Indeterminate
depending on the current state. - Additional read-write property
IsContainer
which determines if a TreeNode
behaves as a container or as an item. - Additional read-write property
CheckboxVisible
which determines if a node exposes a checkbox. - Internal property
NodeLineType
(see previous explanation). - Internal method
SetCheckedState( CheckState value)
: Provides an interface for the TriStateTreeView
class.
Sample code
private void ConfigureTreeView()
{
TriStateTreeNode folderNode = null;
TriStateTreeNode rootNode =
new TriStateTreeNode( "Home - \"CheckboxVisible = false\"." );
rootNode.CheckboxVisible = false;
rootNode.IsContainer = true;
for( int i = 1; i < 4; i++ )
{
folderNode = new TriStateTreeNode( string.Format( "Foldernode {0}," +
" can show 3 states, as shown here.", i), 0, 1 );
folderNode.IsContainer = true;
rootNode.Nodes.Add( folderNode );
}
folderNode = new TriStateTreeNode("Foldernode 4, " +
"cannot be checked, as shown here.", 0, 1);
folderNode.IsContainer = true;
folderNode.CheckboxVisible = false;
rootNode.Nodes.Add(folderNode);
TriStateTreeNode firstFolder = rootNode.FirstNode as TriStateTreeNode;
for(int i = 1; i < 3; i++)
{
TriStateTreeNode itemNode =
new TriStateTreeNode( string.Format( "Item node {0}", i ), 2, 2 );
firstFolder.Nodes.Add( itemNode );
}
TriStateTreeNode secondFolder = firstFolder.NextNode as TriStateTreeNode;
for(int i = 1; i < 3; i++)
{
TriStateTreeNode itemNode =
new TriStateTreeNode( string.Format( "Item node {0}", i ), 2, 2);
secondFolder.Nodes.Add( itemNode );
}
TriStateTreeNode thirdFolder = secondFolder.NextNode as TriStateTreeNode;
for(int i = 1; i < 3; i++)
{
TriStateTreeNode itemNode =
new TriStateTreeNode( string.Format( "Item node {0}", i ), 2, 2 );
thirdFolder.Nodes.Add( itemNode );
}
TriStateTreeNode fourthFolder = folderNode;
fourthFolder.CheckboxVisible = false;
for(int i = 1; i < 3; i++)
{
TriStateTreeNode itemNode =
new TriStateTreeNode( string.Format(
"Item node {0} - no checkboxes", i ), 2, 2 );
itemNode.CheckboxVisible = false;
fourthFolder.Nodes.Add( itemNode );
}
this.triStateTreeView1.SuspendLayout();
this.triStateTreeView1.Nodes.Add( rootNode );
this.triStateTreeView1.ResumeLayout();
secondFolder.FirstNode.Checked = true;
thirdFolder.Checked = true;
}
Points of interest
There are still a couple of issues I want to fix, feedback about it will be appreciated.
Microsoft has a TreeNodeCollectionEditor
which will let you construct a tree at design time. Normally, I think this editor is not likely to be used since most treeviews will show dynamic data which will almost never be pre-configured. However... the editor doesn't work with this treeview because it adds TreeNode
s instead of TriStateTreeNode
s, which disables the entire tri-state concept. I haven't quite figured out what to do about it.
I guess a solution to this problem should involve hiding the Nodes
property from the TreeView
control by adding our own Nodes
property which is a TreeNodeCollection
like collection class that stores TriStateTreeNode
s. That, together with a custom TreeNodeCollectionEditor
, should just about take care of the problem.
History
- 14 December 2007 - First version.