Implementing Drag and Drop in Windows Formsby Wei-Meng Lee
One of the benefits of using a windowing system is the ability to drag and drop objects from one window to another. Such is the functionality that we have taken for granted when using Microsoft Windows. Though it seems such a trivial task, not much has been written about how to implement drag and drop in your Windows application. In this article I will discuss how you can use Windows forms using the .NET Framework to develop applications that support drag and drop operations.Our Sample Application
The sample application that I will develop in this article works very much like the Clipboard Ring in Visual Studio .NET. The Clipboard Ring is basically a clipboard that stores all of the text that you have copied or cut. It is a great tool for temporarily moving segments of codes. In my application, I will develop a Windows application that allows text to be copied or cut from any application (that supports Windows drag and drop functionality). You can then drag the text from the clipboard and drop it onto your application.
|Figure 1. The Clipboard Ring in Visual Studio .NET|
|Figure 2. Our sample application|
In addition, my application will also allow you to drop bitmap images onto a PictureBox control and from there, you can save the image to disk in a variety of image formats such as BMP, TIFF, JPG, and GIF.
The Gory Details
There are a few events that you need to know when handling drag and drop operations:
|Figure 3. Dragging from one control and dropping it onto another control|
On the control to be dragged (Control 1)
MouseDownevent is probably a good starting point to load the data that is going to be dragged.
QueryContinueDragevent allows you to know the outcome of the drag operation; i.e., whether the item is eventually dropped or not.
On the control to be dropped (Control 2)
When the mouse enters the control to be dropped, the
DragEnterevent is fired.
When the mouse hovers over the control to be dropped, the
DragOverevent is fired.
When the mouse leaves the control to be dropped, the
DragLeaveevent is fired.
When the mouse drops over the control to be dropped, the
DragDropevent is fired.
With reference to our Windows application, let's work on the Clipboard Ring first.
The Clipboard Ring
Our Clipboard Ring allows text to be dropped on it, as well as text to be
dragged from it and dropped elsewhere. I have used a
ListBox control to
simulate the Clipboard Ring, as I couldn't find a control that it similar to the
one used by Visual Studio .NET. The first thing you need to do is to set the
property of the
ListBox control to true. This will allow items to be dropped
on it and it can then respond with the necessary actions.
Next, you need to service the
DragEnter event, which is fired when the
mouse tried to drag an item over it:
Private Sub ListBox1_DragEnter(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles ListBox1.DragEnter ' if the data is Text, set the DragDropEffects ' accordingly If (e.Data.GetDataPresent(DataFormats.Text)) Then If (e.KeyState And CtrlMask) = CtrlMask Then e.Effect = DragDropEffects.Copy Else e.Effect = DragDropEffects.Move End If Else e.Effect = DragDropEffects.None End If End Sub
You use the
GetDataPresent() method from the
to check the format of the item that you are trying to accept from the drop
operation. If it is text, then you are ready to accept it. The next thing you
check is whether the user is performing a copy or move. Typically, in Windows,
users perform a copy operation by dragging and dropping with the Control key
pressed. To check for this special keystroke, I used the
together with a Control Mask (defined as a byte with a value of 8). If the
control key is pressed, I will set the relevant
Figure 4 shows the different mouse icons when moving and copying text,
|Figure 4. The different mouse icons when moving and copying text, respectively|
When the user releases the mouse button, an item is added to the
Private Sub ListBox1_DragDrop(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles ListBox1.DragDrop ' Adds the text to the ListBox control ListBox1.Items.Add _ (e.Data.GetData(DataFormats.Text).ToString) End Sub
With this done, you can now drag and drop texts over the
ListBox control! Now,
we also want to be able to drag an item from the
ListBox control and drop the
text onto other applications, and so we need to service two other events:
MouseDown event is fired when the user clicks on the
control. This is where we want to set the text to be dragged using the
method of the
Private Sub ListBox1_MouseDown(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles ListBox1.MouseDown ' If none selected, exit If ListBox1.SelectedIndex < 0 Then Return ' Otherwise, do the Drag-and-Drop ListBox1.DoDragDrop(ListBox1.Items _ (ListBox1.SelectedIndex).ToString, _ DragDropEffects.Copy _ Or DragDropEffects.Move) End Sub
QueryContinueDrag> event is constantly fired during the dragging
operation. This is the best place to check if the operation is successful and
how you should react to it. In our case, if the item is moved (by dragging and
dropping without pressing the Control key), the item must be removed from the
Private Sub ListBox1_QueryContinueDrag _ (ByVal sender As Object, _ ByVal e As System.Windows.Forms.QueryContinueDragEventArgs) _ Handles ListBox1.QueryContinueDrag If e.Action = DragAction.Drop Then If (e.KeyState And CtrlMask) <> CtrlMask Then ' a move operation ListBox1.Items.RemoveAt(ListBox1.SelectedIndex) End If End If End Sub
Once this step is done, your Clipboard Ring is complete. Note that in our case, I am dragging and dropping between two applications -- the Clipboard Ring and another Windows application. In fact, there is no difference in dragging and dropping between controls (in an application) and multiple applications.
Dragging and Dropping Images
Besides text, you can also drag and drop images. The steps are similar to those of dragging and dropping text. For my sample application, I am going to allow bitmap images to be dropped onto a PictureBox control and from there you can save the image in GIF, TIFF, BMP, or JPG format.
For the PictureBox control, instead of setting the property
AllowDrop in the
property window, you have to set it at the code level. Typically, you
would set it in the
PictureBox1.AllowDrop = True
Similar to the
ListBox control, I have to service the
Private Sub PictureBox1_DragEnter(ByVal sender As _ Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles PictureBox1.DragEnter ' If the data is Bitmap, set the DragDropEffects ' accordingly If (e.Data.GetDataPresent(DataFormats.Bitmap)) Then If (e.KeyState And CtrlMask) = CtrlMask Then e.Effect = DragDropEffects.Copy Else e.Effect = DragDropEffects.Move End If Else e.Effect = DragDropEffects.None End If End Sub Private Sub PictureBox1_DragDrop(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) _ Handles PictureBox1.DragDrop ' Displays the copied/moved image PictureBox1.Image = e.Data.GetData(DataFormats.Bitmap) End Sub
|Figure 5. Saving the dropped image|
When the Save... button is clicked, save the image using the
Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles Button1.Click ' Displays a SaveFileDialog so the user can save ' the Image Dim saveFileDialog1 As New SaveFileDialog saveFileDialog1.Filter = "Jpeg Image|*.jpg|" & "Bitmap Image|*.bmp|" & "Gif Image|*.gif|" & "Tiff Image|*.tiff" saveFileDialog1.Title = "Save an Image File" saveFileDialog1.ShowDialog() ' If the file name is not an empty string open it for ' saving. If saveFileDialog1.FileName <> "" Then ' Saves the Image via a FileStream created by the ' OpenFile method. Dim fs As System.IO.FileStream = CType _ (saveFileDialog1.OpenFile(), System.IO.FileStream) ' Saves the Image in the appropriate ImageFormat ' based upon the file type selected in the dialog box. Select Case saveFileDialog1.FilterIndex Case 1 PictureBox1.Image.Save(fs, _ System.Drawing.Imaging.ImageFormat.Jpeg) Case 2 PictureBox1.Image.Save(fs, _ System.Drawing.Imaging.ImageFormat.Bmp) Case 3 PictureBox1.Image.Save(fs, _ System.Drawing.Imaging.ImageFormat.Gif) Case 4 PictureBox1.Image.Save(fs, _ System.Drawing.Imaging.ImageFormat.Tiff) End Select fs.Close() End If End Sub
That's simple, isn't it? With some creativity, you can now create compelling Windows application with all of the friendly features that we have come to expect from Windows.
Wei-Meng Lee (Microsoft MVP) http://weimenglee.blogspot.com is a technologist and founder of Developer Learning Solutions http://www.developerlearningsolutions.com, a technology company specializing in hands-on training on the latest Microsoft technologies.
Return to ONDotnet.com