在透明窗体上显示标准Winform控件

透明窗体指的是利用UpdateLayeredWindows函数完成窗体绘制过程的特殊窗体,采用这种技术的最大优势在于支持含Alpha通道的PNG格式图片作为背景图,从而得到非常漂亮的异型窗体。可惜的是这种窗体与Winform体系的本质冲突导致其不能与标准Winform控件共同工作。

Google到的解决方案多是使用双窗体来解决,不过本着技术研究的精神,我尝试设计了另一种让此二者共存的方案。

 

Winform绘图过程简要分析

控件在两种情况下需要调用绘制过程,一:遮挡物移开了;二:自身的视觉相关属性发生变化。这两个情况下,最终都要通过Invalidate函数才能进入到绘制过程,而在OnPaint绘制函数中,绘图设备对象Graphics则是由Control基类传递下来的。由此可见,Invalidate函数的处理过程肯定是发出了一个信号,而系统则在接受到此信号后通过WM_Paint消息将绘图设备对象和绘图区域信息反馈给了控件,并通过Control基类的OnPaint方法完成所有子类的绘制过程。

这么看来,问题的核心就在于如何获取Graphics对象,并将其指定给Control对象的OnPaint方法。

由于Form继承自Control,所以必然遵从同样的绘图过程,但对Layered窗体而言,OnPaint方法是不会被调用了,也就是说,无法获取系统传递的Graphics对象,那么此时,一个窗体又是如何完成绘制的呢?

 

插入我们的Graphics

Layered窗体的绘制过程,核心是UpdateLayeredWindows函数,但若要支持半透明PNG背景,必然有一个背景图的绘制过程。通常,是在UpdateLayeredWindows函数调用之前,先获取操作系统桌面绘图设备的内存映射,再将背景图绘制在此内存设备上,最后,将内存映射设备合并到桌面绘图设备上。(具体过程可以参考我提供的源码)。没错了,既然存在背景图对象,自然让人联想到Graphics类中提供了一个由背景图创建绘图设备的方法,通过这个绘图设备来绘制任何内容,最终都会成为背景图的一部分,那么最终也就能在桌面上显示出来了。

整个方案简单来说就是在Layered窗体绘制过程中,由背景图创建出Graphics对象,并构造一个PaintEventArgs对象传递给winform控件的OnPaint方法,从而使得控件的绘制过程在我们指定的绘图设备上完成。

 

 

 

透明背景的控件

标准Winform控件对透明的支持相当“愚蠢”,是通过将自己的绘图设备指定给父控件,让父控件先在要透明的区域绘制一遍以获得“透明”的效果,但是在以上实现方法中,如果还这样绘制就行不通了,因为传递给控件的Graphics绘图设备就是从窗体的背景图获得的,本身已经包含了背景内容,如果再绘制一次,这种“假透明”就穿帮了。为了强调这一点,我在代码中强制了控件必须实现一个接口来完成绘制过程,在这个接口方法的实现中,你可以自由控制是否要绘制背景。(不是必须的,简单修改一下代码即可)

 

一点遗憾

对于大多数控件来说,这个方案完全符合自绘控件的实现标准,但是对于一些特殊的控件,暂还没有找到好的实现方法(PS.如果你已经实现,或有兴趣研究,欢迎交流)。这些所谓特殊控件即如TextBox那样存在一个输入光标的控件,由于这个闪动的小光标并不是控件自己绘制的,而是由windows系统负责控制的,我暂时还没有找到实现方法。而且,对于TextBox这种控件,似乎其文字也不是由其自身绘制的,实验中,我必须在TextBox的OnTextChanged重载中强制重绘才能完成输入内容的显示。

 

Vista下实现全透明窗体的简单方法

对于Vista及win7系统,在开启Aero后,通过相关API函数可以将毛玻璃效果扩展到整个窗体,令人意外的是,此时若将窗体边框设为None,窗体将完全从桌面上消失,不过控件依旧能正常显示。在给出的代码中,我已同时支持这两种技术来实现全透明的窗体,不过,可能没有人希望在能实现Aero效果的同时实现全透明窗体吧 ^_^

 

代码应用效果:

说明:左上这个蓝色区域即为Layered模式下的半透明窗体实例,为了效果明显一点,窗体本身的绘制过程添加了半透明背景和边框的绘制。
中间那个绿色的箭头是一个标准PictureBox控件,实现了我上文提到的接口以支持透明背景,你可以用这个控件加载一个透明Gif试试 ^_^

 

------------------破处分割线------------------------------

第一次在CSDN博客发帖,怎么发布源码?

下载地址:

http://download.csdn.net/source/3113901


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值