Click here to Skip to main content
15,880,392 members
Articles / Desktop Programming / Windows Forms

Three State TreeView – Part 1 of 2

Rate me:
Please Sign up or sign in to vote.
4.75/5 (18 votes)
3 Jun 2006CPOL3 min read 161K   5.3K   80   24
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 TreeNodes by setting the CheckBoxes 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).

Image 1

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).

Image 2

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 StateChild Nodes
CheckedAll child nodes are checked.
UncheckedAll child nodes are unchecked.
IndeterminateSome child nodes are checked, some are unchecked, and/or some are indeterminate.

These states are defined in code by a new enumeration, CheckBoxState.

C#
/// <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.

C#
/// <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.

C#
/// <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 the ThreeStateTreeNode class.

References

Acknowledgements

I want to thank the following people for their thoughts, insights and words of advice while working on this article.

Ascentium Corporation is a Microsoft Gold Certified Partner, and an award-winning, integrated technology and marketing consultancy located in Bellevue, Washington.

License

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


Written By
Web Developer
United States United States
Ben's current development interests are in WinForms, C#, and .NET 2.0.

Comments and Discussions

 
SuggestionHow to hide checkbox of the certain TreeNode in TreeView control Pin
Member 14778530-Jun-11 16:18
Member 14778530-Jun-11 16:18 
Generalpart 2 released! Pin
Mr.PoorEnglish1-Apr-09 14:07
Mr.PoorEnglish1-Apr-09 14:07 
QuestionHow to check which checkbox is checked. Pin
saania khan8-Dec-06 12:14
saania khan8-Dec-06 12:14 
QuestionMFC? Pin
kiranin5-Dec-06 18:11
kiranin5-Dec-06 18:11 
Questionpart 2?? Pin
MP3Observer4-Dec-06 3:50
MP3Observer4-Dec-06 3:50 
AnswerRe: part 2?? Pin
banguero30-Jan-07 14:01
banguero30-Jan-07 14:01 
GeneralVB.NET Pin
Crzy Cooter27-Oct-06 4:07
Crzy Cooter27-Oct-06 4:07 
Generalpart 2 [modified] Pin
sexmachinn24-Aug-06 11:56
sexmachinn24-Aug-06 11:56 
Thanks for the nice job. I've tested your component and it works fine.

Just wondering... how do you plan to do part 2? I mean... how do you plan to get into the paint event and change how the checkbox is being painted?

The reason I'm asking is because since you don't have the time I'm planning to implement it myself for my own use until you can show up with your solution. Big Grin | :-D

Thanks again,
Alexandre.


-- modified at 12:57 Friday 25th August, 2006
GeneralRe: part 2 Pin
BvUsername5-Sep-06 7:32
BvUsername5-Sep-06 7:32 
QuestionWhen will part 2 be available ? Pin
Peregrine6620-Jul-06 0:29
Peregrine6620-Jul-06 0:29 
AnswerRe: When will part 2 be available ? Pin
BvUsername24-Jul-06 12:14
BvUsername24-Jul-06 12:14 
QuestionRe: When will part 2 be available ? Pin
ImaHack19-Dec-06 10:05
ImaHack19-Dec-06 10:05 
GeneralRe: When will part 2 be available ? Pin
mongeon59-Jan-07 3:59
mongeon59-Jan-07 3:59 
AnswerRe: When will part 2 be available ? Pin
Gregg Mattinson27-Mar-07 10:52
Gregg Mattinson27-Mar-07 10:52 
QuestionRe: When will part 2 be available ? Pin
gpraceman2-May-07 13:41
gpraceman2-May-07 13:41 
AnswerRe: When will part 2 be available ? Pin
Crasch_9930-Aug-07 3:30
Crasch_9930-Aug-07 3:30 
AnswerRe: When will part 2 be available ? [modified] Pin
GrayFace3-Aug-07 10:15
GrayFace3-Aug-07 10:15 
Generalobservation on demo sample Pin
BillWoodruff3-Jun-06 5:12
professionalBillWoodruff3-Jun-06 5:12 
GeneralRe: observation on demo sample Pin
BvUsername4-Jun-06 19:42
BvUsername4-Jun-06 19:42 
GeneralRe: observation on demo sample Pin
BillWoodruff4-Jun-06 22:15
professionalBillWoodruff4-Jun-06 22:15 
AnswerRe: observation on demo sample Pin
jmelgaard29-Dec-06 11:45
jmelgaard29-Dec-06 11:45 
QuestionHow do you handle Loading on Demand Pin
gbBrix2-Jun-06 19:26
gbBrix2-Jun-06 19:26 
AnswerRe: How do you handle Loading on Demand [modified] Pin
BvUsername4-Jun-06 19:50
BvUsername4-Jun-06 19:50 
GeneralRe: How do you handle Loading on Demand Pin
gmpreussner14-Jul-06 12:21
gmpreussner14-Jul-06 12:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.