Aero Glass inside a WPF Window

Recently Tim shared some code for extending Windows Vista Aero "Glass" inside a Windows Forms window.  It sounds like he's eventually planning on showing this with WPF (among other things).  I also looked into this for my upcoming WPF book, and I couldn't wait to share the results of my little WPF glass experiment!  Sorry, Tim, if I'm stealing your thunder!

NOTE: This code will require WPF Beta 2 or later (and Windows Vista).  I'll share the whole project (including an updated Internet Hearts app) when Beta 2 is released.

I took the WPF UI I created for the Internet Hearts migration, slapped it in a System.Windows.Window, and "glassified" it.  Here is the result:

I didn't change a thing in the Hearts UI, and everything worked as expected: the 2D & 3D animations, the elements that already had partial opacity (like the card table), etc.

And when doing a "Flip 3D" (which I'm surprisingly finding more useful than the standard Alt+Tab), the result is pretty cool:

Of course, I'm now breaking all sorts of style, usability, and performance guidelines by my overzealous use of glass, but it's still an interesting exercise!

All it took was this function, which extends the glass frame into the client area of any WPF Window based on the passed-in margin:

public class GlassHelper
{
  public static bool ExtendGlassFrame(Window window, Thickness margin)
{
if (!DwmIsCompositionEnabled())
return false;

    IntPtr hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero)
throw new InvalidOperationException("The Window must be shown before extending glass.");

    // Set the background to transparent from both the WPF and Win32 perspectives
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;

    MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
}

We need the HWND of the Window to pass to DwmExtendFrameIntoClientArea, and WindowInteropHelper enables us to get it.  The MARGINS structure controls how much to extend the glass on each of the four sides.  To get the "sheet of glass" effect, you can pass -1 for all four sides.

The trickiest thing is knowing to set not only Window's Background to Transparent, but also the BackgroundColor of the corresponding HwndSource's CompositionTarget (which is set to black by default).

The MARGINS structure passed to DwmExtendFrameIntoClientArea is similar in spirit to WPF's Thickness type, so I chose to expose Thickness from ExtendGlassFrame and defined MARGINS with a constructor that does the conversion:

struct MARGINS
{
public MARGINS(Thickness t)
{
Left = (int)t.Left;
Right = (int)t.Right;
Top = (int)t.Top;
Bottom = (int)t.Bottom;
}
public int Left;
public int Right;
public int Top;
public int Bottom;
}

The two DWM APIs are defined as follows:

[DllImport("dwmapi.dll", PreserveSig=false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

[DllImport("dwmapi.dll", PreserveSig=false)]
static extern bool DwmIsCompositionEnabled();

Finally, the Window calls ExtendGlassFrame inside OnSourceInitialized:

protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// This can't be done any earlier than the SourceInitialized event:
GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
}

Note that with Thickness I can pass a single value (-1) that will be used for all four MARGIN fields.

I'm going to add dwmapi.dll (and other Windows Vista DLLs) to pinvoke.net so folks can start sharing managed signatures for new APIs such as this.  Enjoy!