Marco Sadowski
Marco Sadowski

Reputation: 446

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?

Upvotes: 1

Views: 1027

Answers (2)

Jimi
Jimi

Reputation: 32248

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));
    }
}

Upvotes: 2

Marco Sadowski
Marco Sadowski

Reputation: 446

Jimi's answer is correct. My problem in Jimi's code is the generation of the Thumbnail Image. Your target application could block the D&D operation because of it. If you wait some seconds (maybe GetThumbnailImage take its time) then the target application will allow the file. I leave my personal code as answer.

Dim dataObj As New DataObject
Dim filePaths = New List(Of String) From {New System.IO.FileInfo(_svgPath).FullName}
Dim sc = New System.Collections.Specialized.StringCollection()
sc.AddRange(filePaths.ToArray())
dataObj.SetFileDropList(sc)
PictureBox.DoDragDrop(dataObj, DragDropEffects.Copy)

And again: Special thanks to @Jimi for his effort <3

Upvotes: 0

Related Questions