Categories
Programming

Drawing non-themed push buttons in Windows

When visual styles are enabled in Windows, one may use the DrawThemeBackground function to draw themed push buttons. However, users of Windows XP, Windows Vista and Windows 7 can disable visual styles by selecting the Windows Classic theme or one of the High Contrast themes (and in Windows 2000 and earlier, visual styles weren’t available at all). The classic theming mode has been removed in Windows 8 (I have written about this previously), so in that OS the visual styles APIs should always be available. I suspect it will be a long time before NT6.1 and earlier versions of Windows constitute an insignificant share of the market, though, so this post should be useful until then.Screenshot of Non-Themed Buttons

There are four states that a non-themed button can be in: normal, pressed, defaulted or inactive. How can we draw these states?

The rough non-themed equivalent of the DrawThemeBackground function is the DrawFrameControl function. It has a parameter uType that specifies the type of the frame control to draw – in our case, this should be DFC_BUTTON (= 4). The next parameter is uState. We should set this to DFCS_BUTTONPUSH (=0x0010). For the normal state, this is all that needs to be done. For the inactive state, we should set uType to DFCS_INACTIVE (=0x0100) | DFCS_BUTTONPUSH (in fact, this doesn’t seem to change the appearance of the button frame; the button content/text should be rendered as gray, but that’s out of the scope of this article).

When a non-themed button is pressed or defaulted, it has a single pixel border drawn around it, and the frame control’s bounding rectangle is contracted by 1 pixel on each side. The border’s colour is that of the window frame (COLOR_WINDOWFRAME = 6) – you can use the GetSysColorBrush function to get the appropriate brush. For the defaulted state, just call DrawFrameControl with uType set to DFCS_BUTTONPUSH (with the deflated rectangle). For the pressed state, it might seem like DFCS_PUSHED should be used for uType, but this creates an appearance different to that of standard push buttons. This style is used elsewhere, like on the Windows taskbar, for scrollbar buttons and window caption buttons, etc. Internet Explorer’s owner/custom-drawn buttons also use this style, and I’m sure many other programs do, too. It should be avoided, however, if you’re seeking the native appearance. To properly imitate a pressed push button, avoid using the DrawFrameControl function and use the FrameRect function to draw an inner border with the button shadow colour (COLOR_BTNSHADOW = 16). It’s important to then use the FillRect function to draw the background with the button face colour (COLOR_BTNFACE = 15).

Here’s some pseudocode to illustrate the whole process:

In addition to drawing the background, note that for all states but pressed, the content (text) should be shifted one pixel left and one pixel up. When the button is pressed, it should move one pixel right and one pixel down, so it is centred in the button. Some owner/custom-draw implementations neglect this detail.

Categories
Programming Windows

When should a program’s UI animations be disabled?

Modern frameworks like the Windows Presentation Foundation, its relative Silverlight and the upcoming Windows Runtime make it easy to add animations to a program’s graphical user interface. When implemented well, animations subtly improve the user experience and can demonstrate a high level of polish in your application. The Zune client software and Windows Live Messenger both feature beautiful animations – coincidentally, both were written with internal Microsoft frameworks (Iris and ‘DirectUI’, respectively; the second is apparently unrelated to anything in WinRT). The Twitter client MetroTwit shows what can be done in WPF (its interface was inspired by the Zune software and the Metro design language).

There are times, however, when animations may become detrimental to the user experience. Animations may appear choppy on lower-end or older systems (especially with heavy frameworks like WPF), and in general they should be disabled entirely for Remote Desktop Connection sessions (only very fast networks with low latency may be able to handle them). Jossef Goldberg from the Visual Studio team has written about the efforts made to bring the performance of Visual Studio 2010 over remote connections up to par, and while his advice is mainly about WPF, the same principles apply to other frameworks, also.

So, how can we decide whether animations should be disabled or not? The answer will likely vary from project to project, but there are a few things that we can do:

1. Check if the program is running in a remote session

In Win32, use the GetSystemMetrics function to get the SM_REMOTESESSION metric (a non-zero result means it is a remote session). In WPF, use the SystemParameters.IsRemoteSession property in the System.Windows namespace. In Windows Forms, use the SystemInformation.TerminalServerSession property in the System.Windows.Forms namespace.

