Drag-and-drop in Windows Forms is as easy as 1-2-3 (and maybe 4)

25 November 2019

How to support drag-and-drop in Windows Forms.

Last week I was writing a Windows Forms application that needed to process data that had been dragged onto it.

This sent me down a (small) rabbit hole. Supporting drag and drop was not quite as straightforward as I initially thought!

Here's how to do it, broken down into a few manageable steps.

1. Allow drag-and-drop on the control

Set the AllowDrop property to true on the control which data can be dragged onto. If you don't do this, then the events detailed below won't fire.

This may be the whole form, or perhaps a label saying "drop here".

2. Handle the "enter" event

The DragEnter event fires when data is dragged over the control in question. That is, the mouse enters the control's bounds while the left button is pressed and the user is dragging something.

This event uses DragEventArgs, which has the property AllowedEffect. This is a flags enum containing all the different "effects" (Link, Copy, Move) allowed by the source of the drag. For example, if you drag a file from Windows File Explorer, you're allowed to link or copy but not move.

In the event handler, you need to set the value of DragEventArgs.Effect to one of the allowed values. This does two things:

If you want to make a bigger visual change than just the cursor (e.g. change the appearance of the control), then you should also do that in the DragEnter event handler.

3. Handle the "drop" event

The DragDrop event fires when the user releases the mouse to drop data onto the control.

The data that has been dragged can be found in DragEventArgs.Data. This is an IDataObject, which contains the data in a number of "formats". The exact formats used will depend on where the data was dragged from, so when developing there's a bit of trial and error to see which formats are appropriate for you to use.

Some helpful methods on IDataObject:

As an example, one of the formats when dragging data from Windows File Explorer was "FileName". If you call GetData("FileName") then you get a string[] containing the full paths of the files being dragged.

Once you have the data you want you can then do whatever you like. For example, my application reads in the file, transforms it, and puts the result in a text box.

This event also uses DragEventArgs, and it uses the same instance as the one provided in DragEnter, meaning you can inspect the value you chose for Effect if you need to.

4. Handle the "leave" event (maybe)

The DragLeave event fires when the mouse leave the bounds of the control and the user is still dragging the item.

Handling this event is only necessary if you need to tidy up from the DragEnter event; perhaps you need to reset some visuals to indicate that releasing the mouse won't complete the drag-and-drop operation.

Example

The following example shows you how to accept files dragged from File Explorer. It captures a file dragged onto dragDropLabel and displays the file contents in outputTextBox.

using System.Linq;
using System.Windows.Forms;

private void dragDropLabel_DragEnter(object sender, DragEventArgs e)
{
    if (e.AllowedEffect.HasFlag(DragDropEffects.Copy) &&
        e.Data.GetData("FileName") is string[] fileNames &&
        fileNames.Any())
    {
        e.Effect = DragDropEffects.Copy;
    }
}

private void dragDropLabel_DragDrop(object sender, DragEventArgs e)
{
    var fileNames = (string[])e.Data.GetData("FileName");
    var fileName = fileNames[0];

    this.outputTextBox.Text = File.ReadAllText(fileName);
}

Appendix: exploring the data

While developing, the following snippet was useful for discovering what formats were supported by DragEventArgs.Data and exactly how the data looked.

foreach (var format in e.Data.GetFormats())
{
    var data = e.Data.GetData(format);

    string text;
    if (data is MemoryStream ms)
    {
        using (var sr = new StreamReader(ms))
        {
            text = sr.ReadToEnd();
        }
    }
    else
    {
        text = data?.ToString();
    }

    System.Diagnostics.Debug.WriteLine($"{format}:{text}");
}