Click here to Skip to main content
16,007,779 members
Articles / Desktop Programming / Windows Forms
Article

Drawing smooth text and pictures on the extended glass area of your WinForm in Windows Vista

Rate me:
Please Sign up or sign in to vote.
4.71/5 (60 votes)
10 Jul 2008GPL34 min read 483.8K   7.1K   163   104
This article tells you how to draw text and pictures correctly on your Vista form's extended glass area.

Introduction

Windows Vista has a new aero theme that includes what we call the glass effect (powered by the Desktop Windows Manager). While the non-client area of all windows get glass for free, extending glass into the client area and drawing text or pictures on glass are things you have to work for, as a developer.

[This intro text is borrowed from Daniel Moth: Windows Vista: Get the glass effect in your C# applications <-You can go to this link and see more about Vista glass ^_^]

[Copyright Daniel Moth, Aug 22, 2006. All rights reserved.]

The demonstration is written in C#, and was tested on Windows Vista Beta 2.5472.

How to extend the frame into client area?

The Desktop Windows Manager (DWM) provides a group of APIs called DWM API, two of them can help us: DwmIsCompositionEnabled and DwmExtendFrameIntoClientArea.

DwmIsCompositionEnabled is used to check whether the glass is enabled by the user:

C#
[System.Runtime.InteropServices.DllImport("dwmapi.dll")]
public extern static int DwmIsCompositionEnabled(ref int en ) ;

And DwmExtendFrameIntoClientArea is used to extend the frame into your window's client area:

C#
[System.Runtime.InteropServices.DllImport("dwmapi.dll")]
public extern static int DwmExtendFrameIntoClientArea(IntPtr hwnd, 
                         ref MARGINS margin ) ;

The second parameter of DwmExtendFrameIntoClientArea is a structure MARGINS, which indicates how far into the client area the frame should be extended. If you want to render the entire client and non-client area as a seamless sheet of glass, just set any margin to -1:

C#
int en=0;
MARGINS mg=new MARGINS();
mg.m_Buttom = -1;
mg.m_Left = -1;
mg.m_Right = -1;
mg.m_Top = -1 ;
//make sure you are not on a legacy OS 
if (System.Environment.OSVersion.Version.Major >= 6)             
{
    DwmIsCompositionEnabled(ref en);
    //check if the desktop composition is enabled

    if(en>0)
    {
          DwmExtendFrameIntoClientArea(this.Handle, ref mg);

    }else{
          MessageBox.Show("Desktop Composition is Disabled!");

    }
}else{
     MessageBox.Show("Please run this on Windows Vista.");
}

If you run your app now, you can’t see any effect. Because the system will draw the client area with the window backcolor automatically. So we have to paint it with a solid black brush(it so happens that the bit pattern for RGB black (0x00000000) is the same as the bit pattern for the 100% transparent ARGB), or just set the TansparencyKey property into your form’s BackColor. Then run your app, there will be the desired effect.

Sample Image - textonglass_6.png

Problems

But soon you will find some problems:

If there’s a Label on the glass area, or you draw some text using a Graphics object, you’ll see the text smoothing works “ugly”. Since it uses the form’s background to determine the color it should smooth against --- if you set the TansparncyKey,it’s the tansparncy key color, if you paint the area with a black brush, it is black, and, even worse, all the black area(usually the control’s text) is also transparent:

Sample screenshot

Sample screenshot

Solution

The solution to these problem is: do not use any Label, and draw the text by yourself. But don’t draw your text directly on the glass using the Graphics.DrawString() method for the ugly text smoothing. You have to “draw” your text into a GraphicsPath object first, then release the path to the glass surface by using the Graphics.FillPath method:

C#
Graphics g = this.CreateGraphics();
GraphicsPath blackfont = new GraphicsPath();
SolidBrush brsh = new SolidBrush(Color.White);

blackfont.AddString("Hello Vista", 
    new FontFamily("Tahoma", (int)FontStyle.Regular, 26, 
    new Point(0, 0), StringFormat.GenericDefault);

//SmoothingMode must be set, or text smoothing will not work

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality  ;   
g.FillPath(brsh, blackfont);    //Fill the font with White brush

Here is the effect:

Sample screenshot

Or drawing some “glow” behind the text is also OK, the only thing to remember is to do this with the GraphicsPath object:

C#
Graphics g = this.CreateGraphics();

Rectangle r = new Rectangle(pictureBox1.Left, pictureBox1.Top, 
              pictureBox1.Width, pictureBox1.Height);

GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();

GraphicsPath fontpath = new GraphicsPath();

path.AddEllipse(r);

fontpath.AddString("Hello Vista", new FontFamily("Tahoma"), 
    (int)FontStyle.Regular, 26,pictureBox1.Location , 
     StringFormat.GenericDefault );
PathGradientBrush pgb = new System.Drawing.Drawing2D.PathGradientBrush(path);

Color[] c ={ (Color.Transparent) };

//SmoothingMode must be set, or text smoothing will not work
 
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality  ;  
pgb.CenterColor = Color.FromArgb(255, Color.White);

pgb.SurroundColors = c;

g.FillEllipse(pgb, r);
g.FillPath(Brushes.Black  , fontpath);

Sample screenshot

And drawing a picture with the Alpha channel on the glass area is quite easy, if you make the form into glass by painting it black (that’s also why the demo project did not use the transparency key method to extend the glass). Just call the Graphics.DrawImage() method and it will work correctly.

Sample screenshot

Some Tips

By the way, don’t extend the glass area by using the transparency key method. Because this method has another problem – Click Transparent: When you click on the glass area, you just click something at the back of your window.

Using DrawThemeTextEx

The method above is simple, but not the standard way Windows Vista uses to draw text. Windows Vista provides an API DrawThemeTextEx for us to darw the glowing text on the Aero glass. It's a little complex to use this API.

First, we must create a memory DC by using the P/Invoke API CreateCompatibleDC. Second, you need to create a 32-bit bitmap by using the API CreateDIBSection. Third, select both the bitmap and the font into the memory DC you created before, and draw the text in the memory DC using DrawThemeTextEx. Fourth, draw the text to screen by using BitBlt. Fifth....there's no fifth now, that's all.

Sample screenshot

To see more details, please visit Vista Goodies in C++: Using Glass in Your UI or Aero Glass from Managed Code, or see the GlassText.cs in the code package.

History

  • 08.29.2006 -- Initial post
  • 07.10.2008 -- Source updated

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
China China
Pang is experienced in developing IM system, distributed backend caching & storage system for large scale internet services. He also has a broad interest in playing around all kinds of programming stuff, like hacking, UI programming. He's current project is MSNPSharp, a C# implementation of MSNP API library which allows you to develop your own MSN client and bot. Please see http://code.google.com/p/msnp-sharp/

Comments and Discussions

 
GeneralOn maximise Pin
Chris_McGrath15-Aug-07 19:37
Chris_McGrath15-Aug-07 19:37 
GeneralRe: On maximise Pin
Pang Wu16-Aug-07 17:22
Pang Wu16-Aug-07 17:22 
GeneralGRACIAS .. Pin
Mariah L10-Jun-07 23:55
Mariah L10-Jun-07 23:55 
Questionglass for wpf on xp sp2 Pin
nitstheone1-Apr-07 2:50
nitstheone1-Apr-07 2:50 
AnswerRe: glass for wpf on xp sp2 Pin
Pang Wu1-Apr-07 3:55
Pang Wu1-Apr-07 3:55 
QuestionDraw on the non-client area? Pin
giiiiif24-Mar-07 6:53
giiiiif24-Mar-07 6:53 
AnswerRe: Draw on the non-client area? Pin
Pang Wu29-Mar-07 17:56
Pang Wu29-Mar-07 17:56 
QuestionVB.net Version Pin
SveFro27-Feb-07 3:38
SveFro27-Feb-07 3:38 
I've converted the DrawGlowingText function from the c# version that was in "GlassText.cs" to Vb.net

Every thing seems to work but the text is drawn using japaneese signs, Not normal letters Blush | :O
Can anyone tell me whats wrong?

Here's The code:

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices

Public Class VistaAeroAPI
Public Declare Function DwmIsCompositionEnabled Lib "dwmapi.dll" (ByRef en As Integer) As Integer
Public Declare Function DwmExtendFrameIntoClientArea Lib "dwmapi.dll" (ByVal hWnd As IntPtr, ByRef margins As MARGIN_STRUCT) As Integer

Public Declare Function DrawThemeTextEx Lib "UxTheme.dll" (ByVal hTheme As IntPtr, ByVal hdc As IntPtr, ByVal iPartId As Integer, ByVal iStateId As Integer, ByVal text As String, ByVal iCharCount As Integer, ByVal dwFlags As Integer, ByRef pRect As RECT, ByRef pOptions As S_DTTOPTS) As Integer

Public Declare Function CreateDIBSection Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal pbmi As BITMAPINFO, ByVal iUsage As Int32, ByVal ppvBits As Integer, ByVal hSection As IntPtr, ByVal dwOffset As Int32) As IntPtr
Public Declare Function BitBlt Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal nXDest As Integer, ByVal nYDest As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc As Integer, ByVal nYSrc As Integer, ByVal dwRop As Int32) As Boolean

