Click here to Skip to main content
15,892,697 members
Articles / Programming Languages / C#

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

Rate me:
Please Sign up or sign in to vote.
4.70/5 (21 votes)
10 Oct 2003CPOL5 min read 261.4K   3.9K   112  
Internet Explorer advanced settings tree view clone. Supports checkbox and radio button nodes.
/* Copyright � 2003 by Christoph Richner.\nAll rights are reserved.
 * 
 * If you like this code then feel free to go ahead and use it.
 * The only thing I ask is that you don't remove or alter my copyright notice.
 * Your use of this software is entirely at your own risk. I make no claims or
 * warrantees about the reliability or fitness of this code for any particular purpose.
 * If you make changes or additions to this code please mark your code as being yours.
 * 
 * If you have questions or comments then please contact me at: microweb@bluewin.ch
 * */


using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;


namespace Raccoom.Windows.Forms
{
	/// <summary>
	/// Microsoft Internet Explorer Advanced Settings TreeView Clone. 
	/// <p>TreeView handles
	/// <list type="bullet">
	/// <item><c>checkbox</c> </item>
	/// <item><c>radiobutton</c> groups</item>
	/// <item>nodes to group items</item>
	/// </list></p>
	/// <seealso  cref="TreeNodeCheckBox"/><seealso  cref="TreeNodeRadioButtonGroup"/><seealso  cref="TreeNodeBase"/>
	/// <p>Provides a user interface for browsing the properties of an object. The public read and writeable properties of type
	/// <c>bool</c> and <c>enum</c> are fetched and displayed as <c>CheckBoxes</c> and <c>RadioButtons</c>.</p>
	/// <p>The fetched properties are bound to the nodes, if you change the states in the treeview your instance is updated.</p>
	/// </summary>
	/// <example>
	/// The following example creates a new instance of a TreeViewRadioBoxReflection and set a DataGridBoolColumn as selected object.
	/// <code>
	/// TreeViewRadioBox treeViewRadioBox = new TreeViewRadioBox();
	/// treeViewRadioBox.SelectedObject = new System.Windows.Forms.DataGridBoolColumn();
	/// </code>
	/// </example>
	/// <remarks>The reflection only fill <c>bool</c> and <c>enum</c> types, the selected object must provide this types, otherwise don't be suprised about a empty TreeViewRadioBoxReflection.</remarks>
	public class TreeViewRadioBoxReflection : TreeViewRadioBox
	{
		#region fields
		/// <summary>Specifies the <c>Imagelist</c> index where the dynamic loaded image relies.</summary>
		private int clearImageIndex_;
		/// <summary>The object for which the treeview displays properties.</summary>
		private object selectedObject_;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		#endregion

		/// <summary>
		/// Constructor needed for Design Time
		/// </summary>
		/// <param name="container"></param>
		public TreeViewRadioBoxReflection(System.ComponentModel.IContainer container)
		{	
			container.Add(this);
			InitializeComponent();
		}

		/// <summary>
		/// Standard constructor
		/// </summary>
		public TreeViewRadioBoxReflection()
		{
			InitializeComponent();
		}


