Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C#

Adding XP Themes to Custom .NET Controls

Rate me:
Please Sign up or sign in to vote.
4.76/5 (38 votes)
31 Aug 2003CPOL5 min read 231.8K   5.5K   130  
Rendering your own theme parts with the Windows XP UxTheme API
This article is about exploring the ability to create custom controls in the .NET platform, that make use of Windows XP's visual styles and themes. To that end, I have included a couple of fairly simple controls that mimic some of the behaviors that Windows Explorer implements in XP.
/////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 - Don Kackman
//
// Distribute and change freely, but please don't remove my name from the source 
//
// No warrantee of any kind, express or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user. 
//
// questions - contact me at dkackman_2000@yahoo.com
//

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

using System.Windows.Forms.Themes;

namespace System.Windows.Forms
{
	/// <summary>
	/// Abstract base Panel control to help out with rendering while themed and not themed
	/// </summary>
	public class ThemeablePanel : Panel
	{
		private ThemeInfo m_ThemeInfo;
		private string m_WindowClassName;

		/// <summary>
		/// Initializes the ThemeablePanel class
		/// </summary>
		/// <param name="windowClassName">The window class name that will be used to retreive ThemeParts</param>
		protected ThemeablePanel( string windowClassName ) 
		{
			m_WindowClassName = windowClassName;
			m_ThemeInfo = new ThemeInfo();

			BackColor = SystemColors.ControlLightLight;
			SetStyle( ControlStyles.Selectable, false );
		}

		/// <summary>
		/// This contructor is needed so derived classes can be used
		/// by the designer. It shouldn't be called otherise
		/// </summary>
		protected ThemeablePanel() : this( "" )
		{
		}

		/// <summary>
		/// ThemeInfo instance for the control
		/// </summary>
		protected ThemeInfo ThemeInfo
		{
			get { return m_ThemeInfo; }
		}

		/// <summary>
		/// Get the current window theme for the panel using the window class name
		/// If the app is not themed returns null
		/// </summary>
		/// <returns>ThemeInfo object or null</returns>
		protected WindowTheme GetCurrentWindowTheme()
		{
			WindowTheme theme = null;

			// try and get the window theme
			// If we can't catch the error and return null
			try
			{
				// don't get a the window theme if the app isn't themed or we are in design mode
				if ( UxTheme.IsAppThemed && !DesignMode )
					theme = m_ThemeInfo[m_WindowClassName];
			}
			catch ( IndexOutOfRangeException )
			{
			}

			return theme;
		}

		protected override void OnPaintBackground(PaintEventArgs pevent)
		{
			WindowTheme theme = GetCurrentWindowTheme();

			if ( theme == null )
			{
				base.OnPaintBackground( pevent );
				OnPaintUnthemedBackground(pevent);
			}
			else
			{
				OnPaintThemedBackground( pevent );
			}
		}

		/// <summary>
		/// Derived classes should override this method if they need to do custom
		/// rendering on the background when the app isn't themed.
		/// The base class implementation does nothing
		/// </summary>
		/// <param name="pevent">Paint event arguments</param>
		protected virtual void OnPaintUnthemedBackground( PaintEventArgs pevent )
		{
			// base class implementation is a no-op
		}

		/// <summary>
		/// Derived classes should override this method if the need to do
		/// custom background rendering when the app is themed.
		/// The base class implementation just does a nomral background paint
		/// </summary>
		/// <param name="pevent">Paint event arguments</param>
		protected virtual void OnPaintThemedBackground( PaintEventArgs pevent )
		{
			//base class implementation just does a normal background paint
			base.OnPaintBackground (pevent);
		}

		/// <summary>
		/// Draw the specified theme part
		/// </summary>
		/// <param name="graphics">Graphics object used for painting</param>
		/// <param name="bounds">Rectangle to pain into</param>
		/// <param name="partName">The name of the part to paint</param>
		/// <returns>The ThemePart object that is painted</returns>
		protected ThemePart DrawPart( Graphics graphics, Rectangle bounds, string partName )
		{
			WindowTheme theme = GetCurrentWindowTheme();

			ThemePart part = theme.Parts[partName];
			part.DrawBackground( graphics, bounds );

			return part;
		}
		/// <summary>
		/// Draw the specified theme part
		/// </summary>
		/// <param name="graphics">Graphics object used for painting</param>
		/// <param name="bounds">Rectangle to pain into</param>
		/// <param name="partName">The name of the part to paint</param>
		/// <param name="stateName">The name of the part's state to paint</param>
		/// <returns>The ThemePartState object used to paint</returns>
		protected ThemePartState DrawPart( Graphics graphics, Rectangle bounds, string partName, string stateName )
		{
			WindowTheme theme = GetCurrentWindowTheme();

			ThemePart part = theme.Parts[partName];
			ThemePartState state = part.States[stateName];
			state.DrawBackground( graphics, bounds );

			return state;
		}

		/// <summary>
		/// Draws the specified part onto the control
		/// </summary>
		/// <param name="graphics">Graphics object used for painting</param>
		/// <param name="control">The control who's bounds will be used to paint</param>
		/// <param name="partName">The name of the part to paint</param>
		/// <returns>The ThemePart object that is painted</returns>
		protected ThemePart DrawPart( Graphics graphics, Control control, string partName )
		{
			return DrawPart( graphics, new Rectangle( 0, 0, control.Width, control.Height ), partName );
		}

		/// <summary>
		/// Draws the specified part and state onto the control
		/// </summary>
		/// <param name="graphics">Graphics object used for painting</param>
		/// <param name="control">The control who's bounds will be used to paint</param>
		/// <param name="partName">The name of the part to paint</param>
		/// <param name="stateName">The name of the part's state to paint</param>
		/// <returns>The ThemePartState object used to paint</returns>
		protected ThemePartState DrawPart( Graphics graphics, Control control, string partName, string stateName )
		{
			return DrawPart( graphics, new Rectangle( 0, 0, control.Width, control.Height ), partName, stateName );
		}
	}

}

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
Team Leader Starkey Laboratories
United States United States
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10

It only went downhill from there.

Hey look, I've got a blog

Comments and Discussions