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.
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
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.
Private Sub TreeView1_AfterSelect(ByVal sender As System.Object,
_ByVal e As System.Windows.Forms.TreeViewEventArgs)
If TreeView1.SelectedNode.Level = 0 Then Exit Sub
Dim newNode As New TreeNode
newNode = TreeView1.SelectedNode.Clone
If .BackColor = Color.White Then
.BackColor = Color.Yellow
.BackColor = Color.White
TreeView1.SelectedNode = .Parent
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:
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
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:
Private Sub ShowIt(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim aNode As TreeNode
Dim msg As String = "Selected nodes:" & vbCrLf
For Each aNode In TreeView2.Nodes
msg = msg & aNode.Text & vbCrLf
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.
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.