Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Drag and Drop to Windows Folder - C#

4.96/5 (9 votes)
11 Feb 2009CPOL4 min read 1   3.2K  
Drag and drop a file to a Windows folder from your application without using Shell Extensions.

Image 1

Introduction

All the source code I could find to implement a Drag and Drop to a Windows folder was using Shell Extensions. Initially, I implemented it that way. And, it was working fine in all Windows versions except Vista. They have changed some internal interfaces and made my code not work, which prompted me to find another way of implementing it without depending on the Operating System too much.

I have used Visual Studio 2005 for development, and have tested this in Windows XP, Windows 2000, Windows 2003, and Vista.

Background

I have just concentrated on fetching the directory path where the drop is performed. Use your own methods to initiate the drag and handle it. I have used FileSystemWatcher to implement this. Here is the working:

When a drag is initiated from your application, a temporary file is created in the system's temp directory. We need to identify this among the tons of files created there, so I have prefixed it with a constant which the application can detect. As it is being used for identification, make it in a unique pattern so that other files will not have that name. A FileSysemWatcher will be set to the system's Temp folder to check if any file with this name is created there. When we initiate a drag, this file is created and the FileSystemWatcher will catch it. We will set this file as the drag source so that Windows will recognize it as a genuine file drop and this file will be copied to the location where we need to download the original file. What I did next is to implement a FileSystemWatch for the dropped file for all the logical drives in the system. If it is dropped in any Windows folder or the Desktop, it will be caught and the file system watch over the drives will be removed instantly. If the drop is canceled, then also the watch is canceled.

Using the code

First, we need to put the system's Temp folder on watch for the file we create when a drag is initiated. We need to do it in the starter thread itself, and these are the variables I have used in Program.cs to do it:

C#
#region Variables

//A prefix used in filename to identify a drag from the application
internal const string DRAG_SOURCE_PREFIX = "__DragNDrop__Temp__";

//The item which we drag is being stored here
internal static object objDragItem;

//A FileSystemWatcher to monitor the System's Temp Directory for a drag 
private static FileSystemWatcher tempDirectoryWatcher;

//A Hashtable to keep multiple FileSystemWatchers 
public static Hashtable watchers = null;

#endregion

And in the Main(), I have added the watch:

C#
static void Main()
{
    //Sets tempDirectoryWatcher to monitor
    //the creation of a file from our application
    tempDirectoryWatcher = new FileSystemWatcher();
    tempDirectoryWatcher.Path = Path.GetTempPath();            
    tempDirectoryWatcher.Filter = 
       string.Format("{0}*.tmp", DRAG_SOURCE_PREFIX);
    tempDirectoryWatcher.NotifyFilter = NotifyFilters.FileName;
    tempDirectoryWatcher.IncludeSubdirectories = false;
    tempDirectoryWatcher.EnableRaisingEvents = true;
    tempDirectoryWatcher.Created += 
       new FileSystemEventHandler(TempDirectoryWatcherCreated);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new DragNDrop());
}

Here, the FIleSystemWatch will be monitoring the temp folder for a file to be created with "DRAG_SOURCE_PREFIX" and with a ".tmp" extension.

Now in the event of such a file being created, we need to put all the logical drives in the system on watch.

C#
private static void TempDirectoryWatcherCreated(object sender, FileSystemEventArgs e)
{
    try
    {
        if (watchers == null)
        {
            int i = 0;
            Hashtable tempWatchers = new Hashtable();
            FileSystemWatcher watcher;
            //Adding FileSystemWatchers and adding Created event to it 
            foreach (string driveName in Directory.GetLogicalDrives())
            {
                if (Directory.Exists(driveName))
                {
                    watcher = new FileSystemWatcher();
                    watcher.Filter = string.Format("{0}*.tmp", DRAG_SOURCE_PREFIX);
                    watcher.NotifyFilter = NotifyFilters.FileName;
                    watcher.Created += new FileSystemEventHandler(FileWatcherCreated);
                    watcher.IncludeSubdirectories = true;
                    watcher.Path = driveName;
                    watcher.EnableRaisingEvents = true;
                    tempWatchers.Add("file_watcher" + i.ToString(), watcher);
                    i = i + 1;
                }
            }
            watchers = tempWatchers;
            tempWatchers = null;
        }
    }
    catch (Exception ex)
    {
        //Handle your exception here
    }
}

