做过.NET Winform窗体美化的人应该都很熟悉UpdateLayeredWindow吧,UpdateLayeredWindow可以实现窗体的任意透明,效果很好,不会有毛边。不过使用这个API之后,会有一个问题就是无法使用普通控件,而且没有Paint消息。为了解决这个问题,有两种方法。
一、使用双层窗体,底层窗体使用UpdateLayeredWindow作为背景,上层窗体用普通窗体,并且可以使用TransparencyKey或者Region来实现去除不需要的窗体内容,让上层窗体能看到底层的窗体。
二、直接单层窗体,使用控件的DrawToBitmap把控件图像绘制到UpdateLayeredWindow的窗体上,这样就可以看到普通控件了。不过这个也有问题:1.控件内容不能自动更新 2.效率低,很多控件使用DrawToBitmap绘制出的图像不完整,甚至绘制不出图像。比如TextBox无法显示光标,WebBrowser无法显示内容。
三、采用DirectUI技术,重写所有基础控件。效果最好,不过工作量巨大。
使用UpdateLayeredWindow时,一般是需要对Bitmap缓存起来,通过设置剪辑区域,局部重绘来提高效率。另外还可以异步重绘,模拟Winform的失效到重绘。
有些人会说为什么不直接用WPF啊,Wpf和Winform各有优缺点,适应不同的场合。Winform相对于使用更简单一些,系统要求更低。当然需要看人的习惯了和擅长的。
UpdateLayeredWindow 基本使用方法:
重写窗体的 CreateParams 属性
protected override CreateParams CreateParams { get { CreateParams cp = base .CreateParams; cp.ExStyle |= 0x00080000 ; // WS_EX_LAYERED 扩展样式 return cp; } }
API调用:
public void SetBitmap(Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException( "位图必须是32位包含alpha 通道" ); IntPtr screenDc = Win32.GetDC(IntPtr.Zero); IntPtr memDc = Win32.CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb( 0 )); // 创建GDI位图句柄,效率较低 oldBitmap = Win32.SelectObject(memDc, hBitmap); Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); Win32.Point pointSource = new Win32.Point( 0 , 0 ); Win32.Point topPos = new Win32.Point(Left, Top); Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); blend.BlendOp = Win32.AC_SRC_OVER; blend.BlendFlags = 0 ; blend.SourceConstantAlpha = opacity; blend.AlphaFormat = Win32.AC_SRC_ALPHA; Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0 , ref blend, Win32.ULW_ALPHA); } finally { Win32.ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { Win32.SelectObject(memDc, oldBitmap); Win32.DeleteObject(hBitmap); } Win32.DeleteDC(memDc); } }
API声明:
class Win32 { public enum Bool { False = 0 , True } ; [StructLayout(LayoutKind.Sequential)] public struct Point { public Int32 x; public Int32 y; public Point(Int32 x, Int32 y) { this .x = x; this .y = y; } } [StructLayout(LayoutKind.Sequential)] public struct Size { public Int32 cx; public Int32 cy; public Size(Int32 cx, Int32 cy) { this .cx = cx; this .cy = cy; } } [StructLayout(LayoutKind.Sequential, Pack = 1 )] struct ARGB { public byte Blue; public byte Green; public byte Red; public byte Alpha; } [StructLayout(LayoutKind.Sequential, Pack = 1 )] public struct BLENDFUNCTION { public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat; } public const Int32 ULW_COLORKEY = 0x00000001 ; public const Int32 ULW_ALPHA = 0x00000002 ; public const Int32 ULW_OPAQUE = 0x00000004 ; public const byte AC_SRC_OVER = 0x00 ; public const byte AC_SRC_ALPHA = 0x01 ; [DllImport( " user32.dll " , ExactSpelling = true , SetLastError = true )] public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); [DllImport( " user32.dll " , ExactSpelling = true , SetLastError = true )] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport( " user32.dll " , ExactSpelling = true )] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport( " gdi32.dll " , ExactSpelling = true , SetLastError = true )] public static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport( " gdi32.dll " , ExactSpelling = true , SetLastError = true )] public static extern Bool DeleteDC(IntPtr hdc); [DllImport( " gdi32.dll " , ExactSpelling = true )] public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); [DllImport( " gdi32.dll " , ExactSpelling = true , SetLastError = true )] public static extern Bool DeleteObject(IntPtr hObject); [DllImport( " user32.dll " , EntryPoint = " SendMessage " )] public static extern int SendMessage( int hWnd, int wMsg, int wParam, int lParam); [DllImport( " user32.dll " , EntryPoint = " ReleaseCapture " )] public static extern int ReleaseCapture(); public const int WM_SysCommand = 0x0112 ; public const int SC_MOVE = 0xF012 ; public const int SC_MAXIMIZE = 61488 ; public const int SC_MINIMIZE = 61472 ; }
需要呈现图像的时候调用 SetBitmap 方法。只要优化好,呈现效率比普通的Paint重绘方式高很多,并且不卡不闪烁,支持任意透明。
下面是自己开发出来的效果:
这个是用OpenGL绘制的
推荐一款C#界面库:DSkin界面库(Winform平台首个DirectUI界面库) http://d.cskin.net
- 38楼渃水
- 好屌好屌
- 37楼伊夏
- 66666,太炫了,对WinForm重燃信心啊
- 36楼Dr.Liu
- 不错,很少看到国内在这方面的作品啊
- 35楼笑对当空
- mark下 这可不容易啊
- 34楼困了
- 不错 挺漂亮
- 33楼新的开始
- 膜拜
- 32楼shungdawei
- 不错,效果挺好的,系统不再那么死板
- 31楼流年、落了誰
- winform都这么屌了,涨姿势了
- 30楼小熊xfl
- 是挺好的,但是连个注解都没有,怎么看??
- 29楼wgmesc#
- hao
- 28楼ta_wuhen
- 酷,有机会研究下
- 27楼网络白蚁
- DSkin界面库 不免费。
- 26楼hoodlum1980
- 不错,给了我些有用的信息
- 25楼cyberspace
- 那个仿QQ登录很帅啊,楼主可否发个源码?,我的邮箱:dahuaziliao@163.com
- Re: DSkin
- @cyberspace,http://bbs.cskin.net/thread-465-1-1.html 不过控件源码不提供
- 24楼风情波涌
- 无码无真相,还是继续WPF吧。
- Re: DSkin
- @风情波涌,http://bbs.cskin.net/forum-56-1.html 这里有我写的LayeredSkin控件库,还有一些特效实现算法
- 23楼寒叶
- 我艹,太牛逼了
- 22楼焦涛
- mark
- 21楼彡流年彡似水
- 一直以为只有WPF才能做出这么绚丽的效果,楼主好厉害!
- 20楼通用C#系统架构
- 的确牛b.
- 19楼IT鸟
- 最炫民族风!
- 18楼networkcomms通信框架
- 研究一下
- 17楼文楚
- 我当初也废了好大劲,辛亏只涉及一个欢迎界面,试了n种办法,最后还是wpf
- 16楼国产毛毛虫
- 软文,广告。。。。皮肤需要收费
- Re: DSkin
- @国产毛毛虫,提供免费的 http://bbs.cskin.net/forum-56-1.html 感兴趣的话可以去看看
- 15楼sky300
- 挺酷的,现在用winform开发的比较少了吧,
- 14楼浮生有梦
- 这个可以学习。
- 13楼禾子三金
- nb
- 12楼辰晓晨
- 太牛逼了 卧槽 怎么弄得
- 11楼Gamain
- 这不是wpf的特长吗,winform这么吊?
- 10楼钻葛格
- 必须mark!
- 9楼幻天芒
- 确实绚丽,霸道!
- 8楼小鱼小虾
- 牛逼
- 7楼LGGGGG
- 第一次发现winform也能这么屌!
- 6楼大恒爸爸
- mark
- 5楼高卿
- 小温?
- Re: DSkin
- @高卿,引用小温?,第一个评论才是
- 4楼郑隆2015
- 这个动态的图是怎么搞的?
- Re: DSkin
- @郑隆2015,【GIF录制工具】ScreenToGif录制屏幕gif动画,http://bbs.cskin.net/forum.php?mod=viewthreadamp;tid=568amp;fromuid=8
- 3楼LiuHuaTao
- DSkin是收费的吧,目前用的免费的CSkin
- Re: 威廉乔克斯_汀
- @LiuHuaTao,CSkin和DSkin是一家的gt;Alt;!
- 2楼威廉乔克斯_汀
- Good~I like
- 1楼Kation
- 我有点不明白既然要这么酷炫为何不用wpf……
- Re: 刘京辉
- @Kation,引用我有点不明白既然要这么酷炫为何不用wpf……,是啊,WPF又没那么难。