程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 對C#中的TreeView添加背景圖

對C#中的TreeView添加背景圖

編輯:關於C語言
 在微軟的.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內部類,導入了自己需要的函數。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved