Introduction
As is known, the first noticeable thing when moving from Windows XP to Windows Vista or Windows 7 is the enhanced graphical user interface, and if your hardware is good enough to support the Windows Aero Theme, you'll get great looks for the window title bars (see the figure below):
But if this theme is not enabled due to lack of good enough hardware, or is manually disabled by the user, the window titles will appear like this:
And here, we are going to get some control of this feature (the Glass Effect of the window titles and frames).
Window Desktop Manager
In the old days of Windows XP and earlier, each window was responsible to draw directly to the screen or the primary display device, but starting from Windows Vista, this is no longer happening, and a new feature has been introduced. This feature, called the Window Desktop Manager, is a service responsible for Desktop Composition that changes the way applications display pixels on the screen, so all the drawing of the individual windows will be redirected into an off-screen surface in the video memory, then this surface rendered into a desktop image and displayed on the screen.
This feature of desktop compositing enables the developers of the Operating System to add many different visual effects to the desktop windows such as:
- 3D animations
- 3D flip of Desktop windows
- Glass Effect of window title bars and frames (the scope of this article)
- Interactive thumbnails on the Task Bar
- etc...
This new feature comes with a rich set of APIs that can be called to manage, control, and make use of the new possible visual effects available in these Operating Systems, and here we are going to use some of them (actually just two of the WDM API functions):
DwmIsCompositionEnabled()
DwmExtendFrameIntoClientArea()
Note: You can find the Windows Desktop Manager as a service (or process) called wdm.exe in the [Processes Tab] of the Windows Task Manager.
Windows Glass (Blur) Effect
The Glass or Blur Effect is an effect of the title bars and frame borders of the windows that display the items behind these title bars and frame borders in a semi-transparent way (see the Recycle Bin icon indicated by the arrow):
Actually, it is not just a transparency, but it displays the objects behind as blurry and just like what a glass does (and that is why it is known as the Glass Effect). This feature is cool and gives our windows a great look (I still remember the question of some friend when he saw a snapshot of a small program that I made previously "How did you do this effect of the title bar?" he said. And my answer was: "I did nothing! This is done by the Operating System itself and this is how the window titles look like on Windows Vista!") .... He was shocked just because he had not seen Windows Vista before. Anyhow, we are going here to add some control to this Glass Effect of the windows and make it look better and as we need.
Our Glass Effect Extender Library
What I'm going to introduce here is to add some flexibility to the Glass Effect and make it possible to:
- Extend the area of the glass effect to any side of the form (make it not just limited by the title bar and frames).
- Make it possible to show an image between the glass effect area and the other client area.
- Draw texts on the glass with a glow around, just like the OS does on the title bars.
These three features are shown in the figure below:
Now, to use this library in your application, just add a reference to the library file rtaGlassEffectsLib.dll which can be found in the attachments of this article.
And then, simply declare a variable of type rtaGlassEffect
as follows:
Dim glass As New rtaGlassEffectsLib.rtaGlassEffect
glass.TopBarSize = 100
glass.ShowEffect(Me)
Now, if you have a Label
called Label1
and you want to draw it with glowing text on a glass effect area of 60 pixels from the bottom of the form, just locate the Label
on the position that it should be displayed at and use the following code:
Dim glass As New rtaGlassEffectsLib.rtaGlassEffect
glass.BottomBarSize = 60
glass.ShowEffect(Me, Label1)
Again, if you have a Label
called Label1
and a PictureBox
that contains an image PictureBox1
that should be displayed on a form with a glass effect so that its width from the left side of the form is 120 pixels and from the top side is 60 pixels, just put the Label
and PictureBox
on the locations you want and use the following code:
Dim glass As New rtaGlassEffectsLib.rtaGlassEffect
glass.LeftBarSize = 120
glass.TopBarSize = 60
glass.ShowEffect(Me, Label1,PictureBox1)
Shared Methods of our Library
Our object rtaGlassEffect
contains some shared methods that can be used in addition to the already used non-shared method in previous examples: ShowEffect()
. The first shared method is DrawTextGlow()
, which can be used to draw a text with a glow on the passed Graphics
object on the specified location, and its code is as follows:
Public Shared Sub DrawTextGlow(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 SavedBitmap As IntPtr = IntPtr.Zero
Dim SavedFont As IntPtr = IntPtr.Zero
Dim MainHDC As IntPtr = Graphics.GetHdc
Dim MemHDC As IntPtr = APIs.CreateCompatibleDC(MainHDC)
Dim BtmInfo As New APIs.BITMAPINFO
Dim TextRect As New APIs.RECT(0, 0, bounds.Right - bounds.Left + 2 * 15, _
bounds.Bottom - bounds.Top + 2 * 15)
Dim ScreenRect As New APIs.RECT(bounds.Left - 15, bounds.Top - 15, _
bounds.Right + 15, bounds.Bottom + 15)
Dim hFont As IntPtr = fnt.ToHfont
BtmInfo.bmiHeader.biSize = Marshal.SizeOf(BtmInfo.bmiHeader)
With BtmInfo
.bmiHeader.biWidth = bounds.Width + 30
.bmiHeader.biHeight = -bounds.Height - 30
.bmiHeader.biPlanes = 1
.bmiHeader.biBitCount = 32
End With
Dim dibSection As IntPtr = _
APIs.CreateDIBSection(MainHDC, BtmInfo, 0, 0, IntPtr.Zero, 0)
SavedBitmap = APIs.SelectObject(MemHDC, dibSection)
SavedFont = APIs.SelectObject(MemHDC, hFont)
Dim TextOptions As APIs.S_DTTOPTS = New APIs.S_DTTOPTS
With TextOptions
.dwSize = Marshal.SizeOf(TextOptions)
.dwFlags = APIs.DTT_COMPOSITED Or _
APIs.DTT_GLOWSIZE Or APIs.DTT_TEXTCOLOR
.crText = ColorTranslator.ToWin32(Clr)
.iGlowSize = 18
End With
Dim Renderer As VisualStyleRenderer = New VisualStyleRenderer(_
System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active)
APIs.DrawThemeTextEx(Renderer.Handle, MemHDC, 0, 0, _
text, -1, flags, TextRect, TextOptions)
With ScreenRect
APIs.BitBlt(MainHDC, .Left, .Top, .Right - .Left, _
.Bottom - .Top, MemHDC, 0, 0, APIs.SRCCOPY)
End With
APIs.SelectObject(MemHDC, SavedFont)
APIs.SelectObject(MemHDC, SavedBitmap)
APIs.DeleteDC(MemHDC)
APIs.DeleteObject(hFont)
APIs.DeleteObject(dibSection)
Graphics.ReleaseHdc(MainHDC)
End Sub
As shown above, this method uses the API function DrawThemeTextEx()
to draw the glowing text to the off-screen surface and then copy it to the surface of the Graphics
object passed to the method using the Windows Vista Visual Style Renderer.
The second shared method of our library is SetGlassEffect()
that shows the glass effect on the form passed to the method with the required areas from the top, left, bottom, and right sides of the form:
Public Shared Function SetGlassEffect(ByVal Frm As Form,
Optional ByVal fromTop As Integer = 0,
Optional ByVal fromRight As Integer = 0,
Optional ByVal fromBottom As Integer = 0,
Optional ByVal fromLeft As Integer = 0) As Boolean
If rtaGlassEffect.GlassEnabled AndAlso Frm IsNot Nothing Then
Dim m As New APIs.MARGINS
m.Top = fromTop
m.Right = fromRight
m.Left = fromLeft
m.Bottom = fromBottom
APIs.DwmExtendFrameIntoClientArea(Frm.Handle, m)
Frm.Invalidate()
End If
End Function
From the code, you can see that this method uses a property called GlassEnabled
to check if the glass effect is supported and enabled on the Operating System before using the API function DwmExtendFrameIntoClientArea()
to extend the effect of glass to the client area of the form.
Properties of the Library
Our library contains several properties holding the options of our glass effect, and I'm going to explain two of them here. The first one is the GlassEnabled
that returns a boolean value indicating whether the Glass Effect is supported and enabled by the system or not. This property uses the API function DwmIsCompositionEnabled()
to check this feature of the system, as follows:
Public Shared ReadOnly Property GlassEnabled() As Boolean
Get
Dim VistaOrAbove As Boolean = (Environment.OSVersion.Version.Major >= 6)
If VistaOrAbove Then
Dim Enabled As Boolean
APIs.DwmIsCompositionEnabled(Enabled)
Return Enabled
Else
Return False
End If
End Get
End Property
Another property of our library is UseHandCursorOnTitle
, that can be used to determine if the hand cursor should be displayed or not while the mouse is over the top area of the glass effect to show that the form can be dragged by hand.
Events Added to the Library
As you may have noticed, the GlassEnabled
property is a great one for testing if this feature is enabled by your system and then start using our library and call ShowEffect()
function to show the glass effect, Now suppose the user has disabled/enabled the Aero Theme while your application was running !! This may cause a problem in the appearance of your application.
To solve this problem, two events were added to the library to keep track of the changes happening to the system while your application is running, these two events are GlassEffectEnabled
event and GlassEffectDisabled
.
Now for people who like to know how stuff works .. I can tell that these events were implemented by monitoring the messages coming from the system to the windows to our application looking for the WM_SYSCOLORCHANGE
message that indicates that the system colors have been changed. So by doing a quick compare between the state of the GlassEnabled
property before and after this message, we can decide whether this feature has been enabled or not.
For doing this, I make use of the NativeWindow
class for hooking the messages of the system as follows:
Public Class HookWindow
Inherits NativeWindow
Sub New()
Dim cp As New CreateParams()
Me.CreateHandle(cp)
End Sub
Public Event MessageArrived(ByVal sender As Object, ByVal e As EventArgs)
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = APIs.WM_SYSCOLORCHANGE Then
RaiseEvent MessageArrived(Me, New EventArgs)
End If
MyBase.WndProc(m)
End Sub
Protected Overrides Sub Finalize()
Me.DestroyHandle()
MyBase.Finalize()
End Sub
End Class
I hope this library will help you to give your applications a better look, so enjoy it!
History
- 5th August, 2009: Initial post
- 14th August, 2009: Two Events added to the library and changed article text