65.9K
CodeProject 正在变化。 阅读更多。
Home

您的应用程序的 Glass Effect 扩展器库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (70投票s)

2009年8月5日

CPOL

7分钟阅读

viewsIcon

228833

downloadIcon

19794

这个库可以让你控制 Windows Vista 和 Windows 7 的玻璃效果。

引言

众所周知,从 Windows XP 转向 Windows Vista 或 Windows 7 时,最引人注目的是增强的图形用户界面,如果您的硬件足够好以支持 Windows Aero 主题,您将获得非常漂亮的窗口标题栏(参见下图)

但如果由于硬件不足而未启用此主题,或者用户手动禁用此主题,则窗口标题将显示如下

在这里,我们将对这个功能(窗口标题和框架的玻璃效果)进行一些控制。

窗口桌面管理器

在 Windows XP 及更早版本时代,每个窗口都负责直接绘制到屏幕或主显示设备,但从 Windows Vista 开始,这种情况不再发生,并且引入了一项新功能。这项名为“窗口桌面管理器”的功能是一项负责桌面合成的服务,它改变了应用程序在屏幕上显示像素的方式,因此所有单个窗口的绘图都将重定向到视频内存中的离屏表面,然后此表面被渲染成桌面图像并显示在屏幕上。

桌面合成的这项功能使操作系统开发人员能够为桌面窗口添加许多不同的视觉效果,例如

  1. 3D 动画
  2. 桌面窗口的 3D 翻转
  3. 窗口标题栏和框架的玻璃效果本文的范围
  4. 任务栏上的交互式缩略图
  5. 等等...

这项新功能附带了一组丰富的 API,可以调用这些 API 来管理、控制和利用这些操作系统中可用的新视觉效果,在这里我们将使用其中的一些(实际上只是两个 WDM API 函数)

  • DwmIsCompositionEnabled()
  • DwmExtendFrameIntoClientArea()

注意:您可以在Windows 任务管理器的[进程选项卡]中找到名为wdm.exe的 Windows 桌面管理器服务(或进程)。

Windows 玻璃(模糊)效果

玻璃或模糊效果是窗口标题栏和框架边框的一种效果,它以半透明方式显示这些标题栏和框架边框后面的项目(参见箭头指示的回收站图标)

实际上,它不仅仅是透明,它还模糊地显示后面的物体,就像玻璃一样(这就是为什么它被称为玻璃效果)。这个功能很酷,让我们的窗口看起来很棒(我仍然记得一位朋友看到我之前做的一个小程序的快照时问我的问题:“你这个标题栏的效果是怎么做出来的?”他说。我的回答是:“我什么也没做!这是操作系统自己完成的,Windows Vista 上的窗口标题就是这样!”)。。。他之所以震惊,只是因为他以前没见过 Windows Vista。无论如何,我们在这里将对窗口的这种玻璃效果进行一些控制,使其看起来更好,并按我们所需的方式。

我们的玻璃效果扩展库

我这里将介绍的是为玻璃效果增加一些灵活性,使其能够

  1. 将玻璃效果的区域扩展到窗体的任何一侧(使其不限于标题栏和框架)。
  2. 使其能够在玻璃效果区域和客户端区域之间显示图像。
  3. 在玻璃上绘制带有发光效果的文本,就像操作系统在标题栏上做的那样。

这三个功能如下图所示

现在,要在您的应用程序中使用此库,只需添加对库文件rtaGlassEffectsLib.dll的引用,该文件可在本文的附件中找到。

然后,简单地声明一个rtaGlassEffect类型的变量,如下所示

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

现在,如果您有一个名为Label1Label,并且您想在窗体底部 60 像素的玻璃效果区域上用发光文本绘制它,只需将Label放置在其应显示的位置,然后使用以下代码

Dim glass As New rtaGlassEffectsLib.rtaGlassEffect

glass.BottomBarSize = 60
glass.ShowEffect(Me, Label1)

同样,如果您有一个名为Label1Label和一个包含图像PictureBox1PictureBox,该图像应在具有玻璃效果的窗体上显示,使其从窗体左侧的宽度为 120 像素,从顶部的高度为 60 像素,只需将LabelPictureBox放置在您想要的位置,然后使用以下代码

Dim glass As New rtaGlassEffectsLib.rtaGlassEffect

glass.LeftBarSize = 120
glass.TopBarSize = 60
glass.ShowEffect(Me, Label1,PictureBox1)

我们库的共享方法

我们的对象rtaGlassEffect包含一些共享方法,除了前面示例中已使用的非共享方法ShowEffect()之外,还可以使用这些方法。第一个共享方法是DrawTextGlow(),它可用于在传递的Graphics对象上指定位置绘制带有发光效果的文本,其代码如下所示

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

如上所示,此方法使用 API 函数DrawThemeTextEx()将发光文本绘制到离屏表面,然后使用 Windows Vista 视觉样式渲染器将其复制到传递给方法的Graphics对象的表面。

我们库的第二个共享方法是SetGlassEffect(),它在传递给方法的窗体上显示玻璃效果,并根据窗体的顶部、左侧、底部和右侧设置所需的区域

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

从代码中可以看出,此方法使用一个名为GlassEnabled的属性来检查操作系统是否支持并启用了玻璃效果,然后才使用 API 函数DwmExtendFrameIntoClientArea()将玻璃效果扩展到窗体的客户端区域。

库的属性

我们的库包含几个属性,用于保存我们玻璃效果的选项,我将在这里解释其中的两个。第一个是GlassEnabled,它返回一个布尔值,指示系统是否支持并启用了玻璃效果。此属性使用 API 函数DwmIsCompositionEnabled()来检查系统的此功能,如下所示

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

我们库的另一个属性是UseHandCursorOnTitle,它可用于确定当鼠标悬停在玻璃效果的顶部区域时是否应显示手形光标,以表明窗体可以手动拖动。

添加到库中的事件

正如您可能已经注意到的,GlassEnabled属性是一个很棒的属性,用于测试系统是否启用了此功能,然后开始使用我们的库并调用ShowEffect()函数来显示玻璃效果。现在假设用户在您的应用程序运行时禁用/启用了 Aero 主题!这可能会导致您的应用程序出现问题。

为了解决这个问题,库中添加了两个事件,以跟踪在您的应用程序运行时系统发生的变化,这两个事件是GlassEffectEnabled事件和GlassEffectDisabled事件。

现在,对于那些喜欢了解事物运作方式的人……我可以告诉您,这些事件是通过监视从系统到我们应用程序窗口的消息来实现的,寻找指示系统颜色已更改的WM_SYSCOLORCHANGE消息。因此,通过快速比较此消息前后GlassEnabled属性的状态,我们可以决定此功能是否已启用。

为此,我利用NativeWindow类来挂钩系统消息,如下所示

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 

希望这个库能帮助您的应用程序拥有更好的外观,尽情享受吧!

历史

  • 2009年8月5日:首次发布
  • 2009年8月14日:库中添加了两个事件,并更改了文章文本
© . All rights reserved.