Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
10 Oct 2003 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here