logo
vbRad Home
Source Code
Book Reviews
Forum
Links
About Us
Contribute

Compare Databases with SQL Effects Clarity
 
 The Mystery of the Themed TitleBar Label Control

Posted on
4/18/2006
Author:
Robert Gelb
Email:
Not Shown
Applies To OS:
Windows
Product:
Visual Studio 2005



Download:

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
    }
}

Download:

Add Your Comment  

Name: Email Address: all fields optional
Notify me via email when someone responds to this message (valid email required).

Enter the word:
 



Comments
#1. By Carlos. Posted on 5/4/2006 4:49:18 PM
Hi Robert,

this is a really nice work. Nevertheless you can free your code from native methods by using VisualStyleInformation.ColorScheme. This will replace your used native method.

Best regards
Carlos.

#2. By Robert Gelb. Posted on 5/12/2006 1:37:13 AM
Carlos, you are absolutely right. I didn't realize VisualStyleInformation.ColorScheme was in .NET 2.0.
It isn't in .NET 1.1 though.

#3. By jignesh. Posted on 5/19/2006 7:15:17 AM
hi i m jignesh
i have make on retailshop software but there is one porblem in preview report.
in software there is invoice no,invoice date, customer name there are three textboxes and one msflexgrid.
in this software on customer purchase some things like shop, toothpaste,shaving cream etc..
so, i add a recrod all thing and all the data will be go in grid against one purchase invoice but i when i generate invoice it will be generate repeatadly. i want to preview like as follows
*****
invoice no :_________ invoice date :_________
Customer name :_______________________________

sr.no. description qty price
--------------------------------------------------
1 shop 1 10.00
2 toothpaste 1 50.00
3 shaving cream 1 60.00

so, please solve this problem i will wait for ur reply


bye

#4. By Paul Shaaf. Posted on 12/8/2006 2:00:16 AM
hey, my name is paul and I LOVE VISUAL BASIC!!!! i really need to know if i can get this software online for free or from limewire or something. I'd appreciate some help. brrrrrrrrr.

-paul brrrrrr

#5. By Anonymous. Posted on 12/29/2007 8:15:04 PM
That is some sweet code right there.

#6. By Anonymous. Posted on 12/29/2007 8:15:59 PM
Nice. Thanks for your work.

#7. By jeetesh. Posted on 2/18/2010 10:32:11 AM
logo
vbRad Home
Source Code
Book Reviews
Forum
Links
About Us
Contribute

Compare Databases with SQL Effects Clarity


The Mystery of the Themed TitleBar Label Control

Posted on
4/18/2006
Author:
Robert Gelb
Email:
Not Shown
Applies To OS:
Windows
Product:
Visual Studio 2005



Download:

* Solution (17 kb)

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;



Operati