一、问题描述
一个基于.NET 2.0的Winform程序运行在Vista中,它用来显示GIF动画。拖拽另外一个程序的窗口把该GIF动画完全覆盖,然后再拖开,重新显示该GIF动画所在的窗口。此时,我们发现动画已经停止播放了。这个问题在XP中不会出现。
二、问题重现
1. 在Visual Studio里,新建一个基于.NET 2.0的Winforms工程
2. 添加一个PictureBox
3. 把该PictureBox的Image属性设为一个GIF动画
4. 在Vista中运行该程序
5. 拖拽另外一个程序的窗口以便完全覆盖GIF动画,再拖开
三、原因分析
Winform每隔50毫秒绘制GIF动画的一帧。出于效率的考虑,当Winforms发现一个有GIF动画的窗口被另一个窗口完全覆盖时,由于该窗口已经不可见,因此会停止其上的GIF动画绘制操作。当覆盖该GIF动画的窗口被移开后,包含该GIF动画的窗口在XP及其以前版本的Windows中,会收到一个WM_PAINT消息。Winforms在该消息的处理函数中会重新开始每隔50ms毫秒绘制一帧动画,因此动画能正常重新播放。
在Vista中,Windows绘图层代码做了一些优化。当一个窗口被另外一个窗口覆盖然后又重新变得可见时,Windows不会向该窗口发送WM_PAINT消息(实际上没有任何消息通知该窗口重新变得可见)。因此在Vista中,含有GIF动画的窗口不能重新启动动画。
四、 解决方案
为了解决这个问题,Winforms决定即使含有GIF动画的窗口完全被覆盖,也不停止GIF动画的绘制操作。由于GIF动画不会被停止,因此也就不存在重新启动GIF动画的问题。
Winforms决定不再停止GIF动画,除了上述问题之外,还有另外一个考虑:在Vista及更高版本的Windows中,窗口的标题栏和边框都是半透明的,因此GIF动画被这样的窗口覆盖时,GIF动画实际上还是可见的。在这种情况下,不停止GIF动画绘制操作给用户提供了更人性化的人机界面体验。
可能会有人当心这个改动带来潜在的效率问题。实际上这种担忧是多余的,因为我们绘制GIF动画是在一个单独的线程中进行的。该线程的运行情况对主线程影响甚小。另外,该线程每绘制完一帧,就会睡眠50毫秒。通常情况下绘制一帧的时间远小于50毫秒。由于这两方面的原因,这个改动对Winforms绘制GIF动画的效率不会带来不利影响。
Windows 7RTM版自带了.NET 2.0,在那个版本的.NET中,我们修正了这个问题。同时,在最新的.NET 4.0中,这个问题也已被改正。如果用户遇到这个问题,有两种方案可供选择:升级到Window 7或者升级到.NET 4.0。