65.9K
CodeProject 正在变化。 阅读更多。
Home

VB.NET 自定义绘制 TreeView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (12投票s)

2005年5月26日

2分钟阅读

viewsIcon

165898

downloadIcon

1794

一个 VB.NET treeview 的所有者绘制实现,用于在节点中显示一些粗体文本。

引言

本文展示了使用 Visual Basic .NET 为 TreeView 实现所有者绘制技术,以粗体字体绘制节点文本的某些部分,如图所示。

背景

Windows 通用控件的所有者绘制技术在以下 MSDN 文章中有充分记录:使用自定义绘制自定义控件的外观&,我建议阅读。文章解释了通知消息、绘制周期和绘制阶段,并提供了一个 C++ 示例,因此我不会在此重复它。

使用代码

源代码中提供了一个 TreeNodeEx 类(从 TreeNode 派生),它允许您在构造函数中指定节点文本、将使用粗体字体的初始文本位置以及粗体文本的长度。

提供了一个辅助函数,如下所示,用于将节点添加到 TreeView

Private Function AddNodeToTreeView(ByVal colNodes As TreeNodeCollection, _
     ByVal sText As String, ByVal iBoldTextInitialPosition As Integer, _
     ByVal iBoldTextLength As Integer) As TreeNodeEx

   Dim objTreeNodeEx As TreeNodeEx

   objTreeNodeEx = New TreeNodeEx(sText, _
                   iBoldTextInitialPosition, iBoldTextLength)
   colNodes.Add(objTreeNodeEx)

   Return objTreeNodeEx

End Function

还提供了一个 TreeViewEx 类(从 TreeView 派生)。该类使用树节点执行所有者绘制。该类的用法如下

Private m_ctlTreeViewEx As TreeViewEx

Private Sub Form1_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load

   Dim objRootTreeNodeEx As TreeNodeEx

   m_ctlTreeViewEx = New TreeViewEx()
   Me.Controls.Add(m_ctlTreeViewEx)
   m_ctlTreeViewEx.Left = 0
   m_ctlTreeViewEx.Top = 0
   m_ctlTreeViewEx.Dock = DockStyle.Fill

   objRootTreeNodeEx = AddNodeToTreeView(m_ctlTreeViewEx.Nodes, _
                       "This is the first node", 12, 5)

   AddNodeToTreeView(objRootTreeNodeEx.Nodes, "The second node", 4, 6)
   AddNodeToTreeView(objRootTreeNodeEx.Nodes, "Third node", 0, 5)
   AddNodeToTreeView(objRootTreeNodeEx.Nodes, "Node 4", 5, 1)
   AddNodeToTreeView(objRootTreeNodeEx.Nodes, "Last node", -1, 0)

   objRootTreeNodeEx.Expand()

End Sub

关注点

源代码中有些值得注意的地方

  • Windows 通用控件通过 WM_NOTIFY 消息将 NM_CUSTOMDRAW 通知发送到父窗口。因此,我们需要在父窗口中拦截该消息,在我们的 treeview 控件之外,这违反了封装规则。幸运的是,.NET Framework 允许控件以“反射”方式接收该消息。为此,.NET Framework 将值 0x2000 添加到 WM_NOTIFY 消息的值,并将其发送到控件。因此,TreeView 控件可以使用以下代码在它自己的 WndProc 过程中接收发送到其父窗口的 WM_NOTIFY 消息
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    
       Const WM_NOTIFY As Integer = &H4E
    
       Dim iResult As Integer
       Dim bHandled As Boolean = False
    
       If m.Msg = (&H2000 Or WM_NOTIFY) Then
       ' It is the reflected WM_NOTIFY message sent to the parent
    
    
          If m.WParam.Equals(Me.Handle) Then
             iResult = HandleNotify(m)
             m.Result = New IntPtr(iResult)
             bHandled = True
          End If
    
       End If
    
       If Not bHandled Then
          MyBase.WndProc(m)
       End If
    
    End Sub
  • 为了绘制节点的文本,该文本混合了粗体和非粗体部分,我们需要绘制初始的非粗体部分、粗体部分和最后的非粗体部分。为此,我们需要知道每个部分以像素为单位的长度,以设置下一个部分的坐标 X,并且我们需要一个非常精确的测量来避免两个部分之间的“空隙”。事实是,在使用函数 Graphics.MeasureCharacterRanges 测量绘制的字符串时,会向精确结果添加一些像素。由于我们需要精确的结果(以便在上一段文本之后立即绘制下一段文本),我们可以使用以下技巧:我们测量文本的长度和重复文本的长度:由于在两种情况下都添加了额外的像素,因此差异将是确切的长度。

历史

  • 2005年5月24日。初始版本。
© . All rights reserved.