Custom Draw TreeView in VB.NET






4.37/5 (12 votes)
May 26, 2005
2 min read

165147

1794
An owner draw implementation of a VB.NET treeview to show some bold text in nodes.
Introduction
This article shows the owner draw technique for a TreeView
using Visual Basic .NET to draw some portions of the text of the nodes in bold font, as shown in the image.
Background
The owner draw technique for Windows Common Controls is well documented in the following MSDN article: Customizing a Control's Appearance Using Custom Draw&, which I recommend to read. The article explains the notification messages, the paint cycles and drawing stages, and provides a C++ example, so I won't repeat it here.
Using the code
A TreeNodeEx
class (derived from TreeNode
) is provided in the source code, which allows you to specify in the constructor the node text, the initial text position that will use the bold font, and the length of the bold text.
A helper function like this is provided to add nodes to a 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
A TreeViewEx
class (derived from TreeView
) is provided too. This class performs the owner draw with the tree nodes. The class is used as follows:
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
Points of Interest
There are some points of interest in the source code:
- Windows Common Controls send
NM_CUSTOMDRAW
notifications throughWM_NOTIFY
messages to the parent window. So, we would need to intercept that message in the parent window, outside of our treeview control, which breaks the encapsulation rules. Fortunately, the .NET Framework allows controls to receive that message "reflected". To do this, the .NET Framework adds the value 0x2000 to the value of theWM_NOTIFY
message and sends it to the control. Therefore, theTreeView
control can receive theWM_NOTIFY
message sent to its parent window using the following code in its ownWndProc
procedure: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
- To draw the text of a node, which mixes bold and non-bold portions, we need to draw the initial non-bold portion, the bold portion, and the final non-bold portion. To do this, we need to know the length of each portion in pixels, to set the coordinate X of the next portion, and we need a very accurate measure to avoid "holes" between two portions. It happens that when using the function
Graphics.MeasureCharacterRanges
to measure drawn strings, some pixels are added to the exact result. Since we need the exact result (in order to draw the next text just after the previous one), we can use the following trick: we measure the length of the text and the length of the text duplicated: since in both cases the extra pixels are added, the difference will be the exact length.
History
- 24-May-2005. Initial version.