Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Draw TreeView in VB.NET

0.00/5 (No votes)
25 May 2005 1  
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 through WM_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 the WM_NOTIFY message and sends it to the control. Therefore, the TreeView control can receive the WM_NOTIFY message sent to its parent window using the following code in its own WndProc 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here