TreeView 拖放入门 (VB.NET)
为 TreeView 控件提供拖放功能。
引言
TreeView
控件在需要向用户呈现任何形式的层次结构的应用程序中非常常见。通常,这种层次结构的性质要求用户能够对其进行重新组织;拖放操作提供了一种自然的解决方案。本文介绍了如何在 TreeView
控件中实现拖放功能。
入门
本文中的代码假定您有一个带有 TreeView
控件的窗体,该控件需要填充一些 TreeNode
(文章顶部的演示项目提供了一种随机生成 TreeNode
层次结构的方法)。通过将 AllowDrop
属性设置为 True
,确保 TreeView
可以接受拖放到其上的对象。
发起拖放
当用户在 TreeNode
上开始拖放操作时,会触发 ItemDrag
事件,我们需要处理它。
Public Sub TreeView1_ItemDrag(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.ItemDragEventArgs) _
Handles TreeView1.ItemDrag
'Set the drag node and initiate the DragDrop
DoDragDrop(e.Item, DragDropEffects.Move)
End Sub
在这里,我们通过调用 DoDragDrop
方法来发起拖放操作,指定正在拖动的 TreeNode
对象,并指定用户在拖放操作完成后将执行的 Move
操作。请注意,DragDropEffects
枚举仅仅描述了在操作过程中可以显示的游标(效果),它不会强制规定对象最终是否会因为成功的拖放操作而被移动。
拖动到控件上
拖放操作进行中时,TreeView
现在必须对拖动对象到其上时做出响应。发生这种情况时,会触发 DragEnter
事件,我们必须处理它。
Public Sub TreeView1_DragEnter(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragEnter
'See if there is a TreeNode being dragged
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) Then
'TreeNode found allow move effect
e.Effect = DragDropEffects.Move
Else
'No TreeNode found, prevent move
e.Effect = DragDropEffects.None
End If
End Sub
我们在这里所做的只是检查拖放操作中是否存在我们乐意将其拖放到 TreeView
上的对象,在本例中是 TreeNode
。如果找到了 TreeNode
,我们就指定游标上将显示 Move
效果,否则我们就指定显示 None
效果。请注意,您只能选择在调用 DoDragDrop
方法初始化拖放操作时指定的(效果)——(见上文的发起拖放)。
重要的是要检查是否存在您期望的类型的对象在拖放数据中(在本例中是 TreeNode
)。在此示例中,我们只有一个控件在发起拖放操作。然而,在一个窗体上,可能有许多其他控件调用 DoDragDrop
方法;通过将控件的 AllowDrop
属性设置为 True
,您就指定了它会在任何对象被拖动到它上面时做出响应。
验证放置目标
上面,我们研究了如何确保只有 TreeNode
s 可以被拖动到 TreeView
上。然而,确保 TreeView
中的特定 TreeNode
是拖放操作的有效目标也很重要。因此,我们必须通过处理 DragOver
事件来在游标经过 TreeNode
时验证它。
Public Sub TreeView1_DragOver(ByVal sender As System.Object, _
ByVale As DragEventArgs) _
Handles TreeView1.DragOver
'Check that there is a TreeNode being dragged
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) = False Then Exit Sub
'Get the TreeView raising the event (incase multiple on form)
Dim selectedTreeview As TreeView = CType(sender, TreeView)
'As the mouse moves over nodes, provide feedback to
'the user by highlighting the node that is the
'current drop target
Dim pt As Point = _
CType(sender, TreeView).PointToClient(New Point(e.X, e.Y))
Dim targetNode As TreeNode = selectedTreeView.GetNodeAt(pt)
'See if the targetNode is currently selected,
'if so no need to validate again
If Not (selectedTreeview.SelectedNode Is targetNode) Then
'Select the node currently under the cursor
selectedTreeview.SelectedNode = targetNode
'Check that the selected node is not the dropNode and
'also that it is not a child of the dropNode and
'therefore an invalid target
Dim dropNode As TreeNode = _
CType(e.Data.GetData("System.Windows.Forms.TreeNode"), _
TreeNode)
Do Until targetNode Is Nothing
If targetNode Is dropNode Then
e.Effect = DragDropEffects.None
Exit Sub
End If
targetNode = targetNode.Parent
Loop
End If
'Currently selected node is a suitable target
e.Effect = DragDropEffects.Move
End If
End Sub
首先,我们检查拖放操作中是否存在 TreeNode
对象。如果不存在,我们就不会继续进行;没有必要改变效果,因为 TreeView
已经在 DragEnter
事件处理程序中处理了。
下一个阶段是获取触发事件的 TreeView
(如果此方法处理单个窗体上的多个 TreeView
控件,这一点很重要),并使用 GetNodeAt
函数找出当前在游标下的 TreeNode
。最后,我们必须确保在游标下的 TreeNode
不是正在被拖动的 TreeNode
或其子节点。这一点很重要;如果我们尝试将一个 TreeNode
放置到它自身或其子节点上,那么该节点及其同级节点将全部消失。
如果游标下的 TreeNode
是一个有效的放置目标,我们就通过设置 Move
效果来向用户提供反馈。
执行放置
最后,我们需要放置 TreeNode
来完成操作。这可以通过处理 TreeView
触发的 DragDrop
事件来实现。
Public Sub TreeView1_DragDrop(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragDrop
'Check that there is a TreeNode being dragged
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) = False Then Exit Sub
'Get the TreeView raising the event (incase multiple on form)
Dim selectedTreeview As TreeView = CType(sender, TreeView)
'Get the TreeNode being dragged
Dim dropNode As TreeNode = _
CType(e.Data.GetData("System.Windows.Forms.TreeNode"), _
TreeNode)
'The target node should be selected from the DragOver event
Dim targetNode As TreeNode = selectedTreeview.SelectedNode
'Remove the drop node from its current location
dropNode.Remove()
'If there is no targetNode add dropNode to the bottom of
'the TreeView root nodes, otherwise add it to the end of
'the dropNode child nodes
If targetNode Is Nothing Then
selectedTreeview.Nodes.Add(dropNode)
Else
targetNode.Nodes.Add(dropNode)
End If
'Ensure the newley created node is visible to
'the user and select it
dropNode.EnsureVisible()
selectedTreeview.SelectedNode = dropNode
End Sub
如上所述,我们首先检查放置数据中是否存在 TreeNode
,并获取触发事件的 TreeView
(以防方法处理多个 TreeView
)。然后,我们定位将成为放置目标的节点,并从其原始位置删除正在移动的节点。
如果不存在目标节点,我们就假定目标是 TreeView
本身,因此将节点添加到 TreeView.Nodes
集合的末尾。否则,我们将节点添加到目标 TreeNode.Nodes
集合的末尾。
最后,通过调用 EnsureVisible
方法,我们将展开刚刚添加的节点的**所有**父节点,确保节点不会被隐藏。选择放置的节点将在操作完成后通过高亮显示来向用户提供反馈。
支持多个 TreeView
本文中的代码旨在允许多个 TreeView
控件在窗体上支持拖放操作。要添加对多个 TreeView
控件的支持,请完成以下步骤:
- 将
TreeView
控件添加到窗体 - 将其
AllowDrop
属性设置为True
- 修改上述每个方法以处理新
TreeView
的相关事件,例如:
Public Sub TreeView1_DragDrop(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragDrop, TreeView2.DragDrop
可以通过这种方式向窗体添加任意数量的额外 TreeView
控件。
结论
我希望本文能提供一个有趣的拖放操作入门。本文应用的逻辑也可以应用于其他控件,从而实现一个完全支持拖放的用户界面。
相关文章
如果您觉得这篇文章很有趣,您可能还对其他关于 TreeView
控件的入门文章感兴趣: