Implementing Drag and Drop in Windows Forms
by Wei-Meng Lee11/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 ApplicationThe 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
MouseDownevent is probably a good starting point to load the data that is going to be dragged. -
The
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 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
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 11 of 11.
-
How to tell if object dragged was received or not?
2007-01-19 07:04:11 brenth [Reply | View]
How do you know if the dragged object was received? The QueryContinueDrag event returns DragAction.Drop whether the object was dropped on a good place (where the drop cursor is showing) or a bad place (where the cursor is the circle with the line through it). How can I tell which case it was?
Thanks
Brent
-
Items disappear when dragging, even if not dropped anywhere
2003-09-10 18:03:10 anonymous2 [Reply | View]
Unfortunately, I sometimes drag items, and decide not to drop them, letting go of the mouse button in a place that doesn't accept the item.
The item is still removed the from the first list box. How can this be prevented? -
Items disappear when dragging, even if not dropped anywhere
2004-10-14 12:38:44 sdx [Reply | View]
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Public NotInheritable Class Form1
Inherits System.Windows.Forms.Form
Friend WithEvents ListDragSource As System.Windows.Forms.ListBox
Friend WithEvents ListDragTarget As System.Windows.Forms.ListBox
Friend WithEvents UseCustomCursorsCheck As System.Windows.Forms.CheckBox
Friend WithEvents DropLocationLabel As System.Windows.Forms.Label
Private indexOfItemUnderMouseToDrag As Integer
Private indexOfItemUnderMouseToDrop As Integer
Private dragBoxFromMouseDown As Rectangle
Private screenOffset As Point
Private MyNoDropCursor As Cursor
Private MyNormalCursor As Cursor
<System.STAThread()> _
Public Shared Sub Main()
System.Windows.Forms.Application.Run(New Form1)
End Sub 'Main
Public Sub New()
MyBase.New()
Me.ListDragSource = New System.Windows.Forms.ListBox
Me.ListDragTarget = New System.Windows.Forms.ListBox
Me.UseCustomCursorsCheck = New System.Windows.Forms.CheckBox
Me.DropLocationLabel = New System.Windows.Forms.Label
Me.SuspendLayout()
' ListDragSource
Me.ListDragSource.Items.AddRange(New Object() {"one", "two", "three", "four", _
"five", "six", "seven", "eight", _
"nine", "ten"})
Me.ListDragSource.Location = New System.Drawing.Point(10, 17)
Me.ListDragSource.Size = New System.Drawing.Size(120, 225)
' ListDragTarget
Me.ListDragTarget.AllowDrop = True
Me.ListDragTarget.Location = New System.Drawing.Point(154, 17)
Me.ListDragTarget.Size = New System.Drawing.Size(120, 225)
' UseCustomCursorsCheck
Me.UseCustomCursorsCheck.Location = New System.Drawing.Point(10, 243)
Me.UseCustomCursorsCheck.Size = New System.Drawing.Size(137, 24)
Me.UseCustomCursorsCheck.Text = "Use Custom Cursors"
' DropLocationLabel
Me.DropLocationLabel.Location = New System.Drawing.Point(154, 245)
Me.DropLocationLabel.Size = New System.Drawing.Size(137, 24)
Me.DropLocationLabel.Text = "None"
' Form1
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 270)
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.ListDragSource, _
Me.ListDragTarget, Me.UseCustomCursorsCheck, _
Me.DropLocationLabel})
Me.Text = "Drag and Drop Example"
Me.ResumeLayout(False)
End Sub
Private Sub ListDragSource_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles ListDragSource.MouseDown
' Get the index of the item the mouse is below.
indexOfItemUnderMouseToDrag = ListDragSource.IndexFromPoint(e.X, e.Y)
If (indexOfItemUnderMouseToDrag <> ListBox.NoMatches) Then
' Remember the point where the mouse down occurred. The DragSize indicates
' the size that the mouse can move before a drag event should be started.
Dim dragSize As Size = SystemInformation.DragSize
' Create a rectangle using the DragSize, with the mouse position being
' at the center of the rectangle.
dragBoxFromMouseDown = New Rectangle(New Point(e.X - (dragSize.Width / 2), _
e.Y - (dragSize.Height / 2)), dragSize)
Else
' Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty
End If
End Sub
Private Sub ListDragSource_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles ListDragSource.MouseUp
' Reset the drag rectangle when the mouse button is raised.
dragBoxFromMouseDown = Rectangle.Empty
End Sub
Private Sub ListDragSource_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles ListDragSource.MouseMove
If ((e.Button And MouseButtons.Left) = MouseButtons.Left) Then
' If the mouse moves outside the rectangle, start the drag.
If (Rectangle.op_Inequality(dragBoxFromMouseDown, Rectangle.Empty) And _
Not dragBoxFromMouseDown.Contains(e.X, e.Y)) Then
' Creates custom cursors for the drag-and-drop operation.
Try
MyNormalCursor = New Cursor("3dwarro.cur")
MyNoDropCursor = New Cursor("3dwno.cur")
Catch
' An error occurred while attempting to load the cursors so use
' standard cursors.
UseCustomCursorsCheck.Checked = False
Finally
' The screenOffset is used to account for any desktop bands
' that may be at the top or left side of the screen when
' determining when to cancel the drag drop operation.
screenOffset = SystemInformation.WorkingArea.Location
' Proceed with the drag and drop, passing in the list item.
Dim dropEffect As DragDropEffects = ListDragSource.DoDragDrop(ListDragSource.Items(indexOfItemUnderMouseToDrag), _
DragDropEffects.All Or DragDropEffects.Link)
' If the drag operation was a move then remove the item.
If (dropEffect = DragDropEffects.Move) Then
ListDragSource.Items.RemoveAt(indexOfItemUnderMouseToDrag)
' Select the previous item in the list as long as the list has an item.
If (indexOfItemUnderMouseToDrag > 0) Then
ListDragSource.SelectedIndex = indexOfItemUnderMouseToDrag - 1
ElseIf (ListDragSource.Items.Count > 0) Then
' Selects the first item.
ListDragSource.SelectedIndex = 0
End If
End If
' Dispose the cursors since they are no longer needed.
If (Not MyNormalCursor Is Nothing) Then _
MyNormalCursor.Dispose()
If (Not MyNoDropCursor Is Nothing) Then _
MyNoDropCursor.Dispose()
End Try
End If
End If
End Sub
Private Sub ListDragSource_GiveFeedback(ByVal sender As Object, ByVal e As GiveFeedbackEventArgs) Handles ListDragSource.GiveFeedback
' Use custom cursors if the check box is checked.
If (UseCustomCursorsCheck.Checked) Then
' Set the custom cursor based upon the effect.
e.UseDefaultCursors = False
If ((e.Effect And DragDropEffects.Move) = DragDropEffects.Move) Then
Cursor.Current = MyNormalCursor
Else
Cursor.Current = MyNoDropCursor
End If
End If
End Sub
Private Sub ListDragTarget_DragOver(ByVal sender As Object, ByVal e As DragEventArgs) Handles ListDragTarget.DragOver
' Determine whether string data exists in the drop data. If not, then
' the drop effect reflects that the drop cannot occur.
If Not (e.Data.GetDataPresent(GetType(System.String))) Then
e.Effect = DragDropEffects.None
DropLocationLabel.Text = "None - no string data."
Return
End If
' Set the effect based upon the KeyState.
If ((e.KeyState And (8 + 32)) = (8 + 32) And _
(e.AllowedEffect And DragDropEffects.Link) = DragDropEffects.Link) Then
' KeyState 8 + 32 = CTL + ALT
' Link drag and drop effect.
e.Effect = DragDropEffects.Link
ElseIf ((e.KeyState And 32) = 32 And _
(e.AllowedEffect And DragDropEffects.Link) = DragDropEffects.Link) Then
' ALT KeyState for link.
e.Effect = DragDropEffects.Link
ElseIf ((e.KeyState And 4) = 4 And _
(e.AllowedEffect And DragDropEffects.Move) = DragDropEffects.Move) Then
' SHIFT KeyState for move.
e.Effect = DragDropEffects.Move
ElseIf ((e.KeyState And 8) = 8 And _
(e.AllowedEffect And DragDropEffects.Copy) = DragDropEffects.Copy) Then
' CTL KeyState for copy.
e.Effect = DragDropEffects.Copy
ElseIf ((e.AllowedEffect And DragDropEffects.Move) = DragDropEffects.Move) Then
' By default, the drop action should be move, if allowed.
e.Effect = DragDropEffects.Move
Else
e.Effect = DragDropEffects.None
End If
' Gets the index of the item the mouse is below.
' The mouse locations are relative to the screen, so they must be
' converted to client coordinates.
indexOfItemUnderMouseToDrop = _
ListDragTarget.IndexFromPoint(ListDragTarget.PointToClient(New Point(e.X, e.Y)))
' Updates the label text.
If (indexOfItemUnderMouseToDrop <> ListBox.NoMatches) Then
DropLocationLabel.Text = "Drops before item #" & (indexOfItemUnderMouseToDrop + 1)
Else
DropLocationLabel.Text = "Drops at the end."
End If
End Sub
Private Sub ListDragTarget_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles ListDragTarget.DragDrop
' Ensures that the list item index is contained in the data.
If (e.Data.GetDataPresent(GetType(System.String))) Then
Dim item As Object = CType(e.Data.GetData(GetType(System.String)), System.Object)
' Perform drag and drop, depending upon the effect.
If (e.Effect = DragDropEffects.Copy Or _
e.Effect = DragDropEffects.Move) Then
' Insert the item.
If (indexOfItemUnderMouseToDrop <> ListBox.NoMatches) Then
ListDragTarget.Items.Insert(indexOfItemUnderMouseToDrop, item)
Else
ListDragTarget.Items.Add(item)
End If
End If
' Reset the label text.
DropLocationLabel.Text = "None"
End If
End Sub
Private Sub ListDragSource_QueryContinueDrag(ByVal sender As Object, ByVal e As QueryContinueDragEventArgs) Handles ListDragSource.QueryContinueDrag
' Cancel the drag if the mouse moves off the form.
Dim lb As ListBox = CType(sender, System.Windows.Forms.ListBox)
If Not (lb Is Nothing) Then
Dim f As Form = lb.FindForm()
' Cancel the drag if the mouse moves off the form. The screenOffset
' takes into account any desktop bands that may be at the top or left
' side of the screen.
If (((Control.MousePosition.X - screenOffset.X) < f.DesktopBounds.Left) Or _
((Control.MousePosition.X - screenOffset.X) > f.DesktopBounds.Right) Or _
((Control.MousePosition.Y - screenOffset.Y) < f.DesktopBounds.Top) Or _
((Control.MousePosition.Y - screenOffset.Y) > f.DesktopBounds.Bottom)) Then
e.Action = DragAction.Cancel
End If
End If
End Sub
Private Sub ListDragTarget_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs) Handles ListDragTarget.DragEnter
' Reset the label text.
DropLocationLabel.Text = "None"
End Sub
Private Sub ListDragTarget_DragLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListDragTarget.DragLeave
' Reset the label text.
DropLocationLabel.Text = "None"
End Sub
End Class
-
Drag Drop Not mentioned
2003-03-11 19:39:57 jblack1 [Reply | View]
from jblack98765@hotmail.com:
What you dont mention in the article is that this visual basic code only works for .net applications, and it only accepts dropped objects from another .net application. You cannot accept objects dropped by another kind of application cappable of dragging
-
How to declare CtrlMask?
2003-01-22 20:59:05 anonymous2 [Reply | View]
I cut and pasted this code but the VS.NET didn't like the CtrlMask. How do you declare it?
-
sliding selected item
2002-12-20 08:50:32 anonymous2 [Reply | View]
I am finding that if you mousedown over an item and then move the mouse up and down the list box, then other iotems get highlighted instead which is then at odds with the data that has been put into the drag area, This means that the dropped item actually recieves the first one that you mouse downed over but not the one currently highlighted?











