Click here to Skip to main content
Click here to Skip to main content

XP theme aware TreeView control which supports checkbox and radio button nodes.

, 10 Oct 2003
Rate this:
Please Sign up or sign in to vote.
Internet Explorer advanced settings tree view clone. Supports checkbox and radio button nodes.

Introduction

This tree view control is able to visualize checkbox and radio buttons, like the one you can find under Internet Explorer settings advanced tab. After Spy++ told me that Microsoft uses a normal tree view control, I fired up my Studio to write my own C# control clone named TreeViewRadioBox.

TreeViewRadioBoxReflection works a bit like the PropertyGrid. The public read and writeable properties of type bool and enum are fetched (reflection) and displayed as checkboxes and radio buttons, grouped by their category attribute. The properties' values are automatically updated if you edit the value in the tree view.

Implementation

Basic

I tried to meet the following requirements / key features

  • Derived from System.Windows.Forms.TreeView class
  • Tree nodes are managed in collections
  • XP theme aware checkbox and radio button images, even if you switch theme at run time
  • TreeView familiar handling in general

No requirements without problems to solve

  • You can’t derive from TreeNodeCollection, internal constructor
  • How to draw the checkbox and radio button images, is it possible to draw on an image surface with GDI+
  • How to use the XP theme API

I guess there are many ways to visualize checkboxes and radio buttons mixed in a tree view. My solution was to use images which reflect the possible states of the two control types.

The main work was to imitate the behavior of the control types and switch the images to visualize the current state. Because System.Windows.Forms.TreeView supports checkboxes out of the box, the base functionalities like Node.Checked property and TreeView.AfterCheck event are provided by Microsoft.

Node and collection types

Because the behavior of a checkbox and a radio button is not equal, there is an abstract base class which defines the common behaviors and special implementations to meet the type specific behavior. The typed collections hold the associated nodes.

Class Description
TreeNodeBase Base abstract TreeNode used for common features
TreeNodeBaseCollection Collection for TreeNodeBase
TreeNodeCheckBox TreeNode which represents a CheckBox
TreeNodeGroup TreeNode which represents a group node
TreeNodeRadioButton TreeNode which represents a RadioButton
TreeNodeRadioButtonCollection Collection for TreeNodeRadioButton
TreeNodeRadioButtonGroup TreeNode which represents a RadioButton parent node

Because I can’t use the TreeNodeCollection I had to use my own collection types. I’m not happy about the collection design because I had to painfully override every Nodes collection with my own collection type. But I gained the easy use of collection add method which only supports the permitted node types.

For instance, a radio button group can only hold radio buttons, no checkboxes. But a TreeNodeGroup can hold any of the custom node types.

How it works

Every time the user selects a tree node, the AfterCheck event is fired. The TreeViewEventArgs.Node holds the selected node. After casting it to TreeNodeBase type we can call the TreeNodeBase.UpdateCheckState() method. UpdateCheckState() is implemented for each custom node type and does the following:

TreeNodeBase Not implemented
TreeNodeRadioButtonGroup Not implemented
TreeNodeGroup Not implemented
TreeNodeCheckbox If the checkbox is checked, the checked image is used, otherwise the unchecked image
TreeNodeRadioButton If the radio is checked, the checked image is used, otherwise the unchecked image. The parent (RadioButtonGroup) now cares about the optional update of a last selected radio

Tip to suppress events

Setting the TreeNode.Checked property from within the BeforeCheck or AfterCheck event causes the event to be raised multiple times and can result in unexpected behavior. The TreeViewRadioBox control provides a simple suppress technique to avoid this.

Every time the code switches the state of the checked flag, it has to call:

internal protected void SuspendItemCheckedEvent(bool supressEvent)
{ 
     this.suspendItemCheckedEvent_ += (supressEvent)? +1 : -1;
}

AfterCheck event now only calls code if suspendItemCheckedEvent equals 0. It’s important to use an integer instead of bool value because of nested suppress calls.

Event handling

The event ItemChecked is fired every time a custom node's state has changed. TreeEventArgs.Node holds the selected node. Every node type can have it's own overridden Nodes collection and specific properties.

So we have to cast the node before we can access them.

private void treeViewItemChecked(object sender, 
                       System.Windows.Forms.TreeViewEventArgs e)
{
   if(e.Node is Raccoom.Windows.Forms.TreeNodeRadioButtonGroup)
   {
     ((Raccoom.Windows.Forms.TreeNodeRadioButtonGroup)
                                 e.Node).SelectedItem.Text);    
   } 

   else
   {      
     e.Node.Text;
   }   
}

Custom drawing

System.Drawing.Graphics class provides a static method which makes it possible to draw on the surface of a bitmap.

The following code draws a checkbox in unchecked state to a bitmap:

