最大的网站源码资源下载站,
在微软的.net的forms窗口控件中,比如treeview和listview,仅仅是对通用控件的简单封装,因此他们不正常的引发paint事件。 微软所发布内容中,能看到的唯一建议就是设置控件的controlstyles.userpaint类型,然后自己为控件做所有的绘图操作。 (译注:老外提供了一个treeviewwithpaint控件类,派生自treeview类,提供了paint事件的挂接。)
一、为了解决这个问题,我们在类内部使用了一个基于bitmap类的graphics对象。当任何窗口重新定义大小时候,对象都会重建。
//recreate internal graphics object
protected override void onresize( system.eventargs e )
{
if( internalbitmap == null || internalbitmap.width != width || internalbitmap.height != height )
{
if( width != 0 && height != 0 )
{
disposeinternal();
internalbitmap = new bitmap( width, height );
internalgraphics = graphics.fromimage( internalbitmap );
}
}
}
二、重写窗口过程
当控件收到了wm_paint消息时候,将执行下面的三个步骤:
1. 通过一个内部的wm_printclient消息,让原来的控件过程把图象画到内部的graphics对象上。
//draw internal graphics
intptr hdc = internalgraphics.gethdc();
message printclientmessage = message.create( handle, wm_printclient, hdc, intptr.zero );
defwndproc( ref printclientmessage );
internalgraphics.releasehdc( hdc );
2. 使用内部的graphics对象建立painteventargs参数,引发用户的onpaint()函数。
//add the missing onpaint() call
onpaint( new painteventargs( internalgraphics, rectangle.fromltrb(
updaterect.left,
updaterect.top,
updaterect.right,
updaterect.bottom ) ) );
3. 把内部graphics对象的位图拷贝到屏幕的graphics设备上。
//draw screen graphics
screengraphics.drawimage( internalbitmap, 0, 0 );
wm_erasebkgnd消息被过滤掉,什么都不做。 case wm_erasebkgnd:
//removes flicker
return;
三、所提供的代码和测试程序能使用paint事件在treenode在被选中的时候,在其边框上画个黄色的边框。但是,其实对于我实际要用的项目来说,需要添加背景图的功能没有实现。而这里离我们的目的还有一步之遥,我们对前文绘图过程2和3之间加一个步骤:
bitmap temp = new bitmap(internalbitmap, internalbitmap.size); // 建立一个临时的位图temp,保存前面绘好的界面
temp.maketransparent(color.white); // 设置白色为透明色
internalgraphics.fillrectangle(brushes.white, 0, 0, this.bounds.width, this.bounds.height);
// 在原来的内部位图对象上,用白色重画背景
if (image != null) // 如果设置了背景图,就在内部对象上画背景
internalgraphics.drawimage (image, 0, 0, image.width, image.height);
internalgraphics.drawimage(temp, 0, 0, temp.width, temp.height);// 把前面绘好的界面按白色为透明色复合到内部位图上
screengraphics.drawimage( internalbitmap, 0, 0 ); // 把合成的临时位图刷到屏幕上
其实,这里还存在一个问题:在处理wm_paint消息时候,通常的做法是使用beginpaint和endpaint函数来操作dc画图的,当树结点展开或者折叠时候,我们收到wm_paint消息,并由消息得到的刷新区域或者说刷新矩形。关键就是在于,这里的刷新区域不是整个客户区,背景图会出现重叠的部分而变形。
解决方法:考虑使用getdc和releasedc操作,可以避开刷新区域的限制,我们可以把整个客户区重画,而实现背景图的完整性。这里要非常注意的是:beginpaint和endpaint函数会自动把需要刷新的区域设为有效,而getdc和releasedc函数不会,所以我们要自己增加两个操作getupdaterect和validaterect,也就是自己把需要刷新的区域设置为有效。否则:会不停的得到wm_paint消息,和死循环一样,cpu占用达到100%。
图一 测试程序
四、结束语
由于使用了win32的api函数,因此附加了一个win32内部类,导入了自己需要的函数。