2. Check if animations are enabled in Windows

Windows Vista introduced many new animations to common controls like buttons (notice the smooth glow effect and compare it to the binary states found in Windows XP). Regrettably this change made most owner-drawn implementations look shoddy since they mostly lack animation (e.g. buttons in Firefox), but that’s a topic for another post.

Anyway, these animations can be switched off in the following dialog (aside: the list box was mercifully increased in size in Windows 7, though the window still isn’t resizable):Performance Options Screenshot

If the user has disabled animations here, it’s probably a good idea to respect that choice in any program you write. We can use the SystemParametersInfo function with the SPI_GETCLIENTAREAANIMATION parameter to check the system setting (or use the SystemParameters.ClientAreaAnimation property in the System.Windows namespace, but note that this always returns true in Windows XP). This only applies to Windows Vista and later.

3. WPF-only: Check the graphics rendering tier

The RenderCapability.Tier property in the System.Windows.Media namespace gives an indication of the graphical capabilities of the system. There are currently 3 tiers, and while the exact meaning may change between .NET releases (as it did in .NET 4.0), this property can be used to help determine what animations, if any, should be enabled.

4. Ask the user!

It can’t hurt to give the user a choice about animations. The remote session checks described above will work for RDP, but there are other technologies available, and we don’t want to punish VNC users by invalidating the entire window 60 times per second for some fancy animation, even if we can’t tell that they’re connecting remotely. If exposing such an option in the UI doesn’t make sense, at least offer it as a command line switch.

Addendum (2011-11-20)

Snooping around PresentationFramework.Aero.dll in .NET Reflector, I happened to come across the ‘Animates’ property in the ButtonChrome class (Microsoft.Windows.Themes namespace):

Similar properties can be found in the other *Chrome classes. Checking the computer’s power status isn’t something covered in this article, but it’s certainly a good idea.

Categories
Programming Windows

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.

Categories
Programming Windows

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.

Categories
Programming

WM_DWMCOLORIZATIONCOLORCHANGED doesn’t give the Aero Glass base colour

From MSDN:

WM_DWMCOLORIZATIONCOLORCHANGED Message

Sent to all top-level windows when the colorization color has changed.

Parameters
wParam: Specifies the new colorization color. The color format is 0xAARRGGBB.
lParam: Specifies whether the new color is blended with opacity.

We receive this message when the Aero Glass colour changes. Unfortunately, the value contained in wParam is not the ‘base colour’, as it is in fact the result of the DwmGetColorizationColor function. As Rafael Rivera noted last year, when glass transparency is enabled, the value returned by this function is quite different to the base colour:

colorization

As such, we shouldn’t rely on the contents of wParam (or on the DwmGetColorizationColor function in general).

The easiest method to find the actual base colour is to retrieve the ColorizationColor DWORD from HKCUSoftwareMicrosoftWindowsDWM. This, however, is undocumented, and could potentially change in a future version of Windows (it is correct in NT 6.0 and 6.1):

(We can’t cast directly to uint despite it being a REG_DWORD, hence the first cast to int…)

For those who feel more adventurous, Rafael went to the trouble of finding a function in dwmapi.dll which returns the base colour (amongst other things). He describes it here.

Categories
Programming

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).

Categories
Keiki Programming

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.

Categories
Keiki Programming

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.

Categories
Keiki Programming

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).

Categories
Keiki Programming

Windows 7-style Notification Area Applications in WPF: Part 3 (Taskbar Position)

View source on GitHub.

In the previous post in this series, I showed how to find the location of a notify icon by implementing the new Windows 7 Shell32.dll function Shell_NotifyIconGetRect in managed code for use with the System.Windows.Forms.NotifyIcon class.

In this post, I will look at how to accurately position a window above (or adjacent to) a notify icon, no matter the location of the taskbar (barring a certain issue that I note at the end of this post :)). As I noted in Part 2, the current version of Keiki is hardcoded to appear in the bottom right-hand corner of the user’s screen, making it very out of place when the taskbar is moved to the top/right/left of the screen. We’ll need to delve into the Win32 API once again, but as in Part 2 the amount of code required is not daunting, even for a Win32 beginner like me.