		#region public interface		
		/// <summary>
		/// Gets or sets the object for which the treeview displays properties.
		/// </summary>
		[Browsable(true), Category("Data")]
		public object SelectedObject
		{
			get
			{
				return selectedObject_;
			}
			set
			{
				if(value==null) return;
				//
				selectedObject_ = value;
				System.Collections.Hashtable categoryMap = new Hashtable();
				TreeNodeBase groupNode = null;
				this.Cursor = Cursors.WaitCursor;
				this.BeginUpdate();
				this.SuspendItemCheckedEvent(true);
				try
				{
					this.Nodes.Clear();
					foreach(PropertyInfo pi in  selectedObject_ .GetType().GetProperties())
					{				
						// check property type
						if(!((pi.PropertyType.BaseType == typeof(Enum)) || (pi.PropertyType == typeof(bool)))) continue;
						// must have get and set accessor's
						if((!pi.CanRead) || (!pi.CanWrite)) continue;
						// read Category
						System.ComponentModel.CategoryAttribute[] category =(CategoryAttribute[]) pi.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), true);
						if(category.GetLength(0)==0) category = new CategoryAttribute[] {new CategoryAttribute("Misc")};
						// create/evaluate category group node
						if(!categoryMap.ContainsKey(category[0].Category))
						{
							TreeNodeGroup node = new TreeNodeGroup(category[0].Category,this.ImageIndex, this.SelectedImageIndex);
							this.Nodes.Add(node);
							categoryMap.Add(category[0].Category,node);
						}
						groupNode = categoryMap[category[0].Category] as TreeNodeBase;
						// add new radio button group node to category group node
						if(pi.PropertyType.BaseType == typeof(Enum))
						{
							TreeNodeRadioButtonGroup node = new TreeNodeRadioButtonGroup(pi.Name,clearImageIndex_,clearImageIndex_);
							groupNode.Nodes.Add(node);
							node.UserData = pi;		
							//							
							foreach(string data in Enum.GetNames(pi.PropertyType))
							{
								TreeNodeRadioButton subnode = new TreeNodeRadioButton (data.ToString());
								subnode.UserData = data;
								//
								node.Nodes.Add(subnode);
							}												
						} 
						// add new checkbox node to category group node
						else if(pi.PropertyType == typeof(bool))
						{
							TreeNodeCheckBox node = new TreeNodeCheckBox (pi.Name);
							groupNode.Nodes.Add(node);
							node.UserData = pi;							
							node.Checked = (bool) pi.GetValue(selectedObject_,null);							
						}
					}
				}
				catch (System.Exception e)
				{
					System.Diagnostics.Debug.WriteLine(e.Message);
				}
				finally
				{
					this.SuspendItemCheckedEvent(false);
					this.Nodes[0].EnsureVisible();
					this.EndUpdate();
					this.Cursor = Cursors.Default;
				}				
			}
		}
		#endregion

		#region internal interface
		/// <summary>
		/// Add the needed images to the <c>Imagelist</c> to draw <c>Checkbox</c> and <c>RadioButton</c>.
		/// </summary>
		protected override void AddImages()
		{
			Bitmap bitmap = new Bitmap(this.ImageList.ImageSize.Width, this.ImageList.ImageSize.Height);
			using(System.Drawing.Graphics graphics =  System.Drawing.Graphics.FromImage(bitmap))
			{				
				graphics.FillRectangle(SystemBrushes.Window,new Rectangle(0,0,this.ImageList.ImageSize.Width, this.ImageList.ImageSize.Height));
				clearImageIndex_ = this.ImageList.Images.Add(bitmap,Color.Transparent);
			}
			bitmap.Dispose();
			//
			base.AddImages();
		}
		#endregion

		#region events
		/// <summary>
		/// Raise the ItemChecked event, handle databinding between properties and nodes.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnItemChecked(TreeViewEventArgs e)
		{
			TreeNodeBase node =  e.Node as TreeNodeBase;
			if((node==null) || (node.UserData==null))return;
			//
			PropertyInfo pi = node.UserData as PropertyInfo;
			if(e.Node is TreeNodeCheckBox)
			{
				pi.SetValue(selectedObject_ ,node.Checked,null);
			} 
			else if(e.Node is TreeNodeRadioButtonGroup)
			{				
				pi.SetValue(selectedObject_ ,Enum.Parse(pi.PropertyType, ((TreeNodeRadioButtonGroup)e.Node).SelectedItemValue.ToString(),true),null);
			}
			//
			base.OnItemChecked (e);
			
		}
		#endregion

		#region Component Designer generated code
		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}


		
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer (Senior)
Switzerland Switzerland
My interest is in the future because I am going to spend the rest of my life there. (Charles Kettering)

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 - 2013 C#, WCF DS (OData), WF, WPF
  • 2014 - 2016 C#, Azure PaaS, Identity, OWIN, OData, Web Api
  • 2017 - now C#, aspnet.core, IdentityServer4, TypeScript & Angular @ Azure IaaS or PaaS

Interests

  • family & friends
  • chilaxing ,)
  • coding

Comments and Discussions