VB.NET 自定义绘制 TreeView






4.37/5 (12投票s)
2005年5月26日
2分钟阅读

165898

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日。初始版本。