Windows Theme Fonts Redux & Sample Code

In a post earlier this year, I investigated how to retrieve information about theme fonts in Windows. Briefly, the Visual Styles APIs can be used when visual styles are enabled, but values need to be hard-coded (to some extent) otherwise.

Andrew Powell commented on my previous post noting difficulties in implementing the GetThemeFont function in managed code. In this post, I’ll demonstrate how to implement the relevant functions in a simple WPF project. In particular, I’ll focus on displaying information about the ‘main instruction’ text style as seen in Task Dialogs.

Read on for details.

Part 1: Fallback Values

As noted last time, the Visual Styles APIs don’t work when visual styles are disabled.

The first step is to call the OpenThemeData function, which takes a window handle and a class name and returns a handle to theme data. If no match to the specified class name is found, NULL is returned. The class we will reference is TEXTSTYLE, which includes the TEXT_MAININSTRUCTION part. The parts and states for standard controls can be found here.

We need to use PInvoke to call OpenThemeData (and the associated CloseThemeData). As usual, PInvoke.net is very useful for getting PInvoke signatures.

C#

1

2

[DllImport("uxtheme.dll",ExactSpelling=true,CharSet=CharSet.Unicode)]

publicstaticexternIntPtr OpenThemeData(IntPtr hWnd,StringclassList);

C#

1

2

[DllImport("uxtheme.dll",ExactSpelling=true)]

publicexternstaticInt32 CloseThemeData(IntPtr hTheme);

C#

1

IntPtr hTheme=OpenThemeData(IntPtr.Zero,"TEXTSTYLE");

If hTheme is NULL (that is, IntPtr.Zero), we can assume that visual styles are disabled (and in the event that this is not the case, we still won’t be able to use GetThemeFont and GetThemeColor). The fallback values that we should use are defined in AeroStyle.xml, which can be found in the Windows SDK.

XHTML

1

2

3

4

5

6

7

8

9

10

11

<class name="TextStyle">

<part name="MainInstruction">

<caption>

<TextColor>0 51 153</TextColor>

<Font>Segoe UI, 12, Quality:ClearType</Font>

<ClassicValue type="TextColor">WindowText</ClassicValue>

<ClassicValue type="Font">CaptionFont</ClassicValue>

</caption>

</part>

...

</class>

Fortunately, the SystemFonts and SystemColors classes in the System.Windows namespace allow easy access to these values:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

FontFamily fontfamily;

doublefontsize;

FontWeight fontweight;

Brush foreground;

if(hTheme==IntPtr.Zero){

fontfamily=SystemFonts.CaptionFontFamily;

fontsize=SystemFonts.CaptionFontSize;

fontweight=SystemFonts.CaptionFontWeight;

foreground=SystemColors.WindowTextBrush;

}else{

...

}

Part 2: GetThemeFont and GetThemeColor

In the case where visual styles are enabled, hTheme will hold a handle to theme data that we will pass to the GetThemeFont and GetThemeColor functions.

At this point, we’re finished, though we need to call CloseThemeData, preferably in a finally block.

Update (2011-10-25): Something I missed in the original implementation was that the lfHeight value represents the font height in pixels, which is a problem as WPF uses device-independent units (DIUs) for its measurements (something I’ve written about before). To properly support non-default DPI settings, the value in pixels should be converted to DIUs. I’ve added code to the sample project below that does this.

6 thoughts on “Windows Theme Fonts Redux & Sample Code”

Hi,
first of all, thank you for this post. It was very helpful for me.
However I ran into an weird issue on all Vista machines. Maybe you have heard of it or know a solution.
I need to get the fonts for “TEXT_BODYTEXT” and “TEXT_BODYTITLE”.
On Win7 I the method GetThemeFont() returns the font family “Segoe UI” and a font weight of 400 (text) or 700 (title).
On Vista however the font family for the title is “Segoe UI Bold”, which cannot be used to create a FontFamily object.
I cannot remove the “Bold” from the string either, because the font name is different in every language.

I have a made a workaround where I just use the font of the text and make it bold, but that’s not a very elegant solution.
Is this the fault of Microsoft? I just want to avoid ending up making a workaround for every system.
I haven’t even tested it on Win8 yet.

FontFamily x = new FontFamily("Segoe UI Bold");
Works for me as expected in Windows Vista, Windows 7 and Windows 8. Are you getting an error when you try to create the object, or is the font just not appearing as you’d expect?

You shouldn’t need any workarounds (that’s the whole point of this code :)).