WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

Implementing Drag and Drop in Windows Forms

by Wei-Meng Lee
11/11/2002

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)

  • The MouseDown event is probably a good starting point to load the data that is going to be dragged.
  • The QueryContinueDrag event 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 DragEnter event is fired.
  • When the mouse hovers over the control to be dropped, the DragOver event is fired.
  • When the mouse leaves the control to be dropped, the DragLeave event is fired.
  • When the mouse drops over the control to be dropped, the DragDrop event 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 AllowDrop 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 DragEventArgs argument 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 KeyState property 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 DragDropEffects property. Figure 4 shows the different mouse icons when moving and copying text, respectively.

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 ListBox control:


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 and QueryContinueDrag.

The MouseDown event is fired when the user clicks on the ListBox control. This is where we want to set the text to be dragged using the DoDragDrop() method of the ListBox control:


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

The 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 ListBox control.


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 Form_Load event:


PictureBox1.AllowDrop = True

Similar to the ListBox control, I have to service the DragEnter and DragDrop events:


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 FileStream class.


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