Introduction
TreeView
controls are extremely common in applications where it is necessary to present any sort of hierarchy to a user. Usually, the nature of such a hierarchy requires that users have the ability to reorganize it; drag and drop operations providing a natural solution. This article provides an introduction to implementing drag and drop functionality to a TreeView
control.
Getting Started
The code in this article assumes you have a form with a TreeView
control, this needs to be populated with some TreeNode
s (the demo project available at the top of the article provides a method to randomly generate a TreeNode
hierarchy). Ensure that the TreeView
can accept objects dragged onto it by setting the AllowDrop
property to True
.
Initiating the Drag
When the user begins the drag action on a TreeNode
, the ItemDrag
event is fired, we need to handle this:
Public Sub TreeView1_ItemDrag(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.ItemDragEventArgs) _
Handles TreeView1.ItemDrag
DoDragDrop(e.Item, DragDropEffects.Move)
End Sub
Here we are initiating a drag operation by calling the DoDragDrop
method, specifying the TreeNode
object being dragged and specifying the Move
operation the user will perform if the drag drop is completed. Note that the DragDropEffects
enumeration merely describes the cursors (effects) that can be presented during the operation, it will not enforce that the object will ultimately be moved as a result of the successful drag drop operation.
Dragging Over the Control
With the drag operation under way, the TreeView
must now react when an object is dragged over it. When this occurs, the DragEnter
event is fired that we must handle:
Public Sub TreeView1_DragEnter(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragEnter
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) Then
e.Effect = DragDropEffects.Move
Else
e.Effect = DragDropEffects.None
End If
End Sub
All we are doing here is checking if there is an object present in the drag drop operation that we are happy to be dropped onto the TreeView
, in this case a TreeNode
. If a TreeNode
is found, then we specify that the Move
effect will be displayed on the cursor, otherwise we specify that the None
effect will be displayed. Note, you may only select the effect(s) that were specified when the drag drop operation was initiated with the DoDragDrop
method (see Initiating the Drag above).
It is important to check that the object of the type you are expecting is present in the drag drop data (in this case, a TreeNode
). In this example, we only have one control that is initiating a drag drop operation. However, on a form, there may be many other controls calling the DoDragDrop
method; by setting the AllowDrop
property to True
on a control, you are specifying that it will react whenever any object is dragged over it.
Validating the Drop Target
Above, we looked at ensuring only TreeNode
s can be dragged over the TreeView
. However, it is also important to ensure that a particular TreeNode
within the TreeView
is a valid target for the drag drop operation. Therefore, we have to validate the TreeNode
as the cursor passes over it by handling the DragOver
event:
Public Sub TreeView1_DragOver(ByVal sender As System.Object, _
ByVale As DragEventArgs) _
Handles TreeView1.DragOver
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) = False Then Exit Sub
Dim selectedTreeview As TreeView = CType(sender, TreeView)
Dim pt As Point = _
CType(sender, TreeView).PointToClient(New Point(e.X, e.Y))
Dim targetNode As TreeNode = selectedTreeView.GetNodeAt(pt)
If Not (selectedTreeview.SelectedNode Is targetNode) Then
selectedTreeview.SelectedNode = targetNode
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
e.Effect = DragDropEffects.Move
End If
End Sub
Firstly, we are checking that there is a TreeNode
object in the drag drop operation. If there is not, we aren't going any further; there is no need to even change the effect because the TreeView
would have taken care of that during the DragEnter
event handler.
Then next stage is to get the TreeView
that fired the event (important if this method is handling multiple TreeView
controls on a single form), and work out the TreeNode
that is currently under the cursor using the GetNodeAt
function. Finally, we must ensure that the TreeNode
under the cursor is not the TreeNode
that is being dragged or a child of that TreeNode
. This is important; if we attempt to drop a TreeNode
onto itself or a ChildNode
, then the node and its siblings would all just vanish.
Provided the TreeNode
under the cursor is a valid drop target, we provide feedback to the user by setting the Move
effect.
Performing the Drop
Finally, we need to drop the the TreeNode
to complete the operation. This is accomplished by handling the DragDrop
event fired by the TreeView
:
Public Sub TreeView1_DragDrop(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragDrop
If e.Data.GetDataPresent("System.Windows.Forms.TreeNode", _
True) = False Then Exit Sub
Dim selectedTreeview As TreeView = CType(sender, TreeView)
Dim dropNode As TreeNode = _
CType(e.Data.GetData("System.Windows.Forms.TreeNode"), _
TreeNode)
Dim targetNode As TreeNode = selectedTreeview.SelectedNode
dropNode.Remove()
If targetNode Is Nothing Then
selectedTreeview.Nodes.Add(dropNode)
Else
targetNode.Nodes.Add(dropNode)
End If
dropNode.EnsureVisible()
selectedTreeview.SelectedNode = dropNode
End Sub
As above, we firstly check that there is a TreeNode
present in the drop data and get the TreeView
firing the event in case of multiple TreeView
s handled by the method. We then locate the node that will be the target of the drop and remove the node that is being moved from its original location.
If there is no target node, we assume the target to be the TreeView
itself, and therefore add the node at the end of the TreeView.Nodes
collection. Otherwise, we add the node to the end of the target TreeNode.Nodes
collection.
Finally, by calling the EnsureVisible
method, all ParentNode
s of the node we just added will be expanded, making sure that the node is not hidden. Selecting the dropped node will provide feedback to the user by highlighting it once the operation is complete.
Supporting Multiple TreeViews
The code in this article has been designed to allow multiple TreeView
controls to support drag and drop operation on the form. To add support for multiple TreeView
controls, complete the following:
- Add a
TreeView
control to the form
- Set its
AllowDrop
property to True
- Amend each of the methods above to handle the relevant events of the new
TreeView
, for example:
Public Sub TreeView1_DragDrop(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.DragEventArgs) _
Handles TreeView1.DragDrop, TreeView2.DragDrop
Any number of additional TreeView
controls can be added to the form in this way.
Conclusion
I hope this article provides an interesting introduction to Drag and Drop operations. The logic applied in this article can also be applied to other controls allowing a user interface that completely supports Drag and Drop.
Related Articles
If you found this article interesting, you may be interested in other introductory articles relating to the TreeView
control: