Search code examples
.netvb.netwinformsdrag-and-drop

Performing a Drag-and-Drop operation like Windows Explorer


I have a Application that can Drag n Drop SVG Files. This is a Tool for Applications like Adobe XD (which support SVG Files). To perform a basic drag n drop operation is really simple.

Dim dataObj As New DataObject(DataFormats.FileDrop, New String() {_svgPath})
PictureBox.DoDragDrop(dataObj, DragDropEffects.Copy)

And it works! If I want to drag n drop a file to the explorer then everything works fine. Also if I Drag n Drop a .png file to paint or a string to the editor: It works.

Now the problem: When it comes to other third party application then it will get a bit confusing. You want to Drag n Drop a SVG file from your app to Adobe XD? Nope, this will not work! You want to Drag n Drop a SVG File from a Folder to Adobe XD? This will work without problems.

This confusing situation not only appears to Adobe XD. They are tons of other programs out there where the Drag n Drop Operation works when using the explorer but your "own created" drag n drop operation failed.

I already tried out a lot. Tested it with different DataFormats, different DragDropEffects… nothing is working. It is like I missed something. I already searched on GitHub for different DoDragDrop() function, searched in the .NET Docs just to see if something is missing.

I found this in the reference source of System.Windows.Forms.Control about the DoDragDrop() function:

Begins a drag operation. The allowedEffects determine which drag operations can occur. If the drag operation needs to interop with applications in another process, data should either be a base managed class (String, Bitmap, or Metafile) or some Object that implements System.Runtime.Serialization.ISerializable. data can also be any Object that implements System.Windows.Forms.IDataObject.

But this info was also not really helpful.

How does the windows explorer create its DragDrop operation and how can I make such a DragDrop operation in .Net (irrelevant if its in vb.net or c#)? Do I need to do more with the DataObject? transform it? change it?


Solution

  • These are the bare-bones requirements for a Drag&Drop operation that involves a DataFormats.FileDrop data type.
    Includes a Thumbnail image of the source file, if the source file type is supported by the Windows GDI related methods (BitBlt, mostly).

    When the Drag&Drop operation is started - dragging the Mouse over a PictureBox - the original .svg file path is added to a specialized StringCollection class.
    The StringCollection path is then included in the DataObject collection passed to the DoDragDrop method using the DataObject.SetFileDropList() method, along with an extended selection of DragDropEffects (set as required).

    The SVGImagePath field used here refers to the original .svg file path.

    Win GDI Image source (Jpeg) tested with PhotoShop, FireFox, Windows Paint, Visual Studio Image Editor.
    .svg Image tested with FireFox and Edge.

    .Net Version: 4.7.1
    Visual Studio version: 15.7.5

    Imports System.Collections.Specialized
    
    Private SVGImagePath As String = "[Source SVG File]"
    
    Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
        If sender IsNot Nothing AndAlso (e.Button = MouseButtons.Left) Then
    
            Dim dataObject As New DataObject()
            Dim FilePathsItems As New StringCollection()
            FilePathsItems.Add(SVGImagePath)
    
            Dim thumbCallback As Image.GetThumbnailImageAbort =
                Function()
                    Return False
                End Function
            dataObject.SetImage(New Bitmap(PictureBox1.Image.GetThumbnailImage(64, 64, thumbCallback, IntPtr.Zero)))
            dataObject.SetFileDropList(FilePathsItems)
            PictureBox1.DoDragDrop(dataObject, (DragDropEffects.Copy Or DragDropEffects.Link))
        End If
    End Sub
    

    C# original code:

    using System.Collections.Specialized;
    
    private string SVGImagePath = string.Empty;
    
    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            //Image.GetThumbnailImageAbort thumbCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
            //FilePathsItems.Add(((FileInfo)listBox1.SelectedItem).FullName);
            DataObject dataObject = new DataObject();
            StringCollection FilePathsItems = new StringCollection();
            FilePathsItems.Add(SVGImagePath);
    
            Image.GetThumbnailImageAbort thumbCallback = ()=> { return false; };
            dataObject.SetImage(new Bitmap(pictureBox1.Image.GetThumbnailImage(64, 64, thumbCallback, IntPtr.Zero)));
            dataObject.SetFileDropList(FilePathsItems);
            pictureBox1.DoDragDrop(dataObject, (DragDropEffects.Copy | DragDropEffects.Link));
        }
    }