最大的網站源碼資源下載站,
在微軟的.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內部類,導入了自己需要的函數。