How to extend the Aero Glass into the Client area
Introduction
Windows Vista has a new default theme called Aero glass. In Aero glass, the title bar of a window and the frame is drawn transculent. This gives the UI a clean and lightweight look. This nice feaure is provided by a service that is called the desktop window manager (DWM).
Extending the Glass
By default the blurry glass effect is only on the title bar and the frame, but the client area is drawn opaque. But there is a simple way to extend the glass into the client area by using the DWM's API.
First thing you need to do is to include some Win32 functions from the dwmapi.dll
library.
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows;
[StructLayout(LayoutKind.Sequential)]
struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[DllImport("dwmapi.dll")]
static extern int
DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
[DllImport("dwmapi.dll")]
extern static int DwmIsCompositionEnabled(ref int en);
I have written a helper method that does all the stuff to extend the glass by the specified amount of pixels on each border of the window.
/// <summary>
/// Extends the glass area into the client area of the window
/// </summary>
/// <param name="window"></param>
/// <param name="top"></param>
public static void ExtendGlass(Window window, Thickness thikness)
{
try
{
int isGlassEnabled = 0;
DwmIsCompositionEnabled(ref isGlassEnabled);
if (Environment.OSVersion.Version.Major > 5 && isGlassEnabled > 0)
{
// Get the window handle
WindowInteropHelper helper = new WindowInteropHelper(window);
HwndSource mainWindowSrc = (HwndSource)HwndSource.
FromHwnd(helper.Handle);
mainWindowSrc.CompositionTarget.BackgroundColor =
Colors.Transparent;
// Get the dpi of the screen
System.Drawing.Graphics desktop =
System.Drawing.Graphics.FromHwnd(mainWindowSrc.Handle);
float dpiX = desktop.DpiX / 96;
float dpiY = desktop.DpiY / 96;
// Set Margins
MARGINS margins = new MARGINS();
margins.cxLeftWidth = (int)(thikness.Left * dpiX);
margins.cxRightWidth = (int)(thikness.Right * dpiX);
margins.cyBottomHeight = (int)(thikness.Bottom * dpiY);
margins.cyTopHeight = (int)(thikness.Top * dpiY);
window.Background = Brushes.Transparent;
int hr = DwmExtendFrameIntoClientArea(mainWindowSrc.Handle,
ref margins);
}
else
{
window.Background = SystemColors.WindowBrush;
}
}
catch (DllNotFoundException)
{
}
}
Next thing you need to do is calling the ExtendGlass
in the OnSourceInitialized
callback. Because that is the time the window handle has been created.
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
GlassHelper.ExtendGlass(this, LayoutRoot.Margin);
}
Since the user (or the system) can enable or disable the glass effect while the application is running, we need to hook up a callback in the WndProc
to undo or initialize the glass extension dynamically.
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == GlassHelper.WM_DWMCOMPOSITIONCHANGED)
{
GlassHelper.ExtendGlass(this, LayoutRoot.Margin);
handled = true;
}
return IntPtr.Zero;
}
Last modified: 2009-12-16 16:45:09
Copyright (c) by Christian Moser, 2011.
Comments on this article
|
Tim |
|
|
Commented on 16.July 2009 |
Is it just me, or does Expression Blend 3 (RC) completely ignore the fact that there's a System.Drawing namespace? I can't make the code work, and it's just this one single part that is holding me up. It says there's no "Drawing" namespace in the "System" namespace. (???) |
|
|
|
Tim |
|
|
Commented on 16.July 2009 |
PS -- I got it working by removing the DPI detection (a workaround, not a fix, of course), and then it started working like a charm. thanks so much for the helper class! Lovely how it pulls the margin right off of my LayoutRoot!! |
|
|
|
Christian Moser |
|
|
Commented on 16.July 2009 |
Hi Tim, The System.Drawing namespace is defined in a separate assembly called "System.Drawing". You probably forgot to add a reference to that assembly. In Blend it's a bit tricky to do this. But in Visual Studio just right-click of the "References" folder in your project and choose "Add Reference..." then select the tab ".NET" and find the "System.Drawing" entry.
I hope this helps.
Greetings Christian |
|
|
|
Tim |
|
|
Commented on 17.July 2009 |
Thanks-- I'm still a bit new to C#, and my experience with Java isn't helping much at times like this :) Thanks for the tip-- I will be sure to make use of it.
By the way, do you know if there's a way to render text with the Vista/Win7 blur behind it? That is, the effect used on the gadget list when you try to add to the sidebar/desktop, or in the titlebar of a window? I've read about this for doing straight-up C/++ application programming, but I don't know if there is a way to specify such things given the interface that Blend gives you. And it would be slightly backwards if I had to programatically render the text instead of using the xaml/gui editor.
If not, I can live with it :P (or fake the effect, either way!) Thanks muchly for sharing your expertise. |
|
|
|
Christian Moser |
|
|
Commented on 17.July 2009 |
Hi Tim,
check out this Expression Blend Behavior: http://gallery.expression.microsoft.com/en-us/GlassBehavior
Greetings Christian |
|
|
|
Tim |
|
|
Commented on 17.July 2009 |
Perfect. Thanks again. This is what I imagined it should be like :) |
|
|
|
Matt Wilkinson |
|
|
Commented on 5.August 2009 |
Reading over the MSDN documentation, it looks as if the whole client area will have the Aero Glass drawn under it if you set the margins to -1.
http://msdn.microsoft.com/en-us/library/aa969537%28VS.85%29.aspx#frameextend |
|
|
|
Ankit Malhotra |
|
|
Commented on 28.August 2009 |
Implementation in VB.NET:
Imports System.Drawing Imports System.Runtime.InteropServices Public Class Form1 <DllImport("dwmapi.dll", CharSet:=CharSet.Auto)> _ Public Shared Sub DwmExtendFrameIntoClientArea(ByVal hWnd As System.IntPtr, ByRef pMargins As Printing.Margins) End Sub
Private Glass_brush As SolidBrush = New SolidBrush(Color.Black) Private border_extensions As Printing.Margins = New Printing.Margins 'Public Structure Margins ' Public Left As Integer ' Public Right As Integer ' Public Top As Integer ' Public Bottom As Integer 'End Structure Public Sub New() InitializeComponent() border_extensions.Top = 300 'amount of border extension required from top border border_extensions.Left = 300 'amount of border extension required from left border border_extensions.Right = 300 'amount of border extension required from right border border_extensions.Bottom = 300 'amount of border extension required from the bottom border
DwmExtendFrameIntoClientArea(Me.Handle, border_extensions) End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) e.Graphics.FillRectangle(Glass_brush, New System.Drawing.Rectangle(0, 0, 300, 300)) '' painting the areas in client regions in black..if these fall in the margins described u would get '' glass effect! End Sub
End Class |
|
|
|
Chris Jacobs |
|
|
Commented on 20.October 2009 |
It seems overkill to do anything based just plainly on operating system such as vista. I work at a massive organisation and they wouldn't touch it. I havent used it so no comment, i like the glass effect. But seriously solutions should and need to be for XP and above, not just for vista. home users use vista organisations tend to stay away. And thats a fact. Why have a nice window that looks crap when majority of users use it. |
|
|
|
mitzaodx |
|
|
Commented on 24.December 2009 |
why does my buttons and images look weird? my pictures have no transparency and my buttons don't show like buttons... they look weird..?? |
|
|
|
mitzaodx |
|
|
Commented on 27.December 2009 |
Fixed the buttons. I changed the color from black to navy and used a pannel to get the aero feel. I just want to know is there a way that the icons displayed in a listview can be full scale? when i set it to 32x32 they look very bad, can it be that the icons look like vista desktop icons? with that kind of select effect? |
|
|
|
saman |
|
|
Commented on 27.December 2009 |
thank,s |
|
|
|
mitzaodx |
|
|
Commented on 28.December 2009 |
Fixed the buttons. I changed the color from black to navy and used a pannel to get the aero feel. I just want to know is there a way that the icons displayed in a listview can be full scale? when i set it to 32x32 they look very bad, can it be that the icons look like vista desktop icons? with that kind of select effect? |
|
|
|
Michał |
|
|
Commented on 6.January 2010 |
Hi, i have a problem with "complete" implementation of this. Could You please post a link to complete source code of GlassHelper and sample Window that is to make use of it ? Thanks in advance, Michal |
|
|
|
Tschaena |
|
|
Commented on 11.January 2010 |
Hi Christian. Worked almost as expected ;-) |
|
|
|
Eon |
|
|
Commented on 17.April 2010 |
Hi Christian, thanks for a great tutorial. It works beautifully but I'm curious, just how does this look on XP? Also, I was wondering if you know a technique to also extend the ability to drag a window by clicking on the title bar over the entire new glass region. Even though the glass looks seamless I am still only able to drag a window by the region up at the top. |
|
|
|
Bob |
|
|
Commented on 26.April 2010 |
Hey--do this work on Win 3.1? |
|
|
|
Jill |
|
|
Commented on 27.April 2010 |
yes, this do work on 3.1. I am currently getting it to work on DOS |
|
|
|
vcxv |
|
|
Commented on 27.May 2010 |
cxv |
|
|
|
Bill |
|
|
Commented on 8.July 2010 |
Any possible eay to achive this with XP |
|
|
|
Bob 2 |
|
|
Commented on 8.July 2010 |
Do this work with DOS 1.1 |
|
|
|
Bob 2 |
|
|
Commented on 8.July 2010 |
Oh.. Jill.. is doing it.. mad poor guy |
|
|
|
QWERT |
|
|
Commented on 27.October 2010 |
thats stolen from another site... faggot |
|
|
|
Simba |
|
|
Commented on 3.March 2011 |
Thanks. very nice paper |
|
|
|
Evans |
|
|
Commented on 10.May 2011 |
There are a couple of lines of code missing in your example to make it perfect.
First: public const int WM_DWMCOMPOSITIONCHANGED = 0x031E; needs to be declared in the GlassHelper class.
Secondly: In order for the WndProc procedure to run in WPF the following lines should be added to the windows load event since WPF windows don't have a hWnd
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); source.AddHook(new HwndSourceHook(WndProc));
Otherwise the example is a nice one. |
|