Rectangle rect = new Rectangle(0,0,16,16);
Bitmap bitmap = new Bitmap(rect.Width,rect.Height);
//
using(System.Drawing.Graphics graphics = Graphics.FromImage(bitmap))
{
    ControlPaint.DrawCheckBox(graphics, rect, 
           ButtonState.Normal | ButtonState.Flat);
}
//
bitmap.Dispose();

After adding this bitmap to the tree view image list, you can fake an unchecked tree node.

The ControlPaint class is not theme aware, so your checkbox will always have the classic Windows style look.

To support themes I used instead of ControlPaint, the excellent code from Don Kackman, A Managed C++ Wrapper Around the Windows XP Theme API to draw my controls theme aware.

Basically the control supports themed and non themed OS.

Using the code

Using this control is common to using System.Windows.Forms.TreeView. You can add nodes to the tree view or his child nodes via the Nodes collection's add method.

This example shows how to add group, checkbox and radio button nodes. This example assumes that you have created an instance of a TreeViewRadioBox control on a Form.

// suspend drawing
treeView.BeginUpdate();

We start by adding the first group node.

// create group node <CODEPROJECT> and add it to tree view nodes collection
TreeNodeGroup cpNode = new TreeNodeGroup("Codeproject"); 
treeView.Nodes.Add(cpNode);

Create a radio button group.

// create radio button group node <SURVEY>(holds radio buttons) 
TreeNodeRadioButtonGroup pollNode = 
     new TreeNodeRadioButtonGroup("Survey");
cpNode.Nodes.Add(pollNode);
// create first radio button
TreeNodeRadioButton radioNode = new TreeNodeRadioButton 
 ("Desktop applications cannot be replaced by network based applications"); 
pollNode.Nodes.Add(radioNode);
// create second radio button 
radioNode = new TreeNodeRadioButton 
 ("There will always be a place for real desktop applications");
pollNode.Nodes.Add(radioNode);
// select current node
radioNode.Checked = true;
// create last radio button
group radioNode = new TreeNodeRadioButton 
 ("Desktop applications will all become network based");
pollNode.Nodes.Add(radioNode);

Create a new group node and add checkbox and radio buttons.

// create group node for code project settings 
TreeNodeGroup settingNode = new TreeNodeGroup("My settings"); 
cpNode.Nodes.Add(settingNode);
// create and add checkbox node <EMAIL Surveys>
settingNode.Nodes.Add(new TreeNodeCheckBox("Email Surveys")); 
settingNode.Nodes.Add(new TreeNodeCheckBox
  ("Use cookies so you don't have to log on again"));
// create group node <NEWSLETTER Topics>
TreeNodeGroup newsletterNode = new TreeNodeGroup("Newsletter Topics");
settingNode.Nodes.Add(newsletterNode); 
// add some newsletter topic checkboxes
newsletterNode.Nodes.Add(new TreeNodeCheckBox("Site News"));
newsletterNode.Nodes.Add(new TreeNodeCheckBox("C# articles"));
newsletterNode.Nodes.Add(new TreeNodeCheckBox("All .NET articles"));

Resume painting, finished.

// resume painting
treeView.EndUpdate();

After the compiler was working, it looks like:

Sample screenshot

TreeViewRadioBoxReflection works familiar to the PropertyGrid, it shows the reflected properties (bool and enum) of a type instance. This example assumes that you have created an instance of a TreeViewRadioBoxReflection control on a Form.

treeView.SelectedObject = new System.Windows.Forms.Form();

Conclusion

  • Before you can add child nodes to a node you must add the node to the tree view, otherwise the images are wrong until the user selects the item.

Have phun with it…

Links

Screenshots with different XP themes are available here (animated GIF which opens in new window).

License

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

About the Author

Chris Richner
Software Developer (Senior) Zeit AG
Switzerland Switzerland
Biography
  • 1996 - 1998 PC Board PPL, HTML, DHTML, Javascript and ASP
  • 1999 - 2001 coding Centura against Sql Database (SqlBase,MSSQL,Oracle)
  • 2002 - 2004 C# Windows Forms
  • 2005 - 2006 C# ASP.NET, Windows Forms
  • 2006 - 2009 C#, WCF, WF, WPF
  • 2010 - 2012 C#, Dynamics CRM, Sharepoint, Silverlight
  • 2013 - now C#, WCF DS (OData), WF, WPF
Interests
  • family & friends
  • chilaxing ,)
  • coding
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralCHM maker Pinmemberkoo920-Oct-03 11:34 
GeneralRe: CHM maker PinmemberJerry Maguire20-Oct-03 14:07 
GeneralRe: CHM maker Pinmemberkoo920-Oct-03 15:59 

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.140721.1 | Last Updated 11 Oct 2003
Article Copyright 2003 by Chris Richner
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid