如果您要建立一个主控描绘的 TreeView 控件,也就是说,想要自行撰写程序代码来绘制 TreeView 控件的话,必须将 DrawMode 属性设定成 TreeViewDrawMode.OwnerDrawAll TreeViewDrawMode.OwnerDrawText ,并且替 DrawNode 事件处理例程撰写程序代码。
 
图表 1 所示者是 TreeViewDrawMode 列举类型的成员及其所代表的意义。
 
成员名称
说明
Normal
此为默认值,表示 TreeView 控件将由操作系统来绘制。  
OwnerDrawAll
此表示 TreeView 控件之 节点的所有项目都将由您自行绘制,包括:图示、复选框、加号、减号、以及连接节点的线条。  
OwnerDrawText
此表示 TreeView 控件之 节点的卷标部分将由您自行绘制,至于节点的其它项目则由操作系统来绘制,包括:图示、复选框、加号、减号、以及连接节点的线条。  
图表1
Visual Basic 2005 - 如何建立一个主控描绘的TreeView控件_第1张图片
                      图表2
 
图表 2 所示者是我们所撰写之程序范例的执行画面,此接口中的 TreeView 控件拥有下列特色:
 
q          如果库存量低于安全存量,便会在节点之卷标文字的右侧额外显示出一个标记文字“低于安全存量,请尽快补货。”

q          用户除了可以按一下卷标文字来选取节点之外,如果该节点额外显示出标记文字的话,也可以按一下标记文字“低于安全存量,请尽快补货。”来选取节点。

q          标记文字“低于安全存量,请尽快补货。”采用加底线的粗斜体之标楷体字型,而且是×××的,这些都是我们通过程序代码来自订的。

q          被选取之节点的背景色是绿色,这也是我们通过程序代码来自订的。
 
显而易见地,本程序示范的 TreeView 控件是一个主控描绘的 TreeView 控件,其设计技巧说明如下:
 
q          请如下所示,建立一个给节点之标记文字使用的 Font 对象:

Private tagFont As New Font(" 标楷体 ", 9, _
  FontStyle.Bold Or FontStyle.Italic Or FontStyle.Underline)

q          用户自订程序 LoadDataToTreeView() 负责将数据库数据表中的数据填入 TreeView 控件中而成为节点。我们以产品名称作为跟节点,并以其它字段的内容作为子节点,而在加入子节点时,会判断「库存量」字段的内容是否低于「安全存量」字段的内容,如果如此的话,便将文字符串“低于安全库存,请尽快补货。”存入该节点的 Tag 属性中:

While drProduct.Read()
  ' 以产品名称作为根节点。
  rootNode = New TreeNode(drProduct(0).ToString)
  TreeView1.Nodes.Add(rootNode)

  ' 以其它字段的内容作为子节点。
  For i As Integer = 1 To nFieldCount – 1
     childNode = New TreeNode( _
       drProduct.GetName(i) & " " & drProduct(i).ToString)
     TreeView1.Nodes(nRow).Nodes.Add(childNode)
     ' 如果库存量低于安全库存,则将警示讯息储
     ' 存于节点的 Tag 属性中。
     If drProduct.GetSqlInt16(3) < drProduct.GetSqlInt16(4) Then
        TreeView1.Nodes(nRow).Nodes(0).Tag = _
            " 低于安全库存,请尽快补货。 "
     End If
  Next
  nRow += 1
End While

q          窗体之 Load 事件处理例程中的下面这一道陈述式,表示要求 TreeView 控件采用主控描绘:

TreeView1.DrawMode = TreeViewDrawMode.OwnerDrawText

q          TreeView 控件的 DrawNode 事件处理例程撰写程序代码,以便进行节点的绘制作业:

Private Sub TreeView1_DrawNode(ByVal sender As Object, _
  ByVal e As DrawTreeNodeEventArgs) Handles TreeView1.DrawNode

  ' 绘制被选取之节点的背景色与节点文字。
  If (e.State And TreeNodeStates.Selected) <> 0 Then

     ' 绘制被选取之节点的背景色。如果该节点拥有标记文字
     ' 「低于安全库存,请尽快补货。」的话, NodeBounds 方法
     ' 会使得醒目提示区域拥有足够的大小来容纳它。
     e.Graphics.FillRectangle(Brushes.Green, NodeBounds(e.Node))

     ' 提取节点字型。如果节点字型未被设定,
     ' 就使用 TreeView 的字型。
     Dim nodeFont As Font = e.Node.NodeFont
     If nodeFont Is Nothing Then
        nodeFont = CType(sender, TreeView).Font
     End If

     ' 绘制节点文字。
     e.Graphics.DrawString(e.Node.Text, nodeFont, _
       Brushes.White, e.Bounds.Left - 2, e.Bounds.Top)
  Else
     e.DrawDefault = True
  End If

  ' 如果存在节点标记,就将它绘制在节点标签的右侧。
  If (e.Node.Tag IsNot Nothing) Then
     e.Graphics.DrawString(e.Node.Tag.ToString(), tagFont, _
       Brushes.Yellow, e.Bounds.Right + 2, e.Bounds.Top)
  End If

  ' 如果节点拥有焦点,便将焦点矩形绘制得够大
  ' 以便能够容纳节点卷标文字。
  If (e.State And TreeNodeStates.Focused) <> 0 Then
     Using focusPen As New Pen(Color.Black)
       focusPen.DashStyle = _
         System.Drawing.Drawing2D.DashStyle.Dot
       Dim focusBounds As Rectangle = NodeBounds(e.Node)
       focusBounds.Size = New Size(focusBounds.Width - 1, _
         focusBounds.Height - 1)
       e.Graphics.DrawRectangle(focusPen, focusBounds)
     End Using
  End If
End Sub

q          TreeView 控件的 MouseDown 事件处理例程撰写下列程序代码,使得用户可以按一下标记文字“低于安全库存,请尽快补货。”来选取节点:

' 不论用户按一下节点卷标或节点标记都选取该节点。
Private Sub TreeView1_MouseDown(ByVal sender As Object, _
  ByVal e As MouseEventArgs) Handles TreeView1.MouseDown
  Dim clickedNode As TreeNode = TreeView1.GetNodeAt(e.X, e.Y)
  If NodeBounds(clickedNode).Contains(e.X, e.Y) Then
     TreeView1.SelectedNode = clickedNode
  End If
End Sub

q          用户自订函式 NodeBounds 能够传回所传递进来之节点的边界范围,包括节点卷标以及节点标记所占用的区域:

Private Function NodeBounds(ByVal node As TreeNode) As Rectangle
  ' 将传回值设定成正常的节点边界范围。
  Dim bounds As Rectangle = node.Bounds
  If (node.Tag IsNot Nothing) Then
     ' 取得 TreeView 控件的 Graphics 对象并使用
     ' 它来计算节点标记的显示宽度。
     Dim g As Graphics = TreeView1.CreateGraphics()
     Dim tagWidth As Integer = CInt(g.MeasureString( _
         node.Tag.ToString(), tagFont).Width) + 6

     ' 使用计算所得的值来调整节点边界范围。
     bounds.Offset(tagWidth \ 2, 0)
     bounds = Rectangle.Inflate(bounds, tagWidth \ 2, 0)
  End If
  Return bounds
End Function
 
参考数据:

Visual Basic 2005程序开发与界面设计秘诀
Visual C# 2005程序开发与界面设计秘诀