[原创][VB.NET] 用LayeredWindow制作PNG透明窗体心得

来源:互联网 发布:福州极客网络 编辑:程序博客网 时间:2024/05/01 19:39

[原创][VB.NET] 关于用GDI32.dll采用UpdateLayeredWindow制作PNG透明窗体的心得(HAVENT81/夜雨流星℡  From Www.Ai169.Com)

------------------------------------------------------------------------

前几天突然想制作一个窗体基类,所以去网上看了一下,发现了PNG透明窗体的源代码,代码的核心就是采用UpdateLayeredWindow的方法绘制窗体,测试了一下,界面非常爽,窗体的半透明效果感觉跟VistaGlass效果几乎一样。但是随之而来的是遇到了一个非常棘手的问题,这个方法存在一个超级大BUG,就是无法可视化所有的控件,无论放什么控件,都无法显示(但是控件确实存在,只是没有绘制而已,我用TEXTBOX测试能够获取内容文本,也有区域感知,就是屏幕上看没有任何东西)。基于这原因,本人上网找了资料,发现很多人也出现同样问题,后来研究一下System.Drawing.Graphics的功能,总算有了一点思路。由于原来的设计就是利用单幅图片进行窗体绘制,所以在这个思路上,即最终用已经实现的SetBitmap()方法来实现窗体绘制,但是在图片输出之前用Graphics类进行图片叠加处理,来实现窗体内控件内容的叠加操作(也许这就是窗体绘制的最原始的方法,只是微软已经将绘制封装到了控件中而已),下面是简单的在原来窗体图片上加绘了一些字符串和一张图片的方法:

Dim _Image As Bitmap = My.Resources.Vista                   '最终要呈现的图片Dim _Graphics As Graphics = Graphics.FromImage(_Image)      '建立一个图片绘制对象Dim _Brush As New SolidBrush(Color.Black)                   '用来处理文字的画笔

Dim _Format As New StringFormat                             '文本布局信息封装对象_Format.Alignment = StringAlignment.Center                  '垂直文本对齐方式_Format.LineAlignment = StringAlignment.Center              '水平文本对齐方式

_Graphics.TextRenderingHint = TextRenderingHint.AntiAlias   '设置高质量模式

'在原始图层上绘制字符串_Graphics.DrawString("HAVENT81/夜雨流星℡", New System.Drawing.Font(SystemFonts.CaptionFont.FontFamily, 120.0F, FontStyle.Bold), _Brush, CSng(((Me.Width / 2))), CSng((Me.Height / 2)), _Format)

'继续在图层之上绘制一张图片,图片来源为资源文件中的 VistaItem_Graphics.DrawImage(My.Resources.VistaItem, 10.0F, 20.0F)_Graphics.Dispose()

Me.SetBitmap(_Image, &HFF)                                  '利用前人总结的技术进行图片填充_Image.Dispose()_Format.Dispose()下面提供的是前人提供的SetBitmap的方法(这个方法要依托一个Win32的类,类代码在后面给出):

Public Sub SetBitmap(ByVal bitmap As Bitmap, ByVal opacity As Byte)            If (bitmap.PixelFormat <> PixelFormat.Format32bppArgb) Then                Throw New ApplicationException("The bitmap must be 32ppp with alpha-channel.")            End If            Dim dC As IntPtr = Win32.GetDC(IntPtr.Zero)            Dim hDC As IntPtr = Win32.CreateCompatibleDC(dC)            Dim zero As IntPtr = IntPtr.Zero            Dim hObject As IntPtr = IntPtr.Zero            Try                zero = bitmap.GetHbitmap(Color.FromArgb(0))                hObject = Win32.SelectObject(hDC, zero)                Dim psize As New Win32.Size(bitmap.Width, bitmap.Height)                Dim pprSrc As New Win32.Point(0, 0)                Dim pptDst As New Win32.Point(MyBase.Left, MyBase.Top)                Dim pblend As New Win32.BLENDFUNCTION                pblend.BlendOp = 0                pblend.BlendFlags = 0                pblend.SourceConstantAlpha = opacity                pblend.AlphaFormat = 1                Win32.UpdateLayeredWindow(MyBase.Handle, dC, (pptDst), (psize), hDC, (pprSrc), 0, (pblend), 2)            Finally                Win32.ReleaseDC(IntPtr.Zero, dC)                If (zero <> IntPtr.Zero) Then                    Win32.SelectObject(hDC, hObject)                    Win32.DeleteObject(zero)                End If                Win32.DeleteDC(hDC)            End Try        End Sub

接下来就是用于API交互的WIN32类:

 Friend Class Win32        ' Methods        <DllImport("gdi32.dll", SetLastError:=True, ExactSpelling:=True)> _        Public Shared Function CreateCompatibleDC(ByVal hDC As IntPtr) As IntPtr        End Function

        <DllImport("gdi32.dll", SetLastError:=True, ExactSpelling:=True)> _        Public Shared Function DeleteDC(ByVal hdc As IntPtr) As Bool        End Function

        <DllImport("gdi32.dll", SetLastError:=True, ExactSpelling:=True)> _        Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Bool        End Function

        <DllImport("user32.dll", SetLastError:=True, ExactSpelling:=True)> _        Public Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr        End Function

        <DllImport("user32.dll", ExactSpelling:=True)> _        Public Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Integer        End Function

        <DllImport("gdi32.dll", ExactSpelling:=True)> _        Public Shared Function SelectObject(ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr        End Function

        <DllImport("user32.dll", SetLastError:=True, ExactSpelling:=True)> _        Public Shared Function UpdateLayeredWindow(ByVal hwnd As IntPtr, ByVal hdcDst As IntPtr, ByRef pptDst As Point, ByRef psize As Size, ByVal hdcSrc As IntPtr, ByRef pprSrc As Point, ByVal crKey As Integer, ByRef pblend As BLENDFUNCTION, ByVal dwFlags As Integer) As Bool        End Function

        ' Fields        Public Const AC_SRC_ALPHA As Byte = 1        Public Const AC_SRC_OVER As Byte = 0        Public Const ULW_ALPHA As Integer = 2        Public Const ULW_COLORKEY As Integer = 1        Public Const ULW_OPAQUE As Integer = 4

        ' Nested Types        <StructLayout(LayoutKind.Sequential, Pack:=1)> _        Private Structure ARGB            Public Blue As Byte            Public Green As Byte            Public Red As Byte            Public Alpha As Byte        End Structure

        <StructLayout(LayoutKind.Sequential, Pack:=1)> _        Public Structure BLENDFUNCTION            Public BlendOp As Byte            Public BlendFlags As Byte            Public SourceConstantAlpha As Byte            Public AlphaFormat As Byte        End Structure

        Public Enum Bool            ' Fields            [False] = 0            [True] = 1        End Enum

        <StructLayout(LayoutKind.Sequential)> _        Public Structure Point            Public x As Integer            Public y As Integer            Public Sub New(ByVal x As Integer, ByVal y As Integer)                Me.x = x                Me.y = y            End Sub        End Structure

        <StructLayout(LayoutKind.Sequential)> _        Public Structure Size            Public cx As Integer            Public cy As Integer            Public Sub New(ByVal cx As Integer, ByVal cy As Integer)                Me.cx = cx                Me.cy = cy            End Sub        End Structure    End Class

以上只是提供了一个思路,有功夫的朋友可以修改这个方法,然后修改一些WINDOWS窗体控件,将它们转化成图像,统一用SetBitmap方法来进行输出,这样就等于完全自己处理控件的显示效果了,不过貌似工程量太庞大了点……