Last time around, I suggested, "Let's see if I can talk about something other than rendering text next time around..." It looks like the answer to that one is no.

Windows Vista has reached Beta 2, and we are already beginning to see documentation around lighting up your applications on Windows Vista. One of the ways to make your application feel more like Vista is to use fonts consistent with the UI: Segoe UI. The fonts section of the Windows Vista User Experience Guideliness discusses using Segoe UI - the system font. This documentation specifies, "From code, you can determine the system font properties (including its size) using the GetThemeFont API function."

Unfortunately, this is all of the direction that it provides. Anybody who has played around with the GetThemeFont API realizes that this is easier said than done. There are a respectably large number of permutations of classes, parts, and states, and anybody who is working in managed code will need to rifle through include files from the Platform SDK to locate all of the values to pass in. Once you have that working, you will then discover that the vast majority of permutations do not contain a font value at all, and the documentation is pretty much silent on the topic of which values to use to improve your chances of success, making this all the more challenging to the developer.

You can avoid a significant amount of p/invoke code by using the Visual Styles API - the VisualStylesRenderer class provides a GetFont method that wraps the unmanaged GetThemeFont API for you, but you are still left with the problem of locating a permutation of class, part, and state that return a Font at all, let alone the one you are hoping to use!

Personally, I find it much easier to use the GetThemeSysFont API to retrieve the system font. With this API, you specify whether you want to retreive the caption, small caption, menu, status, message box, or icon title font. Personally, I use the message box font, given that this font is also designed to display readable, sentence-formatted text. Lets' take a look at how this looks:

GetThemeSysFont on Windows XP SP-2

GetThemeSysFont on Windows Vista Beta 2

The source code to construct this simple example follows:

namespace GetThemeSysFont {

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using System.Windows.Forms.VisualStyles;

    public partial class Form1 : Form {

        #region p/invoke declarations

        [DllImport("uxtheme.dll", ExactSpelling = true,
            CharSet = CharSet.Unicode)]
        private static extern IntPtr OpenThemeData(IntPtr hWnd,
            String classList);

        [DllImport("uxtheme", ExactSpelling = true,
            CharSet = CharSet.Unicode)]
        private extern static Int32 GetThemeSysFont(IntPtr hTheme,
            int iFontId, out LOGFONT plf);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct LOGFONT {
            public int lfHeight;
            public int lfWidth;
            public int lfEscapement;
            public int lfOrientation;
            public int lfWeight;
            public byte lfItalic;
            public byte lfUnderline;
            public byte lfStrikeOut;
            public byte lfCharSet;
            public byte lfOutPrecision;
            public byte lfClipPrecision;
            public byte lfQuality;
            public byte lfPitchAndFamily;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string lfFaceSize;
        }

        private const int TMT_CAPTIONFONT = 801;
        private const int TMT_SMALLCAPTIONFONT = 802;
        private const int TMT_MENUFONT = 803;
        private const int TMT_STATUSFONT = 804;
        private const int TMT_MSGBOXFONT = 805;
        private const int TMT_ICONTITLEFONT = 806;


        #endregion

        public Form1() {
            InitializeComponent();
            Font themeFont = messageBoxFont();
            platformLabel.Font = themeFont;
            platformLabel.Text = Environment.OSVersion.VersionString;
            themeFontLabel.Font = themeFont;
            themeFontLabel.Text = "Message Box Font: " + themeFont.Name +
                " " + themeFont.SizeInPoints + "pt";
        }

        private Font messageBoxFont() {
            // You can avoid p/invoke altogether in managed code
            // using the following:
            // return SystemFonts.MessageBoxFont;
            LOGFONT pFont;
            IntPtr hTheme = OpenThemeData(this.Handle, "WINDOW");
            GetThemeSysFont(hTheme, TMT_MSGBOXFONT, out pFont);
            return Font.FromLogFont(pFont);
        }
    }
}

This approach will work well in both managed code and unmanaged code. Unfortunately, I am not aware of a wrapper in the managed Visual Styles APIs for GetThemeSysFont, so I went the p/invoke route.

Are there other alternatives to getting the system font? Yes there are! (If you read the commented out code, you will even see one - we'll get to that one in a bit.)

Well, we are getting the message box font. If you poke around the NONCLIENTMETRICS structure you can retrieve from a call to SystemParametersInfo, you will notice that there is a lfMessageFont LOGFONT that returns the message box font. This works in both managed and unmanaged code, and has the distinct advantage of not taking a dependency on uxtheme.dll - which you will only find on Windows XP and above.

What about nice shortcuts in the managed world? The SystemFonts class, by its very name, suggests that it will return the correct system font. Unfortunately, if you go with the most obvious choice, the DefaultFont property, you will be handed a font that is very clearly not Segoe UI. What has happened here? Well, internally, the DefaultFont property has a very strong tendency to return the hard-coded value of Tahoma (depending on your locale). Otherwise, you get the outcome GetStockObject(DEFAULT_GUI_FONT). Wait a minute, the DEFAULT_GUI_FONT? The one Raymond Chen describes as, "DEFAULT_GUI_FONT has an even less illustrious history. It was created during Windows 95 development in the hopes of becoming the new default GUI font, but by July 1994, Windows itself stopped using it in favor of the various fonts returned by the SystemParametersInfo function. Its existence is now vestigial."? Yes, that's the one.

OK, so DefaultFont isn't a particularly good choice. That's unfortunate, because it just "feels" like it should be the right choice.

However, the MessageBoxFont property does, indeed, wrap the SystemParametersInfo function with an argument of NONCLIENTMETRICS, giving you a straightforward way to go after this font without having to use p/invoke.

So, in the end, MessageBoxFont is probably the easiest option to go after Segoe UI on Vista using managed code, while providing acceptable results on existing platforms.