• Download demo project - 99.75 KB
  • Download source - 132.83 KB
Glass Effect Extender Library for your Applications_第1张图片

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):

Glass Effect Extender Library for your Applications_第2张图片

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:

Glass Effect Extender Library for your Applications_第3张图片

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:

  1. 3D animations
  2. 3D flip of Desktop windows
  3. Glass Effect of window title bars and frames (the scope of this article)
  4. Interactive thumbnails on the Task Bar
  5. 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):

Glass Effect Extender Library for your Applications_第4张图片

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:

  1. Extend the area of the glass effect to any side of the form (make it not just limited by the title bar and frames).
  2. Make it possible to show an image between the glass effect area and the other client area.
  3. 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:

Glass Effect Extender Library for your Applications_第5张图片

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:

' Declare a variable of our effect that can be used later.
Dim glass As New rtaGlassEffectsLib.rtaGlassEffect

' Set the size of glass effect from the top to 100 pixels.
glass.TopBarSize = 100

' Show the effect on the form.
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)
                                    
    ' Variables used later.
    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

    ' Memory bitmap to hold the drawn glowed text.
    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

    ' Create a DIB Section for this bitmap from the graphics object.
    Dim dibSection As IntPtr = _
        APIs.CreateDIBSection(MainHDC, BtmInfo, 0, 0, IntPtr.Zero, 0)

    ' Save the current handles temporarily.
    SavedBitmap = APIs.SelectObject(MemHDC, dibSection)
    SavedFont = APIs.SelectObject(MemHDC, hFont)

    ' Holds the properties of the text (size and color , ...etc).
    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

    ' Draw The text on the memory surface.
    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)

    ' Reflecting the image on the primary surface of the graphics object.
    With ScreenRect
        APIs.BitBlt(MainHDC, .Left, .Top, .Right - .Left, _
                    .Bottom - .Top, MemHDC, 0, 0, APIs.SRCCOPY)
    End With

    ' Resources Cleaning.
    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