世界上最简单的多选 TreeView
一种快速简便的方法,可以在TreeView中启用多选,并轻松迭代选定的节点。
引言
你可能会认为,允许用户在TreeView中选择多个项目(就像在ComboBox中一样),然后浏览选定项目的集合应该很简单,但事实并非如此。Microsoft没有提供一种简单的方法来做到这一点。本文将向您展示一种简单的方法来实现多选,以及一种简单的方法来维护选定节点的集合——无需派生新的控件,也无需使用递归。
背景
Microsoft的TreeView
控件允许你在每个节点旁边显示一个复选框。但是,这些复选框有点难看,而且它们位于每个节点旁边,这可能不是你想要的。
我知道我可以使用ImageList
并替换我自己的更优雅的复选框,但是TreeView
控件会坚持为每个节点添加图像。如果我把一个空白图像放在ImageList
的第一个位置,那么在没有复选框的节点旁边就会留下难看的空白。
同样糟糕的是,我找到的所有构建选定节点集合的解决方案都涉及使用递归。如果正确实现,递归非常有用。但是,它可能会造成混乱。
我希望获得比这些方法允许的更多控制,因此我决定稍微修改规则并实现一个非常简单的解决方案。与其显示难看的复选框,我决定只更改选定节点的背景颜色。与其使用递归,我认为第二个隐藏的TreeView
可以保存我选定的节点集合。
使用代码
我的方法涉及捕获TreeView
的AfterSelect
事件。事实上,所有工作都在这里完成。代码出奇地简单。它假设你有两个TreeView
,一个标记为Visible=False
。因为它不可见,所以它可以是任何大小,并且可以放在任何位置。把它想象成你的隐藏记事本。
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
此子例程的第一行阻止用户选择根节点(级别0)。如果需要,可以添加此限制以处理TreeView
的其他级别。例如,我用它来阻止除TreeView
节点的最低级别(在我的例子中是级别3)以外的所有级别被选中,所以我的代码如下所示:
If TreeView1.SelectedNode.Level < 3 Then Exit Sub
你不能直接从一个TreeView
复制节点到另一个TreeView
,所以我克隆了SelectedNode
。然后,我决定下一步该做什么。如果SelectedNode
的BackColor
为白色,我将其更改为黄色。这使得它看起来像荧光笔标记了一样。当然,你可以选择任何你想要的颜色。在我将其更改为黄色之后,我将克隆的节点(SelectedNode
的完美副本)添加到第二个隐藏的TreeView
中。如果SelectedNode
的BackColor
已经是黄色(意味着用户之前已选择它,但现在想要取消选择它),我将恢复其白色BackColor
,然后从第二个TreeView
中删除克隆的节点。
结果是,第二个隐藏的TreeView
始终包含用户在第一个可见TreeView
中选择的节点的集合。一旦用户单击“确定”,遍历这些选定节点就成了一件简单的事情。
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
关注点
所有这一切之所以有效,是因为每个节点必须具有唯一的名称。尝试使用Text
属性会造成混乱,因为你无法保证两个节点不会具有相同的文本。
上面第一个子例程的最后一行很有趣。我发现,由于焦点仍然停留在SelectedNode
上,因此BackColor
的变化并不立即显而易见。因为我知道每个SelectedNode
都会有一个父节点,所以我决定将焦点转移到父节点。
结论
正如我上面提到的,还有其他解决这些问题的方案,它们展示了一些重要的编程方法和创造性的想法。其中一个解决方案可能更适合您的需求。但是,我希望您发现我的简单方法是一个不错的替代方案。