Theming & Visual Styles in Windows 8

The following is based on the Windows Developer Preview and might not accurately represent the final version of Windows 8.

A significant change in Windows 8 is the removal of support for Windows classic theming. In the Windows Developer Preview, there is no Windows Classic theme, and all themes (including Windows Basic and the four high contrast themes) use the Desktop Window Manager (DWM) for desktop composition (in Windows Vista and Windows 7, it was only enabled for Aero themes).

While some users may miss the Windows Classic theme (it might return in later builds, of course), this is definitely a positive development. All themes will receive the benefits of hardware acceleration, and there will no longer be the need to maintain a separate code path for when the DWM is disabled (as long as the program targets only Windows 8 or later).

In order to maintain compatibility with earlier versions of Windows, when a high contrast theme is selected, Windows 8 will simulate the Windows classic theming model unless an application specifies Windows 8 as a supported OS in its application manifest. The sample program I made for the previous post did not include such a manifest, so it doesn’t work correctly in the Windows Developer Preview with high contrast themes:WPF sample without manifest

Compare this to a task dialog:

Task Dialog (High Contrast #1)

(The button in the WPF window is also drawn incorrectly, but presumably that will be fixed in .NET 4.5.)

Adding the appropriate manifest to the application causes it to work as it should:

WPF sample with manifest

I’ve updated the sample code in the previous post to include the manifest file.

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.

Continue reading “Windows Theme Fonts Redux & Sample Code”

Windows Theme Fonts

View source on GitHub.

Update: See this post for a sample implementation in WPF.

Screenshot of Task Dialog (Aero)

Have you ever wondered how to access the various font colours and styles found throughout Windows, such as that of the ‘Main Instruction’ text in the Task Dialog shown above?

If you are using WPF, the SystemFonts class might sound promising at first. However, this class only exposes the following: the icon font, caption font, small caption font, menu font, message font and status font. These aren’t very exciting – in fact, they are all simply 9pt Segoe UI in Windows Vista/7 Aero. (Aside: early Windows 8 builds use 11pt Segoe UI Semilight as the caption (and small caption) font.) For those using Win32 directly, the SystemFonts class wraps around the SystemParametersInfo function (specifically with the messages SPI_GETNONCLIENTMETRICS and SPI_GETICONTITLELOGFONT) the GetThemeSysFont function.

MSDN offers some guidance on default fonts and colours in Windows Vista/7: apparently ‘Main Instruction’ text is 12pt #003399 Segoe UI. This table, while helpful, is not comprehensive, and in general it’s not a good idea to hard-code this kind of thing, as themes/visual styles are liable to change.

The keys lie in the Visual Styles APIs, introduced in Windows XP. In particular, the GetThemeFont function and GetThemeColor function (with the TMT_TEXTCOLOR property identifier), both found in UxTheme.dll. We simply need to specify the ‘part and state’ of the control in question (these are defined in Vsstyle.h and Vssym32.h). ‘Main Instruction’ text, for example, is referenced by the TEXT_MAININSTRUCTION part in the TEXTSTYLE class.

Screenshot of Task Dialog (Classic)

Regrettably, visual styles APIs only work when visual styles are enabled (who’d have thought it?). That is to say, we can’t rely on them with classic themes (Windows Classic and the High Contrast themes).

I emailed the very knowledgeable Larry Osterman about this, and he was kind enough to respond:

AeroStyle.xml tells which metrics to ask for which theme parts (for the OS that matches the version of the SDK it’s in), but there’s no theme API support for classic modes.

Basically they get the metric they’re looking for from the AeroStyle.xml file.

AeroStyle.xml is included in the latest versions of the Windows SDK. It contains the same classes and parts and states mentioned earlier in an XML format. The ‘MainInstruction’ part in the ‘TextStyle’ class looks like this, for instance:

Of interest are the ‘ClassicValue’ elements. When visual styles are disabled, it seems that ‘Main Instruction’ text uses the caption font (8pt bold Microsoft Sans Serif, as it happens).

In closing: you can use GetThemeFont and GetThemeColor if visual styles are enabled, but you will need look at AeroStyle.xml and hard-code the classic theme fall-back values.

SetDPI Utility

View source on GitHub.

Updated 2013-01-06.

Ken Silverman’s PNG compression tool PNGOUT (complemented nicely by the free .NET frontend PNGGauntlet) can be remarkably effective at trimming the size of PNGs without altering the image described within.

However, in its quest to remove anything non-essential, PNGOUT by default strips out the image’s DPI (in fact PPI) information. PNGs without PPI information will be treated differently by different software.

WPF either uses a default of 96 or the current system DPI with such images (I’m not sure which, but the latter makes more sense). Sometimes this can have nice side-effects, as Scott Hanselman discovered – images that were designed for 96 PPI but set to 72 PPI were suddenly ‘fixed’ (at least when the application was run in a 96 DPI environment). Better than relying on WPF’s interpretation of PNGs without PPI information is to correctly set the PPI in the first place. For example, if an image is designed for 120 DPI but has its PPI set to 96, WPF will (correctly) try and scale the image, which is clearly not desirable.

PNGOUT features a command line option /k# for removing or keeping optional chunks. The pHYs chunk holds the PPI information, which is what we want to leave alone. Using the command line option /kpHYs with PNGOUT will thus preserve PPI information. (Information from WulfTheSaxon.)

Sometimes, though, it is useful to have a utility that sets PPI information for lots of images at once (the PPI information in the PNGs may not be correct before using PNGOUT, for instance, rendering the /kpHYs switch pointless).

Josip Medved had the same thought and created a tool for setting the PPI to 96 for many images at once.

I decided to slightly extend his tool to take the desired horizontal and vertical PPI as command line arguments. The source and binaries can be downloaded here (SHA-1: 800D83A390F2AD80772D79D7FB45C7EAAB0D4294). The usage is SetDPI dpiX dpiY filepattern1 [filepattern2 […]].

Examples:

  • SetDPI 96 96 *.png (sets all PNG files to 96 PPI)
  • SetDPI 120 120 a.png b.png c.png (sets a.png, b.png and c.png to 120 PPI)
  • SetDPI 144 144 C:Images*.png (sets all PNG images in directory C:Images to 144 PPI)

Pixel Measurements in WPF

Part of the beauty of the Windows Presentation Foundation is that it is designed to be resolution independent – that is, a WPF application should scale perfectly no matter the system’s DPI setting (excluding any bitmap graphics, which mightn’t look as nice as the vector parts).

Instead of measuring things in pixels, WPF uses Device Independent Units (DIUs). If you only ever use 96 DPI (100%), you won’t notice the difference: at that setting, 1 pixel is the same as 1 DIU. Increase the system DPI, though, and this will no longer hold true. For example, at 120 DPI (125%), 1 DIU will be represented as 1.25 pixels. At 144 DPI (150%), 1 DIU = 1.5 pixels. At 192 DPI (200%), 1 DIU = 2 pixels.

Notice that at 120 DPI and 144 DPI, the number of pixels in 1 DIU is not an integer. In cases like these – when edges fall in the middle of screen pixels – WPF uses anti-aliasing by default. This can, however, result in lines that seem blurry (after all, that’s what anti-aliasing does). If this behaviour is undesirable, one can use UIElement’s SnapsToDevicePixels property.

WPF’s built-in handling of DPI scaling is great: in most cases, it should be transparent to both the programmer and user.

Sometimes, though, one might want to measure lengths in pixels, not DIUs. For example, Keiki’s custom window border for when the DWM (Aero) is disabled needs to be 1 pixel thick at any DPI (this is how the system’s notification area applications look). So, how can we specify this?

First, we need to get the system’s DPI setting:

dpiX and dpiY will hold values like 1.0 (96 DPI), 1.25 (120 DPI), 1.5 (144 DPI) and 2.0 (192 DPI).

To specify a pixel measurement, we simply divide the DIU measurement by the DPI factor (dpiX and dpiY should always be the same – at least in Windows 7 and earlier):

This will make the margin 3 pixels thick at any DPI. (For example, at 120 DPI, dpiX = 1.25. 3 / 1.25 = 2.4 DIUs. 2.4 DIUs will be converted by WPF to 3 pixels.)

Fedir Nepyivoda has a neat solution to this problem: instead of manually converting DIU measurements, he created a PixelBorder control (inheriting from Border) that overrides MeasureOverride. Have a look here.

Small Icon Size & DPI in Windows

The GetSystemMetrics function in Windows retrieves system metrics and configuration settings. One such metric is the recommended size (width and height) of ‘small icons’:

Small icons typically appear in window captions and in small icon view.

Another place where small icons show up is the notification area.

MSDN contains a guide to Creating DPI-Aware Applications. It notes the challenge posed by raster graphics and different DPIs. Unlike vector graphics, which can scale without a loss in quality, distinct raster images must be created for different resolutions in order to avoid unpleasant scaling artefacts. (In fact, this is perhaps overstating the benefits of vector graphics: the level of detail suitable for a high-resolution image is not necessarily suitable for a low-resolution image, so using the same vector for all sizes doesn’t always make sense.) Windows icons, as of Windows 7, contain only raster graphics.

Small Icon Sizes

The small icon size varies according to the system DPI and OS version:

DPI Setting Windows 7, XP Windows Vista
96 (Default, 100%) 16×16 16×16
120 (125%) 20×20 22×22
144 (150%) 24×24 26×26
192 (200%) 32×32 36×36

The sizes for Windows Vista (apart from the size at 96 DPI) don’t make much sense – they don’t match up with the DPI scaling ratio. It is possible that this was a mistake, hence the change in Windows 7.

So, if you want to make sure your small icon (e.g. notify icon) looks beautiful under the widest possible range of systems, you should ideally include the images with the sizes 16×16, 20×20, 22×22, 24×24, 26×26, 32×32 and 36×36. If that sounds like a lot of work (it shouldn’t be with a high quality tool like Axialis IconWorkshop), Microsoft’s own recommendation is to include 16×16 and 32×32 pixel icons. Keep in mind, though, that scaling either of those sizes to 20×20 or 22×22 pixels can result in a rather awful-looking icon. While it is still rare to see anything but the default setting of 96 DPI, this will not be the case forever.

WinForms Icon Sizes

When using the System.Drawing.Icon class, it is a good idea to use one of the constructors that takes the icon size as a parameter.

Windows Forms conveniently exposes a property called SmallIconSize in the SystemInformation class. This property gives us a System.Drawing.Size corresponding to values listed above, which we can put straight into our icon constructor:

The equivalent properties in WPF are SystemParameters.SmallIconWidth and SystemParameters.SmallIconHeight.

Both WPF and WinForms wrap around the Win32 GetSystemMetrics function taking the arguments SM_CXSMICON (small icon width) and SM_CYSMICON (small icon height).

Windows 7-style Notification Area Applications in WPF: Recap & Sample

View source on GitHub.

Over the past month I’ve looked at how to implement a Windows 7-style notification area application in WPF.

I covered 6 different topics:

As promised, I’ve put together a small sample project to illustrate all this code working together (with some added polish):

NotifyIconSample.7z
42,775 bytes; SHA-1: 513E998F4CCFC8C5BB6CA9F8001DA204C80FDF3A

The code has a good level of documentation, but I recommend you read the above posts to understand the ideas behind it.

Windows 7-style Notification Area Applications in WPF: Part 6 (Notify Icon Position: Pre-Windows 7)

View source on GitHub.

In Part 2 of this series I demonstrated how to use the Shell_NotifyIconGetRect function to find the position of a notify icon. This function is new to Windows 7, however, and we must find a different solution for earlier versions of Windows.

This turns out to be quite difficult. A post on the MSDN forums by user parisisinjail provided a good starting point, and it led me to a Code Project article by Irek Zielinski that explained exactly what to do – in native code. This post shows how to implement this kind of approach in managed code.

I have verified that the following code works with Windows XP and Vista. It also works under Windows 7, but only when the icon is not located in the notification area fly-out (not found in previous versions). As such, this solution should only be used when Shell_NotifyIconGetRect is not available.

The basic idea is that the notification area is actually just a special Toolbar control, with each icon being a toolbar button. We want to find the toolbar control and loop over the buttons until we find the one that corresponds to our notify icon. We can then find the coordinates of the toolbar button. (The fly-out in Windows 7 has a separate toolbar control, so you could search that instead of using Shell_NotifyIconGetRect if you really wanted to.)

The process sounds straight forward, but the implementation is quite tricky. Read on for the code.

Continue reading “Windows 7-style Notification Area Applications in WPF: Part 6 (Notify Icon Position: Pre-Windows 7)”

Windows 7-style Notification Area Applications in WPF: Part 5 (Fixing Aero Borders)

View source on GitHub.

An issue that came to my attention only recently is that the borders of WPF (update: WPF is not actually to blame) windows without captions/title-bars (that is, with ResizeMode set to ‘CanResize’ and WindowStyle set to ‘None’) are drawn incorrectly when the DWM (read: Aero Glass) is enabled. Specifically, the upper and left borders are drawn one pixel too thin (e.g. 3 pixels when the system setting is 4 pixels) and the colour of the bottom and right borders is different to that of other windows. I’ve tried to illustrate these differences in the screenshot below (the image on the left is of a WPF window).

Aero Border Example

It is conceivable that this will be fixed in a future version of WPF Windows, but for now we can use the DwmExtendFrameIntoClientArea function to do it manually. This is only necessary when the DWM is enabled, of course.

Continue reading “Windows 7-style Notification Area Applications in WPF: Part 5 (Fixing Aero Borders)”

Windows 7-style Notification Area Applications in WPF: Part 4 (Multiple Monitors)

View source on GitHub.

At the end of Part 3 in this series, I noted that the window positioning logic depends on accurately getting the bounds of the monitor where the notify icon is located. Specifically, we require the bounds of the working area (the space on the monitor excluding the taskbar and other docked items). WPF gives us the System.Windows.SystemParameters.WorkArea property, but this gives us the area of the primary display monitor’s working area, and the taskbar (and thus notify icon) might be located on a different monitor. Unfortunately, support for accessing information about anything other than the primary monitor with the SystemParameters class seems to be absent as of .NET 4.0.

We could use the System.Windows.Forms.Screen class to easily solve this problem: the System.Windows.Forms.Screen.GetWorkingArea method has an overload for finding the working area of the monitor that contains a given rectangle, which is exactly what we need to do. However, I am going to opt to use Win32, instead, simply to avoid depending on WinForms (yes, I realise that sounds a bit strange given that this project revolves around a System.Windows.Forms.NotifyIcon). In any case, there is something to be said for knowing what it is that all these .NET functions wrap around 🙂

The two functions we’ll use are MonitorFromRect (to find the handle of the monitor containing the notify icon) and GetMonitorInfo (to get the working area of that monitor).

Continue reading “Windows 7-style Notification Area Applications in WPF: Part 4 (Multiple Monitors)”