Windows Theme Fonts Redux & Sample Code

View source on GitHub.

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.

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.

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

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.

Firstly, the PInvoke signatures:

Let’s start with GetThemeColor:

Now for GetThemeFont:

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.

Download Sample

You can download a working sample here (updated 2011-10-25):

WindowsThemeFontsSample.7z

14,489 bytes; SHA-1: B01722E889780D94CDB26E31F115F4B2DF2E7111

WPF Windows Theme Fonts Sample: Aero

Task Dialog: Aero

WPF Windows Theme Fonts Sample: Windows Classic

Task Dialog: Windows Classic

WPF Windows Theme Fonts Sample: Windows Classic (Custom)

Task Dialog: Windows Classic (Custom)

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

  1. 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.
    Best regards,
    Tobias

    1. Hi Tobias, thanks for the comment.
      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 :)).

  2. Hi, your demostration doesn;t show how to change the font size, do you have this demo?
    Only get font() cannot do anything.

  3. Can we use the same code to change font and size in WinForms (and not WPF)?

    1. The P/Invoke calls will work just the same, but you’ll find Windows Forms has different classes and methods for dealing with fonts. It’s actually more straightforward: just use Font.FromLogFont for the font and Color.FromArgb for the colour.

Leave a Reply

Your email address will not be published. Required fields are marked *