Three State TreeView – Part 1 of 2






4.75/5 (15 votes)
Two part series on how to create a three state Treeview in .NET 2.0 without using Win32 or a state imagelist
Introduction
The .NET 2.0 TreeView
control supports the use of check boxes in TreeNode
s by setting the CheckBox
es property to True
on an instance of the TreeView
. However, the standard checkbox
of a TreeNode
supports only two states, Checked
or Unchecked
, and related nodes are completely unaware of the check state of related parent/child nodes. To address this problem, a third state, Indeterminate
, will be introduced for a TreeNode
as well as the ability to communicate state changes to related parent/child nodes.
Solution
To address the complexities of the problem surrounding the drawing of the control, this project is broken down into a two-part series:
Part 1, "Creating a Pseudo Three State Check Box TreeView
: Cascading State Changes to Parent and Child Nodes," implements a variable state to track the current check state of a TreeNode
as well as cascading the change of state to parent and child nodes. This is a pseudo solution because the third state, Indeterminate
, is represented by a check mark, not the traditional square block (see Figure 1).
Part 2, "Creating a True Three State Check Box TreeView
: Managing the Drawing of the TreeView
," addresses the problem of drawing a three-state check box in the TreeView
control that uses the third state Indeterminate
of a check box (see Figure 2).
Points of Interest
To implement the state and state change behavior, it is necessary to define the states and override the behavior of the base TreeView
and TreeNode
controls.
The following is a summary of the behavior of states for related parent/child nodes.
Parent Node State | Child Nodes |
Checked |
All child nodes are checked . |
Unchecked |
All child nodes are unchecked . |
Indeterminate |
Some child nodes are checked , some are unchecked , and/or some are indeterminate . |
These states are defined in code by a new enumeration, CheckBoxState
.
/// <summary>
/// The available states for a three state check box.
/// </summary>
[FlagsAttribute]
public enum CheckBoxState
{
Unchecked = 1,
Checked = 2,
Indeterminate = CheckBoxState.Unchecked | CheckBoxState.Checked
}
When a node’s check box is checked or unchecked manually or programmatically, the state property of that node needs to be updated as well as the state of related nodes. The Toggle
method of the TreeNode
is overridden to update the state and to begin the process of updating the states of related parent/child nodes.
/// <summary>
/// The current state of the check box.
/// </summary>
private Enumerations.CheckBoxState mState = Enumerations.CheckBoxState.Unchecked;
public Enumerations.CheckBoxState State
{
get { return this.mState; }
set
{
if (this.mState != value)
{
this.mState = value;
// Ensure if checkboxes are used to make the checkbox checked or unchecked.
// When going to a fully drawn control, this will be managed in the drawing
// code.
// Setting the Checked property in code will cause the OnAfterCheck to be
// called and the action will be 'Unknown'; do not handle that case.
if ((this.TreeView != null) && (this.TreeView.CheckBoxes))
this.Checked = this.mState == Enumerations.CheckBoxState.Checked;
}
}
}
/// <summary>
/// Manages state changes from one state to the next.
/// </summary>
public new void Toggle()
{
this.Toggle(this.State);
}
/// <summary>
/// Manages state changes from one state to the next.
/// </summary>
/// <param name="fromState">The state upon which to base the state change.</param>
public void Toggle(Enumerations.CheckBoxState fromState)
{
switch (fromState)
{
case Enumerations.CheckBoxState.Unchecked:
{
this.State = Enumerations.CheckBoxState.Checked;
break;
}
case Enumerations.CheckBoxState.Checked:
case Enumerations.CheckBoxState.Indeterminate:
default:
{
this.State = Enumerations.CheckBoxState.Unchecked;
break;
}
}
this.UpdateStateOfRelatedNodes();
}
/// <summary>
/// Manages updating related child and parent nodes of this instance.
/// </summary>
public void UpdateStateOfRelatedNodes()
{
ThreeStateTreeView tv = this.TreeView as ThreeStateTreeView;
if ((tv != null) && tv.CheckBoxes && tv.UseThreeStateCheckBoxes)
{
tv.BeginUpdate();
// If want to cascade checkbox state changes to child nodes of this node and
// if the current state is not intermediate, update the state of child nodes.
if (this.State != Enumerations.CheckBoxState.Indeterminate)
this.UpdateChildNodeState();
this.UpdateParentNodeState(true);
tv.EndUpdate();
}
}
The TreeView
’s OnAfterCheck
method is overridden to manage the process of toggling the state for the current node and related parent/child nodes. The OnAfterCheck
will get called when the Checked
property of a node changes. This occurs when the user checks the check box via the mouse or keyboard. It also happens when the checked
property is changed in code. When changed in code, the Action
type is Unknown
. This is a case in which we do NOT want to toggle the state as it is already in progress.
/// <summary>
/// Raises the AfterCheck event.
/// </summary>
protected override void OnAfterCheck(TreeViewEventArgs e)
{
base.OnAfterCheck(e);
if (this.UseThreeStateCheckBoxes)
{
switch (e.Action)
{
case TreeViewAction.ByKeyboard:
case TreeViewAction.ByMouse:
{
if (e.Node is ThreeStateTreeNode)
{
// Toggle to the next state.
ThreeStateTreeNode tn = e.Node as ThreeStateTreeNode;
tn.Toggle();
}
break;
}
case TreeViewAction.Collapse:
case TreeViewAction.Expand:
case TreeViewAction.Unknown:
default:
{
// Do nothing.
break;
}
}
}
}
Conclusion
The addition of a state variable to a TreeNode
allows us to introduce a third state, Indeterminate
, for the TreeView
control. The ability to notify related parent/child nodes of state changes also enhances the native TreeView
’s behavior. In Part 2 of this series, the drawing of a true Indeterminate
state check box will be addressed for a complete implementation of a Three State TreeView
control.
To Do
- Implement full designer support for the
TreeNode
Editor to support theThreeStateTreeNode
class.
References
- TriStateTreeview in VB.NET, Carlos J. Quintero (CodeProject article)
- TreeView Class (MSDN)
- TreeNode Class (MSDN)
Acknowledgements
I want to thank the following people for their thoughts, insights and words of advice while working on this article.
- Robert Miles, CTO, Ascentium Corporation
- David Langer, Solutions Architect, Ascentium Corporation
- Troy Schneringer, Developer, Ascentium Corporation
Ascentium Corporation is a Microsoft Gold Certified Partner, and an award-winning, integrated technology and marketing consultancy located in Bellevue, Washington.