The checking if the directory exists or not may sound absurd to you. But in an OS like Windows 2000, even if there is no floppy drive present, an "A:/" entry will be shown in My Computer. I've added it to avoid exceptions like this. Now, if a file is dropped with the drag prefix we have declared with the ".tmp" extension, it will be caught by the event handler.

I think we are done with Program.cs and will move further. So now, we need to handle the events we've created for all the FileSystemWatches which we've kept in the Hashtable which monitors the creation of the file.

C#
private static void FileWatcherCreated(object sender, FileSystemEventArgs e)
{
    try
    {
    string dropedFilePath = e.FullPath; 
        if (dropedFilePath.Trim() != string.Empty && objDragItem != null)
        {
            string dropPath = dropedFilePath.Substring(0, 
                         dropedFilePath.LastIndexOf('\\'));
            if (File.Exists(dropedFilePath))
                File.Delete(dropedFilePath);

            //Use your Download Code here
            MessageBox.Show(String.Format("{0} dropped to {1}",
                         objDragItem.ToString(),dropPath));
        }
        objDragItem = null;
    }
    catch (Exception ex)
    {        
    //Handle your exception here    
    }
}

We just need to remove the file name from the path here. I have also deleted the temp file dropped there.

One more method is required to remove the file system watch for all the logical drives to reduce system overhead.

C#
public static void ClearFileWatchers()
{
    try
    {
        if (watchers != null && watchers.Count > 0)
        {
            for (int i = 0; i < watchers.Count; i++)
            {
                ((FileSystemWatcher)watchers["file_watcher" + i.ToString()]).Dispose();
            }
            watchers.Clear();
            watchers = null;
        }
    }
    catch (Exception ex)
    {        
    //Handle your exception here    
    }
}

Now, let's move on to the form or control from where you are going to initiate the drag. Before initiating the drag, the Hashtable keeping the FileSystemWatchers is set to null. You can do this by calling the ClearFileWatchers() method we just wrote in Program.cs. This could be done in the MouseDown event of the ListView if any ListViewItem is being clicked. And in MouseMove (i.e., drag is initiated), we can create the temp file in the System's Temp folder and start the drag by calling the DoDragDrop() method.

C#
private void ItemsListView_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.None)
        return;
    if (itemDragStart && Program.objDragItem != null)
    {
        dragItemTempFileName = string.Format("{0}{1}{2}.tmp", 
                   Path.GetTempPath(), Program.DRAG_SOURCE_PREFIX, 
                   ItemsListView.SelectedItems[0].Text);
        try
        {
            Util.CreateDragItemTempFile(dragItemTempFileName);

            string[] fileList = new string[] { dragItemTempFileName };
            DataObject fileDragData = new DataObject(DataFormats.FileDrop, fileList);
            DoDragDrop(fileDragData, DragDropEffects.Move);

            ClearDragData();
        }
        catch (Exception ex)
        {
            //Handle your exception here
        }
    }
}

Here, Util is a static class, and using the method CreateDragItemTempFIle(), I am creating the temp file in the System Temp folder. In that method, please make sure that there is already no such file with the name existing in the Temp folder. Otherwise, it will be replaced and the Created event won't be triggered for the FileSystemWatcher watching the Temp folder. Better to delete the temp file if it exists earlier before creating the new one.

That will do you the job.

Points of interest

This will just do a temporary job of dropping the files. I still think I need to get back to shell extensions. If you intend to drop files to a single machine, this works fine. But, if you need to drop it to another machine in the network, this won't work. There, only Shell can help you.

History

  • Posted: January 26th 2008.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)