0

I am attempting to drag a file represented by a TreeView node onto the desktop, Windows Explorer, or any other applications that allow files to be dropped onto them. I've written the code below based on various Internet examples I've found and I'm running it as administrator. It does allow me to drag as long as I remain in the TreeView control that contains the nodes, displaying the Copy icon with the cursor as it moves. However, when I drag it off the control to the desktop or Windows Explorer for example, the icon turns into the red circle with a slash across it and nothing gets dropped. I've ensured that the dragged file actually does exist.

private void treeView_Files_ItemDrag(object sender, ItemDragEventArgs e)
{
    DoDragDrop(e.Item, DragDropEffects.Copy);
}

private void treeView_Files_DragDrop(object sender, DragEventArgs e)
{
    TreeNode NewNode;

    if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
    {
        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
        TreeNode DestinationNode = ((TreeView)sender).GetNodeAt(pt);
        NewNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
        string[] files = new string[] { "C:\\temp\\TestFile.pdf" };
        DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
        DoDragDrop(dataObject, DragDropEffects.Copy);
    }
}
BenevolentDeity
  • 587
  • 6
  • 15
  • Explorer and Desktop are not going to know what to do with a TreeNode. Files are not TreeNodes DragDrop the file the TN *represents* instead. You wont get any feedback though (like did the user Copy vs Move) – Ňɏssa Pøngjǣrdenlarp Feb 23 '18 at 03:14
  • Maybe I didn't phrase it correctly. I don't want to drop a TreeNode, but the file represented by a TreeNode. Regardless, just for test purposes in the sample code I provided above I bypassed the TreeNode business just before the drop and used a file path I already knew existed: "C:\\temp\\TestFile.pdf". The current problem is that the Copy (or Move) icon is only visible when the mouse pointer is on the control. – BenevolentDeity Feb 23 '18 at 05:05
  • 1
    The first argument to `DoDragDrop` is what you want to drag and drop. Pass the file *not* the item that was dragged – Ňɏssa Pøngjǣrdenlarp Feb 23 '18 at 05:45
  • Plutonix: I guess I'm just missing your point. Regardless of what I dragged, please take a look at the last three lines of code in my example. I am creating a string array containing the path to the file I want to drop. Then I'm creating a data object from it. Then I'm doing a drop of that data object. Why do you say I'm dropping the object that was dragged? For test purposes I explicitly ignored the dragged item and substituted in a test file path. What needs to be changed? – BenevolentDeity Feb 23 '18 at 19:30
  • 1
    Nominating to reopen, since this is decidedly NOT a duplicate. Dragging a file *out* of a TreeView is very different from dragging a file *into* a TreeView, and involves completely different bits of code. This definitely deserves its own answer. – Dmitry Brant Apr 18 '18 at 16:08

2 Answers2

0

I was unable to figure out why my original code above would not display the Copy icon on the cursor unless it was within the TreeView control itself and, hence, would not let me drop the desired file. However, I was able to come up with a solution that does display the icon and will drop on any application capable of accepting a dropped file. I simply abandoned the use of the ItemDrag and DragDrop events entirely and used the MouseDown event instead as shown below. As in my original code I'm using a test file instead of the file represented by the node, but extracting the real file path from the node is trivial. Of course some additional simple code is necessary to determine if the coordinates are actually on a node:

private void treeView_Files_MouseDown(object sender, MouseEventArgs e)
{
    TreeNode node = treeView_Files.GetNodeAt(e.X, e.Y);
    string[] files = new string[] { "C:\\temp\\TestFile.pdf" };
    DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
    DoDragDrop(dataObject, DragDropEffects.Copy);
}
BenevolentDeity
  • 587
  • 6
  • 15
0

Try constructing the DataObject and performing the DoDragDrop() operation inside the ItemDrag function, instead of the DragDrop function:

private void treeView_Files_ItemDrag(object sender, ItemDragEventArgs e)
{
    string[] files = new string[] { "C:\\temp\\TestFile.pdf"};
    DataObject dataObject = new DataObject(DataFormats.FileDrop, files);
    DoDragDrop(dataObject, DragDropEffects.Copy);
}

The e.Item parameter (which is the TreeView item) is not really the object that you want to pass to Explorer, so you shouldn't be putting it into the DoDragDrop call.

On a side note, I spent a long time researching how it's possible to get the "destination" folder onto which the item was dropped. My solution involves roughly the following:

  • Create a FileSystemWatcher and make it send events when new files are created.
  • Give it a Filter that matches the file name that you're dropping.
  • When the file is dropped (i.e. when it's created in the destination folder), you'll receive an event via the watcher.

Something like this:

private void CreateWatcher()
{
    var watcher = new FileSystemWatcher();
    watcher.Filter = "TestFile.pdf";
    watcher.NotifyFilter = NotifyFilters.FileName;
    watcher.Created += new FileSystemEventHandler(OnWatcherFileCreated);
    watcher.IncludeSubdirectories = true;
    watcher.Path = "C:\\";
    watcher.EnableRaisingEvents = true;
}

private void OnWatcherFileCreated(object sender, FileSystemEventArgs e)
{
    // Note: this happens in a separate non-UI thread.
    Console.WriteLine("Dropped onto: " + e.FullPath);
}
Dmitry Brant
  • 7,612
  • 2
  • 29
  • 47