Drag-and-drop is everywhere. Everywhere but probably not in your own applications! Possibly that your are thinking that it is too hard to implement or that no-one uses this kind of feature. But if look how your users are working with other applications, you will find them using drag-and-drop intuitively. Why don't you give them this boost of performance?
In this column, I will show you how to implement this feature to drag within your application and even between your application and other existing applications.
Basics
The drag-and-drop feature is very much like the cut-and-paste feature. You take the content of a source to copy it into a target.
The cut-and-paste feature uses the Clipboard during the operation while the drag-and-drop uses a DataObject object (which is nothing more then a private clipboard).
If you ever implemented drag-and-drop with VB6, you surely remember that there were two (2) kinds of dragging: Controls (using the DragMode property) and Contents (using the OLEDragMode property). The method I’ll show you here is the equivalent of the content’s drag-and-drop feature.
First example: Dragging text
This first example will let you drag-and-drop text between 2 textboxes on a form. You first need to add 2 textbox controls to a form. You must set the AllowDrop property of all textbox controls to True to let them receive the drop notification.
Figure 1: The sample application.
You can then copy this first snippet of code that starts the drag operation.
Private Sub TextBox_MouseMove( _
ByVal sender As Object, ByVal e As MouseEventArgs) _
Handles TextBox1.MouseMove, TextBox2.MouseMove
'Nothing to do if no mouse button is pressed.
If e.Button = 0 Then Exit Sub
'Get a reference to the current textbox control
Dim ctlCurrent As TextBox = DirectCast(sender, TextBox)
'Nothing to do if the textbox is empty!
If ctlCurrent.TextLength = 0 Then Exit Sub
'Nothing to do if we are still inside the control's borders.
If e.X >= 0 AndAlso e.X < ctlCurrent.Width AndAlso _
e.Y >= 0 AndAlso e.Y < ctlCurrent.Height Then Exit Sub
'If we get here, it means that the mouse is
' being dragged outside the control.
'Store the text to be drag into a DataObject object
Dim objDO As New DataObject
If ctlCurrent.SelectionLength = 0 Then
objDO.SetData(DataFormats.Text, ctlCurrent.Text)
Else
objDO.SetData(DataFormats.Text, ctlCurrent.SelectedText)
End If
'Starts the drag operation
Dim effect As DragDropEffects = _
DragDropEffects.Copy Or DragDropEffects.Move
effect = ctlCurrent.DoDragDrop(objDO, effect)
'This portion of code will run after the code has been dropped.
'Delete the (selected) text if it was a move.
If effect = DragDropEffects.Move Then
If ctlCurrent.SelectionLength = 0 Then
ctlCurrent.Text = ""
Else
ctlCurrent.SelectedText = ""
End If
End If
End Sub
Be careful to list all the textbox controls you want to handle with this handler by adding them to the Handles clause (see the event declaration). The comments included should be enough to let you know what it is going.
The DragEnter event can be used to change the cursor effect depending on the control that is being entered and the content of the DataObject.
Private Sub TextBox_DragEnter( _
ByVal sender As Object, ByVal e As DragEventArgs) _
Handles TextBox1.DragEnter, TextBox2.DragEnter
'Check the format of the data being dropped.
If e.Data.GetDataPresent(DataFormats.Text, True) Then
'Checks if the CTRL key is held
If CBool(e.KeyState And 8) Then
'Display the copy cursor.
e.Effect = e.AllowedEffect And DragDropEffects.Copy
Else
'Display the move cursor
e.Effect = e.AllowedEffect And DragDropEffects.Move
End If
Else
'Display the no-drop cursor.
e.Effect = DragDropEffects.None
End If
End Sub
Finally, the DragDrop event is used to manage the actual operation. Private Sub TextBox_DragDrop( _
ByVal sender As Object, ByVal e As DragEventArgs) _
Handles TextBox1.DragDrop, TextBox2.DragDrop
'Check if data is present
If Not e.Data.GetDataPresent(DataFormats.Text, True) Then Exit Sub
'Determine if it is a copy or move operation
If CBool(e.KeyState And 8) Then
' Display the copy cursor.
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
'Get a reference to the current textbox control
Dim ctlCurrent As TextBox = DirectCast(sender, TextBox)
'Paste the text to drop
ctlCurrent.SelectedText = e.Data.GetData(DataFormats.Text).ToString
End Sub
I concede that there are many lines here but considering that this code can handles all textboxes of a single form is not that bad! Didn’t VB6 do that alone?
To convince you that you did a good job, try to drag-and-drop text between your textboxes and Word. It should be working as well.
Second example: Dragging a picture
Dragging images between Picture controls is not really different from dragging text between TextBox controls. It is simply a matter of type of data. One strange thing (a bug or a feature – I don’t know) is that the AllowDrop property doesn’t appear into the Properties dialog. You have to set it by code (in the Form_Load event for example) like this:
Private Sub Form1_Load( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
PictureBox1.AllowDrop = True
PictureBox2.AllowDrop = True
End Sub
Because doesn't change a lot from the previous example, I won't copy the complete code here. You can download the source code of this demo using the link at the bottom of this column to see the picture dragging in action.
Third example: Dragging filename from the Windows Explorer
This example will let a listbox receive a filename dragged from the Windows Explorer. Once you have the filename and path in your application, you can do whatever you want it!
To do this, add a ListBox control to your form and don’t forget to set it’s AllowDrop property to True. Then you need this code:
Private Sub ListBox1_DragEnter( _
ByVal sender As Object, ByVal e As DragEventArgs) _
Handles ListBox1.DragEnter
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
e.Effect = DragDropEffects.All
End If
End Sub
Private Sub ListBox1_DragDrop( _
ByVal sender As Object, ByVal e As DragEventArgs) _
Handles ListBox1.DragDrop
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
Dim MyFiles() As String
Dim i As Integer
' Assign the files to an array.
MyFiles = e.Data.GetData(DataFormats.FileDrop)
' Loop through the array and add the files to the list.
For i = 0 To MyFiles.Length - 1
ListBox1.Items.Add(MyFiles(i))
Next
End If
End Sub
When you look at the code, you see that it is, once again, much the same! Checking if the data is present in the appropriate format and retrieve it.
Other examples: ListViews & TreeViews
The downloadable demo also includes code that implements the drag-and-drop feature between 2 ListViews and between 2 TreeViews.
The main difference with these controls is that they raise the ItemDrag event as soon as the user starts to drag a node. If you look at the code, you will also discover the there are no values into the DataFormats enum that fits nodes. For this reason, we use overloaded GetDataPresent and GetData methods passing string to see what’s being dragged.
Conclusion
A bit of code to write and understand but it gives you great features. You surely some places where drag-and-drop would ease user’s experience.
I hope you appreciated the topic and see you next month.