Click HERE to download a sample chunk of code that moves text from buttons and textboxes, and moves pictures to pictureboxes and includes displaying 'move' icons
Windows users fall into two general categories: those who prefer to use the keyboard and those who prefer to use the mouse. Programmers have been taught to look after the needs of keyboard users by providing access keys (the underlined letter in a command or menu) and shortcuts (such as a CTRL + letter combination), but the needs of mouse users have largely been ignored. Programmers tend to primarily be keyboard users, so the emphasis on keyboard-oriented features is understandable, but every programmer should consider providing mouse support as well.
One thing that mouse users expect is the ability to drag and drop. If you look at most major applications or at Windows itself, drag and drop is everywhere. For example, users are accustomed to dragging and dropping files in the Windows Explorer and to dragging and dropping text in Microsoft Word.
Despite these expectations, few Visual Basic programmers provide drag-and-drop capability in their applications — most likely because implementing drag and drop appears to be much more difficult than it actually is. We will demonstrate how easy drag and drop really is, with examples showing how to move text, pictures, and files within forms, between forms, and even between applications.
Drag and drop is actually the same as cutting and pasting (or copying and pasting) using the mouse instead of the keyboard. In both cases you have a source (where you are cutting or copying from) and a target (where you are pasting to). During either operation, a copy of the data is maintained in memory. Cut and paste uses the Clipboard; drag and drop uses a DataObject object, which is in essence a private clipboard.
Here is the sequence of events in a typical drag-and-drop operation:
The DoDragDrop method takes two parameters:
A new DataObject object is automatically created.
For most simple cases, you can enable dragging with a single line of code, and you can enable dropping with just a few more lines of code.
A simple scenario where drag and drop is useful involves copying text from one TextBox control to another. Typically keyboard commands are available in this scenario as well (select the source TextBox, press CTRL + C, select the target TextBox, press CTRL + V), but drag and drop is more efficient because it only requires a single motion (select and drag).
To enable drag and drop for text
Private MouseIsDown As Boolean = False
Private Sub TextBox1_MouseDown(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseDown
' Set a flag to show that the mouse is down.
MouseIsDown = True
End Sub
Private Sub TextBox1_MouseMove(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseMove
If MouseIsDown Then
' Initiate dragging.
TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy)
End If
MouseIsDown = False
End Sub
Private Sub TextBox2_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles TextBox2.DragEnter
' Check the format of the data being dropped.
If (e.Data.GetDataPresent(DataFormats.Text)) Then
' Display the copy cursor.
e.Effect = DragDropEffects.Copy
Else
' Display the no-drop cursor.
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub TextBox2_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop
' Paste the text.
TextBox2.Text = e.Data.GetData(DataFormats.Text)
End Sub
In the above example, the MouseDown event is used to set a flag showing that the mouse is down, and then the DoDragDrop method is called in the MouseMove event. Although you could initiate the drag in the MouseDown event, doing so would create undesirable behavior: Every time a user clicks the control, the no-drag cursor would be displayed.
The DoDragDrop method takes two parameters:
Also in the MouseMove event the MouseIsDown flag is set to False. Although unnecessary in this example, if you had multiple controls that support dragging you could get a run-time exception.
In the DragEnter event, the GetDataPresent method checks the format of the data being dragged. In this case it is text, so the Effect property is set to Copy, which in turn displays the copy cursor.
In the DragDrop event, the GetData method is used to retrieve the text from the DataObject and assign it to the target TextBox.
The next section provides an example of dragging a different type of data and providing support for both cutting and copying.
Although not as common as dragging and dropping text, dragging and dropping pictures is useful for many applications. There really is not much difference between the two — it is just a different type of data.
There are many cases where a user wants to choose between copying or moving data in a drag-and-drop operation. The Windows user interface guidelines dictate that dragging moves an item and holding down the CTRL key while dragging copies an item.
To enable drag and drop for a picture
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
' Enable dropping.
PictureBox2.AllowDrop = True
End Sub
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
If Not PictureBox1.Image Is Nothing Then
' Set a flag to show that the mouse is down.
m_MouseIsDown = True
End If
End Sub
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If m_MouseIsDown Then
' Initiate dragging and allow either copy or move.
PictureBox1.DoDragDrop(PictureBox1.Image, DragDropEffects.Copy Or _
DragDropEffects.Move)
End If
m_MouseIsDown = False
End Sub
Private Sub PictureBox2_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox2.DragEnter
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
' Check for the CTRL key.
If e.KeyState = 9 Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub PictureBox2_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox2.DragDrop
' Assign the image to the PictureBox.
PictureBox2.Image = e.Data.GetData(DataFormats.Bitmap)
' If the CTRL key is not pressed, delete the source picture.
If Not e.KeyState = 8 Then
PictureBox1.Image = Nothing
End If
End Sub
In the above example, note that the AllowDrop property for the second PictureBox control is set in the Form1_Load event. This is necessary because the AllowDrop property is not available at design time.
In the MouseDown event, the code first checks to make sure that there is an image assigned to the PictureBox; otherwise, after you moved the picture, subsequent clicks would raise an exception.
Aside: a little bit of code to illustrate how to use a drag icon with the Picturebox example:
Private Sub PictureBox1_GiveFeedback(ByVal sender As Object, ByVal e As _ System.Windows.Forms.GiveFeedbackEventArgs) Handles PictureBox1.GiveFeedback
' Set the custom cursor based upon the effect.' we'll use our own drag cursors ...
e.UseDefaultCursors = False' this could use any of the dragdropeffects, e.g. DragDropEffects.Copy
If ((e.Effect And DragDropEffects.Move) = DragDropEffects.Move) Then
Cursor.Current = New Cursor("happy.ico")
'Current = MyNormalCursor
Else
Cursor.Current = New Cursor("unhappy.ico")
'Current = MyNoDropCursor
End If' the icon files should be in the same directory as the .exe file
End Sub
Also note that in both the DragEnter and DragDrop events the code checks to see if the CTRL key is pressed to determine whether to copy or move the picture. Why are the values different? In the DragEnter event, the left mouse button is down, resulting in a value of 8 for the CTRL key plus 1 for the left mouse button. For a list of KeyState enumerations, see DragEventArgs.KeyState Property.
Both examples so far have dealt with dragging between two controls on the same form; they would also work for dragging items between controls on different forms within an application. The next example demonstrates accepting items dropped from another application — in this case, files that are dragged from Windows Explorer.
Drag and drop is used pervasively in Windows for moving or copying files. Windows Explorer fully supports drag and drop, and for many users this is the preferred method of working with files. In addition, many users are accustomed to dropping files onto an application to open them — for example, dragging and dropping a .doc file onto Microsoft Word.
In this example drag and drop is used to populate a ListBox control with a list of files dragged from Windows Explorer.
To enable drag and drop for a file
Private Sub ListBox1_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.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 _
System.Windows.Forms.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
You may notice that in the DragEnter event the Effect is set to DragDropEffects.All. Because the files themselves are not actually being moved or copied, it does not really matter which AllowedEffects were set by the source, so specifying All means that dropping is enabled for any FileDrop.
In the above example the FileDrop format contains the full path for each file being dropped. Rather than populating a list, you could just as easily perform other operations on the files — for example, opening them in MDI (multiple-document interface) document windows.
The next example demonstrates a special case for drag and drop: dragging items back and forth between two lists.
Another common scenario where drag and drop is expected (and highly appreciated) involves moving items from one list of items to another. Typically buttons are also available in this scenario, but they require two mouse clicks (select the item, click the button). Drag and drop is more efficient here, because it only requires a single motion (select and drag).
To move multiple items back and forth between two lists
Private Sub ListView_ItemDrag(ByVal sender As Object, ByVal e As _
System.Windows.Forms.ItemDragEventArgs) Handles ListView1.ItemDrag, _
ListView2.ItemDrag
Dim myItem As ListViewItem
Dim myItems(sender.SelectedItems.Count - 1) As ListViewItem
Dim i As Integer = 0
' Loop though the SelectedItems collection for the source.
For Each myItem In sender.SelectedItems
' Add the ListViewItem to the array of ListViewItems.
myItems(i) = myItem
i = i + 1
Next
' Create a DataObject containg the array of ListViewItems.
sender.DoDragDrop(New _
DataObject("System.Windows.Forms.ListViewItem()", myItems), _
DragDropEffects.Move)
End Sub
Private Sub ListView_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListView1.DragEnter, _
ListView2.DragEnter
' Check for the custom DataFormat ListViewItem array.
If e.Data.GetDataPresent("System.Windows.Forms.ListViewItem()") Then
e.Effect = DragDropEffects.Move
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub ListView_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListView1.DragDrop, _
ListView2.DragDrop
Dim myItem As ListViewItem
Dim myItems() As ListViewItem = _ e.Data.GetData("System.Windows.Forms.ListViewItem()")
Dim i As Integer = 0
For Each myItem In myItems
' Add the item to the target list.
sender.Items.Add(myItems(i).Text)
' Remove the item from the source list.
If sender Is ListView1 Then
ListView2.Items.Remove(ListView2.SelectedItems.Item(0))
Else
ListView1.Items.Remove(ListView1.SelectedItems.Item(0))
End If
i = i + 1
Next
End Sub
You might wonder why this example uses ListView controls rather than ListBox controls. There is a good reason: The ListBox control does not support dragging multiple items. Clicking the list invalidates the multiple selection.
The ListView and TreeView controls have an ItemDrag event that facilitates dragging. In the above example, a single ItemDrag event handler covers both controls; they are listed in the Handles clause. The sender parameter represents whichever control is initiating the drag.
Because the DataFormats class does not include a member of type ListViewItem, the data must be passed as a system Type instead. The ItemDrag code creates an array of the type ListViewItem and populates it by looping through the SelectedItems collection. In the DoDragDrop method, a new DataObject is created and populated with the array. This same technique can be used to drag and drop any system Type.
In the DragDrop event, the array is copied from the DataObject into a new ListViewItem array, and each ListViewItem is added to the Items collection of the target ListView control.
As you can see from these examples, adding drag-and-drop capabilities is not terribly difficult. When you understand the basic techniques, you can add your own customized drag-and-drop code to your applications. Most developers have application standards for such things as access and shortcut keys. Consider adding drag and drop to those standards.
edited & modified from an original article by Steve Hoag of the Microsoft VB.NET Team by DrB
Posted by DrB on Thursday April 15, 2010