3

drag-n-drop is a pretty discussed topic in a lot sites (this too) and i found nice questions too, but no answer to this case.

I have a listView with some elements and i need them to be droppable on the Windows Explorer. When dropped i need only the file path where those are dropped, i don't need to copy anything, just need the path.

Similar question (and why they don't work for me):

The only solution i found:

http://www.codeproject.com/Articles/23207/Drag-and-Drop-to-Windows-Folder-C

This works but in a very "unpratical" way, it creates a file watcher, create a dummy file, let the DragDrop function copy it, watch where it was created and finally delete it. Testing it in my Windows8.1 results in a incorrect Explorer refresh and i can still see the file until i refresh my screen (F5).

Is this the only way? I still can't believe i can't achieve this in a simpler way

Community
  • 1
  • 1
HypeZ
  • 4,017
  • 3
  • 19
  • 34

2 Answers2

2

Think about it for a minute... if you know about drag and drop, then you'll know that the drag source worries about packaging up the data into the correct format and the drag target worries about retrieving the data in the correct format. Your problem is that your drag target is not in your WPF application and so there is very little that you can do as the data is dropped.

A much better solution would be to implement your own basic file browser and then as part of your application, it would be far simpler to access the file path with the drag and drop operation. Either way, you've got a lot of work to do.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Hi and thanks you For your answer :) my application already have a file explorer but its on a remote path and the procedure for download a file is already implemented, I need the target path to start my download procedure. My program generates a queue of download and uploads from and to different paths, so I just need the target path to make all this working :) – HypeZ Jan 07 '14 at 12:51
  • I understand there is no clean way of getting the filepath where the file was dropped. I'm interested in this because of the following problem: When dragging a file from my application using move (not copy) and the file already exist in the target directory, I have no way to detect if the user chose cancel in the windows "Replace file dialog" ( http://miksovsky.blogs.com/flowstate/WindowsLiveWriter/image_thumb_1.png ) and therefor the file will be lost if I remove it from my application well (since it should be moved, not copied). Or is there a way to detect that? – salle55 Oct 22 '14 at 11:41
1
  1. Create an empty FileDrop item as soon as you start dragging any item from your ListView.
  2. As one of your mouse buttons is always down while dragging, start a timer which fires an event as soon as you release the pressed mouse button.
  3. When the button is released, get the window handle of the window where the mouse is located. Match that handle against any opened Windows Explorer window.
  4. If a matched window was found, get the location URL of that Windows Explorer Window and manipulate the available URL of that Windows Explorer window to get the (UNC) Windows path.

Create a Windows Form in design mode and add a ListView to it with name lvFiles. Set its AllowDrop property to True. Then add a timer to the form and name it dropTimer. Set the interval to 50. Set Enabled to False. In the events of dropTimer, double click, so the event will be dropTimer_Tick.

Go to code behind and paste code below.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace test
{
    public partial class Form1 : Form
    {

        [DllImport("user32.dll")]
        static extern int GetForegroundWindow();

        [DllImport("user32.dll")]
        static extern short GetKeyState(VirtualKeyStates nVirtKey);


        enum VirtualKeyStates : int
        {
            VK_LBUTTON = 0x01,
            VK_RBUTTON = 0x02,
        }

        bool IsKeyPressed(VirtualKeyStates testKey)
        {
            bool keyPressed = false;
            short result = GetKeyState(testKey);
            switch (result)
            {
                case 0:
                    keyPressed = false;
                    break;
                case 1:
                    keyPressed = false;
                    break;
                default:
                    keyPressed = true;
                    break;
            }
            return keyPressed;
        }

        int GetActiveWindowHandle()
        {
            const int nChars = 256;
            int handle = 0;
            StringBuilder Buff = new StringBuilder(nChars);
            handle = GetForegroundWindow();
            if (GetWindowText(handle, Buff, nChars) > 0)
                return handle;
            else
                return 0;
        }

        private string GetWindowsExplorerPathFromWindowHandle(int handle)
        {
            // Add a project COM reference to Microsoft Internet Controls 1.1
            SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindowsClass(); 
            string fileName;
            string path = "";
            foreach ( SHDocVw.InternetExplorer ie in shellWindows )
            {    
                fileName = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
                if (fileName.Equals("explorer") && ie.HWND == handle)
                {
                    path = ie.LocationURL;
                    path = path.ToLower();
                    path = path.Replace("file://", "");
                    if (path.StartsWith("/"))
                        path = path.Substring(1);
                    path = path.Replace("/", "\\");
                    if (!path.Contains(":")) // unc paths
                        path = "\\\\" + path;
                    break;
                }
            }
            return path; 
        }

        // Replace the created event from the designer with this event:
        //
        private void lvFiles_ItemDrag(object sender, ItemDragEventArgs e)
        {
            // fake drag and drop effect (start)
            string dataFormat = DataFormats.FileDrop;
            string[] data = new string[1];
            data[0] = "";
            DataObject dataObject = new DataObject(dataFormat, data);

            // catch mouse events
            if (IsKeyPressed(VirtualKeyStates.VK_LBUTTON))
                MouseButtonPressed = MouseButtons.Left;
            else if (IsKeyPressed(VirtualKeyStates.VK_RBUTTON))
                MouseButtonPressed = MouseButtons.Right;
            else
                MouseButtonPressed = MouseButtons.None;
            if (MouseButtonPressed == MouseButtons.Left || MouseButtonPressed == MouseButtons.Right) 
                this.dropTimer.Enabled = true;

            // fake drag and drop effect (launch)
            DoDragDrop(dataObject, DragDropEffects.Copy);
        }


        private void dropTimer_Tick(object sender, EventArgs e)
        {
            bool mouseButtonsReleased = false;
            if (MouseButtonPressed == MouseButtons.Left && !IsKeyPressed(VirtualKeyStates.VK_LBUTTON))
                mouseButtonsReleased = true;
            else if (MouseButtonPressed == MouseButtons.Right && !IsKeyPressed(VirtualKeyStates.VK_RBUTTON))
                mouseButtonsReleased = true;
            if (mouseButtonsReleased)
            {
                dropTimer.Enabled = false;
                int handle = GetActiveWindowHandle();
                string dropPath = GetWindowsExplorerPathFromWindowHandle(handle);

                MessageBox.Show(dropPath); // Here is where the Windows Explorer path is shown
            }
        }

    }
}

Fill your ListView some way and drag any of the ListView item(s) to a Windows Explorer window; The drop path will show up.

  • thank you, but i can't find ExplorerHelper.GetActiveWindowHandle(); i used this but doesn't work, give always 0: [DllImport("user32.dll", EntryPoint = "GetActiveWindow", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] static extern IntPtr GetActiveWindowHandle(); – HypeZ Apr 03 '14 at 13:21
  • ok i made it work using GetForegroundWindow() but with this is i can't get the correct folder where the user dropped! GetActiveWindow() can't work for this problem, it refeers to the active Windows used by the calling thread, not explorer.exe. So.. that ExplorerHelper can be handy here :D – HypeZ Apr 03 '14 at 13:38
  • Sorry, Just get the code from above again, I added GetActiveWindow from my ExplorerHelper class and I also added GetForeGroundWindow(). I'm curious if you get it working now! – Marcel Pennock Apr 04 '14 at 11:12
  • thank you for your help :) Sadly, the GetForegroundWindow() function doesn't get the right drop target :( If you drop on the desktop it fails (returns 0), and if you drop on a folder you don't get that folder path. This is a partial solution, cool and handy but partial :( Thank you anyway! – HypeZ Apr 04 '14 at 13:26