Click here to Skip to main content
Click here to Skip to main content
Go to top

Simple Tri-State TreeView

, 30 Mar 2011
Rate this:
Please Sign up or sign in to vote.
Provides another, simple way to get a tri-state TreeView, ensuring compatibility to all Windows UIs / styles


Microsoft provides a TreeView control which isn't able to display tree state checkboxes. There are many ways to implement this feature such as window proc hooking and the other stuff. This article will show you another, more simpler way using techniques provided by the .NET Framework only.

This control only inherits the original tree view control replacing CheckBoxes property usage with usage of the StateImageList and primarily overriding the node click event procedure to handle setting the tri-state.
To conform to all Windows UI styles, the state image list will be built dynamically using the CheckBoxRenderer class provided by the .NET Framework while the control is created.


There may be needs to have a tree view control showing on parent nodes that check states its children aren't all the same. Further there may be needs, refreshing check states up / down the tree while checking a sub-/super-node.

Using techniques such as window proc hooking isn't the right way for everyone because it isn't always easy to debug. Further using static images would avoid displaying right checkboxes on different Windows UIs.

Using the Control / Code

Add the TriStateCheckBoxTreeView.cs to your project and simply drop the control to a form. You'll find the differences to the normal tree view control provided by the .NET Framework in the property grid. There is now a property to enable tri-state usage directly below the CheckBoxes-property.


Check state checking is done as normal using the Checked property from the tree node. To determine all three possible states, you may check the StateImageIndex giving the following three possible indexes:

  • 0 - Unchecked
  • 1 - Checked
  • 2 - Mixed

Rendering checkboxes using CheckBoxRenderer

Bitmap GetCheckBoxBitmap(CheckBoxState myState)
    Bitmap bmpCheckBox = new Bitmap(16, 16);
    Graphics gfxCheckBox = Graphics.FromImage(bmpCheckBox);
    CheckBoxRenderer.DrawCheckBox(gfxCheckBox, new Point(2, 2), myState);
    return bmpCheckBox;

Setting the tri-state

protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
 Stack<TreeNode> stNodes;
 TreeNode tnBuffer;
 bool bMixedState;
 int iSpacing;
 int iIndex;
    iSpacing = ImageList == null ? 0 : 18;			// if user clicked area
    if (e.X > e.Node.Bounds.Left - iSpacing ||		// *not* used by the state
        e.X < e.Node.Bounds.Left - (iSpacing + 16))		// image we can leave here.
    { return; }
    tnBuffer = e.Node;					// buffer clicked node and
    tnBuffer.Checked = !tnBuffer.Checked;			// flip its check state.
    stNodes = new Stack<TreeNode>(tnBuffer.Nodes.Count);	// create a new stack and
    stNodes.Push(tnBuffer);					// push buffered node first.
    do {							// let's pop node from stack,
        tnBuffer = stNodes.Pop();				// inherit buffered node's
	tnBuffer.Checked = e.Node.Checked;			// check state and push
        for (int i = 0; i < tnBuffer.Nodes.Count; i++)		// each child on the stack
            stNodes.Push(tnBuffer.Nodes[i]);			// until there is no node
    } while (stNodes.Count > 0);				// left.
    bMixedState = false;
    tnBuffer = e.Node;					// re-buffer clicked node.
    while (tnBuffer.Parent != null) {			// while we get a parent we
        foreach (TreeNode tnChild in tnBuffer.Parent.Nodes)	// determine mixed check states
            bMixedState |= (tnChild.Checked != tnBuffer.Checked);// and convert current check
        iIndex = (int)Convert.ToUInt32(tnBuffer.Checked);	// state to state image index.
        tnBuffer.Parent.Checked = bMixedState || (iIndex > 0);	// set parent's check state and
        if (bMixedState)					// state image in dependency
            tnBuffer.Parent.StateImageIndex = CheckBoxesTriState ? 2 : 1;
        else						// of mixed state.
            tnBuffer.Parent.StateImageIndex = iIndex;
        tnBuffer = tnBuffer.Parent;				// finally buffer parent and
    }							// loop here.


This code has been written as simple as possible. There is one limitation you should keep in mind: If you're adding nodes from code you have to call control's Refresh() after adding has been completed to ensure all nodes got the right state.


  • 17 February 2010: First version
  • 18 February 2010: Extended sections "Introduction", "Background", "Using the control / code", fixed demo project
  • 18 March 2010: Small cosmetics to comments
  • 30 March 2011: Updated demo project


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


About the Author

Software Developer
Germany Germany
No Biography provided

Comments and Discussions

