Treeview是一個很常用的Winform控制項,它提供了結合複選框和表徵圖的展示方式,而且有上下級節點的縮排,在開發中很方便實用。通常在使用中,通過指定一個表徵圖清單控制項(ImageList),把要填充的所有節點表徵圖都放到該控制項中,把Treeview的ImageList屬性指向它,然後在程式中根據節點資料來指定對應的表徵圖序號(或者名稱),可以方便地實現表徵圖的動態切換。但有時,節點的表徵圖不是固定的,可能是程式中動態產生的,比如常見的顏色設定和GIS圖例等功能,需要即時重新整理節點的顏色和圖案,顯然,並不能把所有的節點圖片預先儲存在ImageList中,該怎麼辦呢?幸好,Treeview控制項為我們提供了自訂繪製的介面。
下面結合著以前寫的一個圖例控制項,說明一下自訂繪製時的用法和要點,在此做個拋磚引玉。
首先,要實現自訂繪製,需要設定Treeview.DrawMode屬性,來指明繪製模式:
Treeview.DrawMode = TreeViewDrawMode.OwnerDrawText; //自訂繪製節點的文本和表徵圖
或
Treeview.DrawMode = TreeViewDrawMode.OwnerDrawAll; //自訂繪製節點的全部(包括節點虛線和複選框,以及節點展開和收縮時的+-號)
在通常情況下,我們只需要繪製表徵圖和文本就行,虛線和複選框由系統繪製,以下樣本即採用TreeViewDrawMode.OwnerDrawText模式。
其次,設定了繪製模式之後,需要實現節點繪製事件Treeview.DrawNode,該事件僅當 DrawMode 屬性設定為 OwnerDrawAll 或 OwnerDrawText 的 TreeViewDrawMode 值時,才引發。另外,該事件是在繪製每一個節點時都觸發,並非針對所有節點。
該事件的聲明為:public delegate void DrawTreeNodeEventHandler (Object sender,DrawTreeNodeEventArgs e),其中參數DrawTreeNodeEventArgs中包含了當前要繪製的節點對象,以及該節點的範圍座標等資訊。
在實現該方法時,主要負責處理兩個事情:繪製表徵圖和繪製文本,繪製的方法依靠參數DrawTreeNodeEventArgs中的Graphics物件控點。範例程式碼如下:
1 /// <summary>
2 /// 自己繪製節點表徵圖和文字
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 void legendTree_DrawNode(object sender, DrawTreeNodeEventArgs e)
7 {
8 Rectangle nodeRect = e.Node.Bounds; //節點地區
9
10 Point drawPt = new Point(nodeRect.Location.X - 18, nodeRect.Location.Y); //繪製表徵圖的起始位置
11 Size imgSize = new Size(12, 12); //圖片大小
12 Rectangle imgRect = new Rectangle(drawPt, imgSize);
13
14 //--------繪製圖片: 判斷節點類型,並根據各節點的類型繪製不同的圖片--------------------
15 if (e.Node is DirectoryNode)
16 {
17 this.LegendIcon.Draw(e.Graphics, drawPt, 0);
18 }
19 else if (e.Node is LayerNode)
20 {
21 ....
22 }
23 else if (e.Node is ThemeNode)
24 {
25 ....
26 }
27 else
28 {
29
30 }
31
32 //-----------------------繪製文本 -------------------------------
33 Font nodeFont = e.Node.NodeFont;
34 if (nodeFont == null)
35 nodeFont = ((TreeView)sender).Font;
36 Brush textBrush = SystemBrushes.WindowText;
37 if (mapView.MapObject.ReadOnly)
38 textBrush = SystemBrushes.GrayText; //如果不可編輯,則將字型顏色置灰
39 //反色反白
40 if ((e.State & TreeNodeStates.Focused) != 0)
41 textBrush = SystemBrushes.Window;
42 //不限定文本地區,以免大字型時間長度文本被截取----edited by: Vivi 2009/11/19
43 e.Graphics.DrawString(e.Node.Text, nodeFont, textBrush, Rectangle.Inflate(nodeRect, 2, 0));
44 }
在繪製過程中,一定要注意控製圖標的起點位置,以及表徵圖填充的範圍大小,另外,如果有些固定的表徵圖可以放到ImageList中或者是本地圖片檔案中,也可以通過圖片操作的相關類進行繪製,從而獲得更大的操作靈活性。
繪製後的效果如下:
注意:在繪製過程中,要注意的一個問題是效能和效率問題,由於即時繪製和頻繁調用,在大資料量時應該盡量避免,如果在繪製中使用e.Graphics.GetHdc(),則一定要記得在使用之後調用e.Graphics.ReleaseHdc()。