InitializeComponent();
this.richtextbox1.DragEnter += new System.Windows.Forms.DragEventHandler(this.richtextbox1_DragEnter);
this.richtextbox1.DragDrop += new System.Windows.Forms.DragEventHandler(this.richtextbox1_DragDrop);
Then
Event
private void Listbox1_MouseDown(object sender, MouseEventArgs e)
{
Listbox1.Refresh();
Listbox1.DoDragDrop(Listbox1.SelectedItem, DragDropEffects.Copy);
richtextbox1.DoDragDrop(Listbox1.SelectedItem, DragDropEffects.Copy);
}
private void richtextbox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
int i;
String s;
// Get start position to drop the text.
i = richtextbox1.SelectionStart;
s = richtextbox1.Text.Substring(i);
//richtextbox1.Text = richtextbox1.Text.Substring(0, i);
//// Drop the text on to the RichTextBox.
//richtextbox1.Text = richtextbox1.Text + e.Data.GetData(DataFormats.Text).ToString();
//richtextbox1.Text = richtextbox1.Text + s;
}
private void richtextbox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Text))
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}
private void lstItemsFC_MouseDown(object sender, MouseEventArgs e)
{
lstItemsFC.Refresh();
lstItemsFC.DoDragDrop(lstItemsFC.SelectedItem, DragDropEffects.Copy);
richtextbox1.DoDragDrop(lstItemsFC.SelectedItem, DragDropEffects.Copy);
}
By,
Samdoss (Software Developer)