Public Declare Function SelectObject Lib "gdi32.dll" (ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr
Public Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As IntPtr) As Boolean
Public Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hDC As IntPtr) As IntPtr
Public Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As Boolean
<structlayout(layoutkind.sequential)> _
Public Structure RECT
Public Sub New(ByVal tLeft As Integer, ByVal tTop As Integer, ByVal tRight As Integer, ByVal tBottom As Integer)
Left = tLeft
Top = tTop
Right = tRight
Bottom = tBottom
End Sub
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
<runtime.interopservices.structlayout(runtime.interopservices.layoutkind.sequential)> _
Public Class BITMAPINFO
Public biSize As Integer
Public biWidth As Integer
Public biHeight As Integer
Public biPlanes As Short
Public biBitCount As Short
Public biCompression As Integer
Public biSizeImage As Integer
Public biXPelsPerMeter As Integer
Public biYPelsPerMeter As Integer
Public biClrUsed As Integer
Public biClrImportant As Integer
Public bmiColors_rgbBlue As Byte
Public bmiColors_rgbGreen As Byte
Public bmiColors_rgbRed As Byte
Public bmiColors_rgbReserved As Byte
End Class
<runtime.interopservices.structlayout(runtime.interopservices.layoutkind.sequential)> _
Public Structure S_DTTOPTS
Dim dwSize As Integer
Dim dwFlags As Integer
Dim crText As Integer
Dim crBorder As Integer
Dim crShadow As Integer
Dim iTextShadowType As Integer
Dim ptShadowOffset As POINT
Dim iBorderSize As Integer
Dim iFontPropId As Integer
Dim iColorPropId As Integer
Dim iStateId As Integer
Dim fApplyOverlay As Boolean
Dim iGlowSize As Integer
Dim pfnDrawTextCallback As Integer
Dim lParam As IntPtr
End Structure

