TreeView的自定义绘制图标处理
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()。