Recently I had to implement a gradient Title Bar label much like the one in Outlook 2003 (image below). In addition, my title bar would have to change its background gradient colors depending on what Windows XP theme was selected.
Easy enough, I thought. I'll just P/Invoke into uxtheme.dll (which handles theming in Windows XP), get the colors I need and paint the gradient. So I downloaded Steve McMahon's awesome VB6 Theme Explorer to see what colors are available for my current theme and how to get the needed colors. To my utter surprise, the colors that you see in the Outlook 2003 screenshot were not present in the theme. Just for a sanity check, I switched my theme and checked the Theme Explorer against Outlook. Same result, those colors are not defined in the theme.
At this point, I asked questions all over the place and was pointed to the site of Klik! software, which produces EntryLib.NET set of components featuring the Office 2003 colors. A quick email conversation with the developer of EntryLib.NET followed. Initially, based on memory, he thought that those colors are derived from the theming APIs. However, I downloaded the product and went through it with Lutz Roeder's excellent .NET Reflector to see how it was done. It turned out that the colors in EntryLib.NET were being hardwired based on the currently loaded theme. After being presented with Reflector's results, the developer confirmed that the colors indeed are hard-wired. So I just copied and pasted those color definitions from Reflector into my code.
Once I knew the colors, everything else was child's play. In addition to painting the gradient, the themed label knows what to do when the user changes the theme (or specific colors within the theme). The only thing that's hardwired is the font size of the caption (which is easy enough to make dynamic). Note that in addition to the code below, you'll also need the *.designer.cs code. Code below is simply for demonstration, so just download the project.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace vbRad.Controls
{
public partial class CtlGradientHeader : UserControl
{
#region Constants
private const string BLUE_THEME = "NormalColor";
private const string GREEN_THEME = "HomeStead";
private const string SILVER_THEME = "Metallic";
#endregion
#region Enums
private enum eThemeIndex
{
BlueTheme = 0,
GreenTheme = 1,
SilverTheme = 2,
NoTheme = 3
}
#endregion
#region Private Variables
private static string _ThemeName = string.Empty;
private static int _ThemeIndex;
private static Color[] _TitleBarBegin;
private static Color[] _TitleBarEnd;
private static Font _TextFont = new Font("Tahoma", 11f, FontStyle.Bold);
private static Brush _TextBrush = new SolidBrush(SystemColors.Window);
private static PointF _TextPoint = new PointF(6f, 2f);
private string _Caption = string.Empty;
#endregion
#region Constructors
public CtlGradientHeader()
{
InitializeComponent();
}
static CtlGradientHeader()
{
//setup colors
SetupOffice2003Colors();
SetupCurrentTheme();
SystemEvents.UserPreferenceChanged +=
new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
}
#endregion
#region Private Methods
private static void SetupCurrentTheme()
{
_ThemeName = NativeThemeMethods.GetThemeName();
switch (_ThemeName)
{
case BLUE_THEME:
_ThemeIndex = (int)eThemeIndex.BlueTheme;
break;
case GREEN_THEME:
_ThemeIndex = (int)eThemeIndex.GreenTheme;
break;
case SILVER_THEME:
_ThemeIndex = (int)eThemeIndex.SilverTheme;
break;
default:
_ThemeIndex = (int)eThemeIndex.NoTheme;
break;
}
}
private static void SetupOffice2003Colors()
{
_TitleBarBegin = new Color[] {
Color.FromArgb(0x59, 0x87, 0xd6),
Color.FromArgb(0xaf, 0xc0, 130),
Color.FromArgb(0xa8, 0xa7, 0xbf),
SystemColors.ControlDark };
_TitleBarEnd = new Color[] {
Color.FromArgb(3, 0x38, 0x93),
Color.FromArgb(0x63, 0x7a, 0x44),
Color.FromArgb(0x70, 0x6f, 0x91),
SystemColors.ControlDarkDark };
}
private void PaintControlGradient(PaintEventArgs e)
{
Color GradientBegin = _TitleBarBegin[_ThemeIndex];
Color GradientEnd = _TitleBarEnd[_ThemeIndex];
Rectangle rect = new Rectangle(new Point(0, 0), this.Size);
LinearGradientBrush gradientBrush = new LinearGradientBrush(rect,
GradientBegin, GradientEnd, 90f);
e.Graphics.FillRectangle(gradientBrush, rect);
if (_Caption.Length > 0)
//now paint the text
e.Graphics.DrawString(_Caption, _TextFont, _TextBrush, _TextPoint);
}
#endregion
#region Events
[System.Diagnostics.DebuggerStepThrough()]
protected override void OnPaint(PaintEventArgs e)
{
PaintControlGradient(e);
}
static void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
if (e.Category == UserPreferenceCategory.Color)
{
System.Diagnostics.Debug.WriteLine("Theme Changed :" + DateTime.Now.ToString());
SetupCurrentTheme();
}
}
#endregion
#region Properties
public string Caption
{
get { return _Caption; }
set
{
_Caption = value;
this.Refresh();
}
}
#endregion
#region Native Methods
private class NativeThemeMethods
{
[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
private static extern void GetCurrentThemeName(StringBuilder pszThemeFileName,
int dwMaxNameChars, StringBuilder pszColorBuff, int cchMaxColorChars,
StringBuilder pszSizeBuff, int cchMaxSizeChars);
[DllImport("uxtheme.dll")]
private static extern bool IsThemeActive();
public static string GetThemeName()
{
const int CAPACITY = 260;
const int VERSION_WINXP_MAJOR = 5;
const int VERSION_WINXP_MINOR = 1;
OperatingSystem os = System.Environment.OSVersion;
// check if the OS is XP or higher
if ((os.Version.Major == VERSION_WINXP_MAJOR
&& os.Version.Minor >= VERSION_WINXP_MINOR) ||
os.Version.Major > VERSION_WINXP_MAJOR)
{
if (IsThemeActive())
{
StringBuilder stringThemeName = new StringBuilder(CAPACITY);
StringBuilder stringColorName = new StringBuilder(CAPACITY);
StringBuilder stringSizeName = new StringBuilder(CAPACITY);
GetCurrentThemeName(stringThemeName, CAPACITY,
stringColorName, CAPACITY,
stringSizeName, CAPACITY);
return stringColorName.ToString();
}
else
return string.Empty;
}
else
return string.Empty;
}
}
#endregion
}
}