Private Const DTT_COMPOSITED As Integer = 8192
Private Const DTT_GLOWSIZE As Integer = 2048
Private Const DTT_TEXTCOLOR As Integer = 1

<runtime.interopservices.structlayout(runtime.interopservices.layoutkind.sequential)> _
Public Structure POINT
Dim cx, cy As Integer
Sub New(ByVal X As Integer, ByVal Y As Integer)
cx = X
cy = Y
End Sub
End Structure

<runtime.interopservices.structlayout(runtime.interopservices.layoutkind.sequential)> _
Public Structure MARGIN_STRUCT
Dim cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight As Integer

Sub New(ByVal Left As Integer, ByVal Right As Integer, ByVal Top As Integer, ByVal Bottom As Integer)
cxLeftWidth = Left
cxRightWidth = Right
cyTopHeight = Top
cyBottomHeight = Bottom
End Sub
End Structure

Public Shared Sub DrawGlowingText(ByVal Graphics As Graphics, ByVal text As String, ByVal fnt As Font, ByVal bounds As Rectangle, ByVal Clr As Color, ByVal flags As TextFormatFlags)
Dim primaryHdc As IntPtr = Graphics.GetHdc

'


'' Create a memory DC so we can work offscreen
Dim memoryHdc As IntPtr = CreateCompatibleDC(primaryHdc)


'' Create a device-independent bitmap and select it into our DC
Dim info As BITMAPINFO = New BITMAPINFO
info.biSize = Marshal.SizeOf(info)
info.biWidth = bounds.Width
info.biHeight = -bounds.Height
info.biPlanes = 1
info.biBitCount = 32
info.biCompression = 0 '' BI_RGB
Dim dib As IntPtr = CreateDIBSection(primaryHdc, info, 0, 0, IntPtr.Zero, 0)

SelectObject(memoryHdc, dib)


' Create and select font
Dim fontHandle As IntPtr = fnt.ToHfont
SelectObject(memoryHdc, fontHandle)


'' Draw glowing text
Dim renderer As System.Windows.Forms.VisualStyles.VisualStyleRenderer = New System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active)

Dim dttOpts As S_DTTOPTS = New S_DTTOPTS

dttOpts.dwSize = Marshal.SizeOf(GetType(S_DTTOPTS))
dttOpts.dwFlags = DTT_COMPOSITED Or DTT_GLOWSIZE Or DTT_TEXTCOLOR
dttOpts.crText = ColorTranslator.ToWin32(Clr)
dttOpts.iGlowSize = 15 '' This is about the size Microsoft Word 2007 uses(15)
Dim textBounds As RECT = New RECT(0, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top)
DrawThemeTextEx(renderer.Handle, memoryHdc, 0, 0, text, -1, flags, textBounds, dttOpts)
'DrawThemeTextEx(renderer.Handle, memoryHdc, 0, 0, text, -1, (int)flags, ref textBounds, ref dttOpts);

'' Copy to foreground
Dim SRCCOPY As Integer = 13369376 ' old C# Value was: 0x00CC0020

BitBlt(primaryHdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, SRCCOPY)


'' Clean up
DeleteObject(fontHandle)
DeleteObject(dib)
DeleteDC(memoryHdc)

Graphics.ReleaseHdc(primaryHdc)
End Sub

End Class
AnswerRe: VB.net Version Pin
Pang Wu27-Feb-07 20:01
Pang Wu27-Feb-07 20:01 
GeneralRe: VB.net Version Pin
SveFro28-Feb-07 0:04
SveFro28-Feb-07 0:04 
GeneralRe: VB.net Version Pin
Blizo27-Mar-07 16:14
Blizo27-Mar-07 16:14 
GeneralRe: VB.net Version [modified] Pin
SveFro28-Mar-07 0:17
SveFro28-Mar-07 0:17 
GeneralRe: VB.net Version Pin
Blizo28-Mar-07 20:12
Blizo28-Mar-07 20:12 
QuestionRe: VB.net Version Pin
sajee1235-Jan-09 13:39
sajee1235-Jan-09 13:39 
GeneralVery interesting Pin
Dr.Luiji21-Feb-07 21:49
professionalDr.Luiji21-Feb-07 21:49 
GeneralRe: Very interesting Pin
Pang Wu23-Feb-07 16:24
Pang Wu23-Feb-07 16:24 
GeneralWonder how IE in VIsta does it... Pin
Rocky Moore24-Oct-06 5:50
Rocky Moore24-Oct-06 5:50 
GeneralCorrect Method to draw Glowing Text Pin
Tim Dawson24-Oct-06 4:56
Tim Dawson24-Oct-06 4:56 
GeneralRe: Correct Method to draw Glowing Text Pin
Pang Wu24-Oct-06 9:02
Pang Wu24-Oct-06 9:02 
GeneralRe: Correct Method to draw Glowing Text Pin
Pang Wu24-Oct-06 23:39
Pang Wu24-Oct-06 23:39 
GeneralRe: Correct Method to draw Glowing Text Pin
krs4444asdf15-Jan-09 15:11
krs4444asdf15-Jan-09 15:11 
GeneralRe: Correct Method to draw Glowing Text Pin
Pang Wu15-Jan-09 16:54
Pang Wu15-Jan-09 16:54 
GeneralRe: Correct Method to draw Glowing Text Pin
krs4444asdf16-Jan-09 1:58
krs4444asdf16-Jan-09 1:58 
GeneralRe: Correct Method to draw Glowing Text Pin
Pang Wu16-Jan-09 2:41
Pang Wu16-Jan-09 2:41 
GeneralRe: Correct Method to draw Glowing Text Pin
krs4444asdf16-Jan-09 15:31
krs4444asdf16-Jan-09 15:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.