Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

World's Easiest Multi-Select TreeView

2.80/5 (6 votes)
24 May 2009CPOL3 min read 47K  
A quick and easy way to enable multi-select in a TreeView, and an easy way to iterate selected nodes.

Introduction

You would think it would be simple to allow a user to select more than one item in a TreeView, the same way you can in a ComboBox, and then explore the collection of selected items, but it isn't. Microsoft does not provide an easy way to do so. This article will show you an easy way to implement multi-select, and an easy way to maintain a collection of the nodes that are selected--without deriving a new control and without using recursion.

Background

Microsoft's TreeView control allows you to display a checkbox next to every node. But, the checkboxes are a bit ugly, and they are next to every node, which may not be what you want.

I knew I could use an ImageList and substitute my own more graceful checkbox, but the TreeView control would insist on adding an image to every node. If I placed a blank image in the first position of the ImageList, that left ugly gaps next to nodes that didn't get a checkbox.

Just as bad, all the solutions I found for building a collection of selected nodes involved using recursion. Implemented properly, recursion is very useful. But, it can create a terrible mess.

I wanted more control than these approaches allowed, so I decided to bend the rules a bit and implement a very simple solution. Rather than displaying the ugly checkboxes, I decided to just change the background color of the selected nodes. And rather than use recursion, I figured a second, hidden, TreeView could hold my collection of selected nodes.

Using the code

My approach involves trapping the TreeView AfterSelect event. In fact, that's where all of the work happens. The code is surprisingly simple. It assumes you have two TreeViews, one that is marked Visible=False. Since it's not visible, it can be any size you want, and positioned anywhere you want. Think of it as your hidden Notepad.

VB
Private Sub TreeView1_AfterSelect(ByVal sender As System.Object,
                       _ByVal e As System.Windows.Forms.TreeViewEventArgs)
                       _Handles TreeView1.AfterSelect
    If TreeView1.SelectedNode.Level = 0 Then Exit Sub
    Dim newNode As New TreeNode
    newNode = TreeView1.SelectedNode.Clone
    With TreeView1.SelectedNode
        If .BackColor = Color.White Then
            .BackColor = Color.Yellow
            TreeView2.Nodes.Add(newNode)
        Else
            .BackColor = Color.White
            TreeView2.Nodes.RemoveByKey(.Name)
        End If
        TreeView1.SelectedNode = .Parent
    End With
End Sub

The first line of this subroutine prevents the user from selecting a root node (level 0). You can add to this restriction to handle other levels of the TreeView, if you want. For instance, I use this to block all but the lowest level of my TreeView nodes from being selected, which in my case is level 3. So, my code reads:

VB
If TreeView1.SelectedNode.Level < 3 Then Exit Sub

You can't copy a node from one TreeView to another directly, so I clone the SelectedNode. Then, I decide what to do next. If the SelectedNode has a BackColor of white, I change it to yellow. This makes it appear as if a highlighter had marked it. You can choose any color you want, of course. After I change it to yellow, I add the cloned node (a perfect copy of the SelectedNode) to the second, hidden TreeView. If the SelectedNode already had a BackColor of yellow (meaning that the user had selected it previously but now wants to deselect it), I restore its white BackColor and then remove the cloned node from the second TreeView.

The result is that the second, hidden TreeView always contains a collection of the nodes the user has selected in the first, visible TreeView. Once the user has clicked OK, it's a simple matter to iterate through these chosen nodes:

VB
Private Sub ShowIt(ByVal sender As System.Object, ByVal e As System.EventArgs)
                   _Handles btnOK.Click
    Dim aNode As TreeNode
    Dim msg As String = "Selected nodes:" & vbCrLf
    For Each aNode In TreeView2.Nodes
        msg = msg & aNode.Text & vbCrLf
    Next
    MsgBox(msg)
End Sub

Points of interest

All of this works because each node must have a unique name. Trying to use the Text property makes a mess, because you can't guarantee that two nodes will not have the same text.

The last line of the first subroutine above is interesting. I found it annoying that because the focus remained on the SelectedNode, the change in BackColor wasn't immediately apparent. Since I knew that every SelectedNode would have a parent, I decided to shift focus to the parent node.

Conclusion

There are other solutions to these issues, as I mentioned above, and they illustrate some significant programming approaches and creative ideas. One of those solutions might better fit your needs. But, I hope that you find my simple approach a nice alternative.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)