GeneralMy vote of 5 Pinmemberdangler235-Jun-13 17:16 
QuestionASP.NET Pinmemberrohith11923-Sep-12 20:22 
QuestionProblem with RTL check in checkBox Pinmemberefirozen17-Jun-12 19:06 
QuestionAlternative version submitted PinmemberFred_Informatix5-Mar-12 22:12 
GeneralMy vote of 5 PinmemberDreamcooled26-Feb-12 5:15 
QuestionHow can I invert the checkstates for this treeview? [modified] PinmemberStijnBollen19-Jan-12 23:40 
QuestionBug: Unchecking a mixed state node does not update parents mixed state Pinmemberoyvdahl5-Dec-11 2:15 
BugRefresh has no effect [modified] PinmemberMember 824238016-Sep-11 0:24 
AnswerRe: Refresh has no effect [modified] PinmemberMember 824238023-Sep-11 4:10 
BugStateImageIndex issue Pinmemberdymarski5-Jul-11 7:26 
GeneralRe: StateImageIndex issue PinmemberIznogood11-Sep-11 0:18 
SuggestionHow just to hide checkbox of the certain TreeNode in TreeView control PinmemberMember 14778530-Jun-11 16:16 
GeneralRe: How just to hide checkbox of the certain TreeNode in TreeView control PinmemberDreamCatcher2k1030-Jun-11 21:34 
GeneralSmall issue PinmemberMember 793518429-May-11 7:59 
AnswerRe: Small issue PinmemberDreamCatcher2k1029-May-11 22:33 
GeneralRe: Small issue PinmemberTheBertster30-May-11 1:07 
GeneralProblem deselecting node - update parent node Pinmemberveydanvario30-Mar-11 22:57 
AnswerRe: Problem deselecting node - update parent node PinmemberDreamCatcher2k1031-Mar-11 2:05 
GeneralRe: Problem deselecting node - update parent node Pinmemberveydanvario31-Mar-11 23:29 
GeneralRe: Problem deselecting node - update parent node PinmemberMauro Luque1-Apr-11 9:52 
GeneralRe: Problem deselecting node - update parent node Pinmemberveydanvario6-Apr-11 9:21 
GeneralRe: Problem deselecting node - update parent node PinmemberMauro Luque6-Apr-11 9:35 
GeneralBug with Checked State PinmemberRikTheVeggie29-Mar-11 5:31 
AnswerRe: Bug with Checked State [modified] PinmemberDreamCatcher2k1029-Mar-11 6:55 
Questionpossible bug PinmemberMauro Luque15-Mar-11 3:13 
AnswerRe: possible bug PinmemberDreamCatcher2k1029-Mar-11 6:58 
GeneralRefresh() does not set states correctly when adding nodes from code [modified] PinmemberMember 768203914-Mar-11 0:52 
GeneralLosing state after collapse/expand PinmemberDBLOOD9-Mar-11 7:31 
AnswerRe: Losing state after collapse/expand PinmemberDreamCatcher2k109-Mar-11 8:03 
GeneralRe: Losing state after collapse/expand PinmemberDBLOOD9-Mar-11 10:49 
Generalproblem in setting checked status by code Pinmembermahdi87_gh23-Jan-11 0:02 
GeneralRe: problem in setting checked status by code Pinmembermahdi87_gh23-Jan-11 1:52 
AnswerRe: problem in setting checked status by code PinmemberDreamCatcher2k1030-Mar-11 5:53 
GeneralThanks PinmemberGuyThiebaut2-Jan-11 8:20 
GeneralMy vote of 5 PinmemberGuyThiebaut2-Jan-11 8:19 
GeneralBug Report: Root Node Switches To Checked State Incorrectly PinmemberKivash24-Oct-10 4:13 
AnswerRe: Bug Report: Root Node Switches To Checked State Incorrectly PinmemberDreamCatcher2k1026-Oct-10 23:38 
GeneralRe: Bug Report: Root Node Switches To Checked State Incorrectly PinmemberKivash18-Nov-10 5:50 
Questionimages are 256 color PinmemberEugene Ignatov4-Oct-10 22:33 
AnswerRe: images are 256 color PinmemberDreamCatcher2k1011-Oct-10 5:04 
Questionthe Action property of the TreeViewEventArgs Pinmembersaru810-Sep-10 1:05 
AnswerRe: the Action property of the TreeViewEventArgs PinmemberDreamCatcher2k1015-Sep-10 8:27 
GeneralRe: the Action property of the TreeViewEventArgs Pinmembersaru823-Sep-10 15:03 
GeneralNesting Pinmemberm00n120-Jul-10 3:54 
AnswerRe: Nesting [modified] PinmemberDreamCatcher2k1020-Jul-10 3:56 
GeneralMy vote of 1 PinmemberOrkanA15-Jun-10 10:50 
GeneralRe: My vote of 1 PinmemberDreamCatcher2k1021-Jun-10 7:56 
GeneralSimple and nice! PinmemberSudP10-Jun-10 12:35 
QuestionSave user selection PinmemberYY Ang2-Jun-10 17:40 
AnswerRe: Save user selection PinmemberYY Ang2-Jun-10 22:35 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 30 Mar 2011
Article Copyright 2010 by DreamCatcher2k10
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid