Vista: Glass in C#

Sat, June 17, 2006, 05:32 AM under Windows | Vista
We looked at glass in Vista previously so please read that to make sure we are on the same page and using the same terminology... I'll wait...

Great, now that you are back, let's see how to get glass in our C# applications. For starters, if glass is supported and enabled on the user's PC, then your non-client area will by default get the glass effect. In other words, the titlebar and borders will have glass (nothing more for you to do). The question is how to extend that into the client area like other OS applications do (as we saw last time).

There are 3+1 things we need to do:
1. Check that it is supported before proceeding further.
2. Extend the frame into the client area.
3. Paint the extended area black.

Optionally,
4. Allow the user to drag our window by pressing in the glass area as if they were pressing on the window's caption bar.

So let's go!

1. First, let's make sure we are not on a legacy OS:
    if (Environment.OSVersion.Version.Major < 6)
{
Debug.WriteLine("How about trying this on Vista?");
return false;
}
After that, we need to check that glass is enabled by using the DwmIsCompositionEnabled API which resides in Desktop Window Manager (dwmapi.dll)
    // [DllImport("dwmapi.dll")]static extern void DwmIsCompositionEnabled(ref bool pfEnabled);
bool isGlassSupported = false;
DwmIsCompositionEnabled(ref isGlassSupported);
return isGlassSupported;
2. Extending the frame requires setting up a margins structure and passing it to another DWM API: DwmExtendFrameIntoClientArea.

So if we want to extend a bit from the top of the window(i.e. we don't care about extending from the bottom, left or right sides) we can use the following code:
    // [DllImport("dwmapi.dll")]static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMargins);
Margins marg;
marg.Top = 20; // extend 20 pixels from the top
marg.Left = 0;
marg.Right = 0;
marg.Bottom = 0;
DwmExtendFrameIntoClientArea(this.Handle, ref marg);
3. If you run your winforms app now, you'll find a black strip at the top. This is where we need the alpha blending and using a black brush does just that (tricking GDI that has no notion of an alpha channel).

So, in your form's paint event handler add the following code:
    SolidBrush blackBrush = new SolidBrush(Color.Black);
g.FillRectangle(blackBrush, 0, 0, this.ClientSize.Width, marg.Top);
4. Moving the form programmatically can easily be achieved by catching the mouse down/up/move events and some simple logic but instead we can use the following magic:
// make windows do the work for us by lieing to it about where the user clicked
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);

if (m.Msg == 0x84 // if this is a click
&& m.Result.ToInt32() == 1 // ...and it is on the client
&& this.IsOnGlass(m.LParam.ToInt32())) // ...and specifically in the glass area
{
m.Result = new IntPtr(2); // lie and say they clicked on the title bar
}
}

private bool IsOnGlass(int lParam)
{
// get screen coordinates
int x = (lParam << 16) >> 16; // lo order word
int y = lParam >> 16; // hi order word

// translate screen coordinates to client area
Point p = this.PointToClient(new Point(x, y));

// work out if point clicked is on glass
if (y < marg.Top)
return true;

return false;
}
5. There is no 5 :-) Just as a gotcha, if you decide to extend the client frame after the form has been displayed (and hence its handle has been created), you should call
this.RecreateHandle();

Note that there are other simpler approaches that use winforms Transparency but they seem to suffer from a very undesirable effect: When the user clicks on the glass area they are actually clicking on whatever is behind the form (true transparency is not what we want here).

Download a complete sample here.
Friday, February 29, 2008 5:51:00 PM (Pacific Standard Time, UTC-08:00)
Hi Daniel!

I am using your method to accomplish the glass effect in my windows but i have a probolem: as soon as i choose the basic style the inside of the window turn black while te explorer windows etc. stay having a background according to the frame.
Do you ba any chance know how to make it look like the explorer windows?

thanks for the reply.
Ewodos
Friday, February 29, 2008 7:24:41 PM (Pacific Standard Time, UTC-08:00)
ewodos: You need to handle that yourself. Only extend the margins and paint black if DwmIsCompositionEnabled. If the user changes the theme on the fly, then you must hook into the windows message WM_DWMCOMPOSITIONCHANGED.

For any further issues, please use the Vista UI forums.
Saturday, November 08, 2008 7:04:00 PM (Pacific Standard Time, UTC-08:00)
Thx!

This is the only place I have found that taught me how to do this. I looked for about 3 weeks!
Tuesday, November 25, 2008 5:06:00 AM (Pacific Standard Time, UTC-08:00)
Hi Daniel.
I'm trying to put this efect inside a panel, unsuccesfully... Is there any rules for glass to be applied to a form? Like, only across the whole width? Must I draw the panel on glass, instead of "glassing" the background of it?
Thx
Thursday, November 27, 2008 6:30:00 AM (Pacific Standard Time, UTC-08:00)
Hi Daniel,

I have used your glass tutorial to create a glass form where the whole client area is filled with the glass effect. the problem that i am experiencing is the text on controls is also drawn with the glass effect. is there any way i can get around this? i have tried changing the fore colour of the controls to something other than black but this seems to have no effect.

Awesome whats new for the web developer in visual studio 2008 conference by the way
WraithNath
Sunday, November 30, 2008 7:32:16 PM (Pacific Standard Time, UTC-08:00)
Lucas: Glad you found this resource useful.

Marcelo: Sorry, I really don't know – please try the Vista UI forums: http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=117&SiteID=1

WraithNath: Great that you enjoyed the conference session. Please see if any of the many links from this blog post help (http://www.danielmoth.com/Blog/2006/10/vb6-does-glass-on-vista.html) and if not please use the Vista UI forums.
Monday, January 05, 2009 3:59:00 PM (Pacific Standard Time, UTC-08:00)
Hi! Friend, do you how we can get this glass on VB.net please...
Thank you in advance.
Thursday, February 05, 2009 1:01:00 PM (Pacific Standard Time, UTC-08:00)
The comparison in IsOnGlass()
if (y < marg.Top)
should be
if (y > marg.Top)

Otherwise the window won't move if the click occurs out of the title bar.
Storm
Sunday, February 15, 2009 11:45:00 PM (Pacific Standard Time, UTC-08:00)
>> Anonymous Sajee said...
>> Hi! Friend, do you how we can
>> get this glass on VB.net please...

You might want to try VistaBridge: http://code.msdn.microsoft.com/vistabridge
Anonymous
Monday, June 15, 2009 3:03:34 AM (Pacific Daylight Time, UTC-07:00)
I have a problem, I made a simple class GlassForm. Then when I create a form of type GlassForm, It all works real nice except;
All my controls are drawn under the glass effect, causing every control; buttons, lable, textboxes ect. ect. To look "Strange..."
I don't want that, I want my glass applied to the form only and not the controls. Any suggestions?
I use this source for my GlassForm, use it if you like:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace CustomForms
{
public partial class FormGlass : Form
{
#region constant
const string DW_MAPI_DLL = "dwmapi.dll";
#endregion
#region public

#region pubnlic structs
public struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
#endregion

#region constructor
public FormGlass()
{
fMARGINS.cxLeftWidth = 0;
fMARGINS.cxRightWidth = 0;
fMARGINS.cyBottomHeight = 0;
fMARGINS.cyTopHeight = 0;
if (System.Environment.OSVersion.Version.Major >= 6) //make sure that you using Windows Vista
{
DwmIsCompositionEnabled(ref IntegerGlassEnabled); //check if desktop compositon is enabled
}
SetMargins();
}
#endregion

#region properties
public int cxLeftWidth
{
get
{
return fMARGINS.cxLeftWidth;
}
set
{
fMARGINS.cxLeftWidth = value;
SetMargins();
}
}

public int cxRightWidth
{
get
{
return fMARGINS.cxRightWidth;
}
set
{
fMARGINS.cxRightWidth = value;
SetMargins();
}
}

public int cyTopHeight
{
get
{
return fMARGINS.cyTopHeight;
}
set
{
fMARGINS.cyTopHeight = value;
SetMargins();
}
}

public int cyBottomHeight
{
get
{
return fMARGINS.cyBottomHeight;
}
set
{
fMARGINS.cyBottomHeight = value;
SetMargins();
}
}
#endregion

#endregion

#region private

#region private DLL import
[System.Runtime.InteropServices.DllImport(DW_MAPI_DLL)]
private extern static int DwmIsCompositionEnabled(ref int en);
[System.Runtime.InteropServices.DllImport(DW_MAPI_DLL)]
private extern static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margin);
#endregion

#region private variables
private MARGINS fMARGINS = new MARGINS();
private int IntegerGlassEnabled = 0;

#endregion

#region private functions
private void SetMargins()
{
if (IntegerGlassEnabled > 0) //No messages, From has glas, or not.
{
this.BackColor = Color.Black;
DwmExtendFrameIntoClientArea(this.Handle, ref fMARGINS);
}
}
#endregion
#endregion

}
}
Uncle X
Saturday, August 15, 2009 7:39:53 AM (Pacific Daylight Time, UTC-07:00)
@Uncle X: Try setting UseCompatibleTextRendering for the control to true;
Anonymous
Thursday, November 05, 2009 1:29:48 PM (Pacific Standard Time, UTC-08:00)
If the controls look strange set UseCompatibleTextRendering to true.
Now when you use transparent as a BackColor for a control it will turn to black simply set the back color to control and use control as the TransparencyKey for the form you will notice that the control is in glass but the rest of the form is black this can be fixed by gathering all of the controls into a panel with BackColor set to control and Dock set to fill.
Hesham A. Meneisi
Thursday, January 28, 2010 12:51:40 PM (Pacific Standard Time, UTC-08:00)
Daniel, is there a way to combine this with the VisualStyleRenderer? I would like to preview a msstyle file, but I can only preview how a window (composed with the VisualStyleRender elements) would look in Aero basic. I can't get that glass overlay.

I've also posted my question on StackOverflow some time ago, but no-one seems to know the answer.
http://stackoverflow.com/questions/1231643/visualstylerenderer-and-aero

Do you have a clue? Thnx.
Comments are closed.