Windows Vista Aero Pt. 1 - Adding Glass to a Windows Forms Application

Windows Vista Aero Pt. 1 - Adding Glass to a Windows Forms Application

Rate This
  • Comments 63

In a brief departure from my usual ramblings about Windows Presentation Foundation, I wanted to write a couple of technical posts about using new Windows Vista shell features from managed code. To start off, I thought I'd talk about how to add glass to an existing WinForms applications.

Firstly, what do I mean by adding glass? As most people know, Windows Vista includes a new Aero theme; one aspect of that theme is the translucent borders that are supplied by the Desktop Window Manager. There's a lot of subtle sophistication in the way window frames are drawn - a drop shadow from the window, glow effects over the maximize / minimize / close buttons, a reflective texture on the window frame itself - so it's far more than just painting with a 50% opacity gray brush.

On suitably equipped machines, every window gets a glass frame (even Command Prompt!). But some applications extend that glass frame into the client area of the window for aesthetic reasons; for example, Internet Explorer extends glass into the address bar, and Windows Media Player uses glass for the playback controls. Your application can also take advantage of the API behind this to extend glass into its own client area. This isn't free - there's quite a hefty tax involved in rendering glass, so it's something to use sparingly rather than as the background for your whole window. Nevertheless, it's a great way to make your application feel like an integral part of the operating system on which it runs.

In this first part, I'll show you how to add glass to a Windows Forms application; in future entries, I'll cover some other related areas such as drawing text onto glass, using glass in a WPF application, and creating blur effects.

The single API call that does most of the dirty-work is the following one:

[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(
   IntPtr hWnd,
   ref MARGINS pMarInset
);

This call takes two parameters - a window handle and a MARGINS struct that contains information on how much extra the DWM should extend the frame on the top, left, right and bottom sides of the screen. Here's the declaration for MARGINS:

[StructLayout(LayoutKind.Sequential)]
public struct MARGINS
{
   public int cxLeftWidth;
  
public int cxRightWidth;
  
public int cyTopHeight;
  
public int cyBottomHeight;
}

The one big challenge with glass is getting alpha-blending to work correctly. Without using alpha-blending, then the content that you place on the glass will overwrite the glass itself and make it invisible. This is a problem in GDI, since it has no awareness of an alpha channel, but it's a little easier with GDI+. In your Windows Forms application, you simply need to set the TransparencyKey property to a color that you won't use elsewhere in the application (I use Gainsboro, for reasons that will become apparent later). Then you can create one or more panels that are docked to the margins of your form and set the background color for the panel to the transparency key. Now when you call DwmExtendFrameIntoClientArea, the glass will show within its margins wherever you've set something of the appropriate transparency key.

Here's an example of using the above API call (from a Form_Load event)

MARGINS margins = new MARGINS();
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyTopHeight = 45;
margins.cyBottomHeight = 0;

IntPtr hWnd = this.Handle;
int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);

So long as you've created the panel appropriately, you should now see glass in your application. You can now draw buttons, labels or other controls onto the surface and so long as you set their background color to be transparent, you'll see them integrating well with glass. Here's an example screenshot that puts it all together:

There's just one caveat, which is that the text smoothing doesn't work out quite right. Since it uses the panel background to determine the color it should smooth against, you'll hit problems if you pick a garish color for the transparency key such as Fuchsia - you'll see that the text renders with a horrid pink glow. That's because we've cheated a little bit with the text rendering. So long as you set the transparency color to something that's close to a typical glass color, this effect is barely noticeable (you can just see a little white fringing around the title text above, if you look really closely). That's why we chose Gainsboro as our color earlier. Fortunately, there's a better way to do it - if a little more convoluted. Win32 actually provides a useful API for this situation called DrawThemeTextEx that renders the text correctly on glass and also provides an appropriate back-glow that helps distinguish the text when it's over a complex background. Next time I cover this topic, we'll look at that API as well as discussing how you can detect whether the DWM is present and enabled or not (which is important if you want your application to run downlevel).

Download the sample application and source code (requires Windows Vista to execute).

  • Hi Sven, sorry - your comment didn't actually get eaten, but it did get lost amongst all the stupid search engine optimization spam that I have to moderate each day. Apologies.

    The problem is that GDI has no notion of transparency or an alpha channel, so there's some fancy footwork required to deliver this. You need to pick a single color (any color) out of the 16.7 million available to be your transparency key. A color like black would be a poor choice just because it crops up with such high frequency, but a less common color (such as #FCFDFE) would probably be adequate for most purposes. Hope that helps...
  • Tim, no problem, I realize the amount of spam you have to deal with every day is likely quite high.

    The problem with using TransparencyKey is that it will cause mouse actions (clicks etc.) to operate on the window below the glass (you can try it with your sample app, it happens there too) and it's that which I don't want. How can I extend glass into my client area, but still have mouse clicks on the glass parts register as clicks on my window, instead of the window below it?

    I know it's possible, IE7 et al do it too. But how?
  • Sven: I know how you'd do it in a C++ app; Windows sends a WM_NCHITTEST message to your application before sending mouse messages, to work out what sort of mouse message (if any) it should send to the application's input queue. By judicious handling of this message, you can make any part of your app act like a title bar, sizing border, or even completely transparent.

    Presumably you could override your form's WndProc to handle WM_NCHITTEST.
  • And what about adding glass in WPF application?
  • I've been looking at WM_NCHITTEST but it isn't the solution. Once TransparencyKey is set, the window doesn't receive WM_NCHITTEST messages (or any other mouse messages) for those areas whose colour matches the TransparencyKey.

    I've been checking using reflector, and TransparencyKey is implemented using the SetLayeredWindowAttributes Win32 function. And indeed, in the Platform SDK it says "Hit testing of a layered window is based on the shape and transparency of the window. This means that the areas of the window that are color-keyed or whose alpha value is zero will let the mouse messages through."

    So apparently, there is a way to extend glass into the frame without using a layered window with a color-key, since IE7 and other Vista applications manage to do it. But how? Is there perhaps some window style I can set for WS_EX_LAYERED windows that will prevent them from letting the mouse messages through? I don't mind if you give me the C++ solution, I'm familiar with C++ and Win32 and not afraid of PInvoke. :)
  • Recently Tim shared some code for extending Windows Vista Aero "Glass" inside a Windows Forms window. ...
  • TimS does a great post on getting the Vista “glass” look and feel in a WinForms app...
     
    http://blogs.msdn.com/tims/archive/2006/04/18/578637.aspx...
  • PingBack from http://www.nexusblogs.com/blogs/blogs-blog-at-runboardcom/
  • I just checked with Spy++, and the windows neither Windows Media Player 11 or IE7 use WS_EX_LAYERED, so they are not using a transparency key for their glass areas. So, how do they do it?
  • Any chance someone could convert this to VB .net?  My C++ is a little rusty, to say the least. The only part I dont get is the dll import. The other parts I can use in VB easily I think.

  • I’m starting to see more applications leveraging Aero Glass on Vista.  They look quite cool. ...
  • Ok, so one of the things I hope to achieve with this blog is to share all of the little gems that I come...
Page 2 of 5 (63 items) 12345