Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fix Drag Drop Freezing Explorer

Tags:

I have an application that allows data to be dragged and dropped into it, before performing a potentially very long operation upon it. This works fine, however, the Explorer window freezes whilst my application is processing. Is there any way to "release" it so to speak as soon as I have taken a copy of the file list?

My current code is:

    private void MainForm_DragDrop(object sender, DragEventArgs e)
    {
        ClearTempFiles(); //Clear all files before populating
        string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); //Get the filepaths for the dragdrop data
        List<string> toParse = new List<string>();
        foreach (string file in files) 
        {
            FileAttributes attr = File.GetAttributes(file);
            if (attr.HasFlag(FileAttributes.Directory)) //If a folder has been included, add all files from within
            {
                toParse.AddRange(DirSearch(file));
            }
            else
            {
                toParse.Add(file); //Add files
            }
        }
        CurrentJobData = new JobData(toParse); //Create new JobData from these files <---- Takes up to a few minutes with hundreds of files.
        CurrentJobData.ToTree(treeView1); //Push this data to the TreeView
    } //Handles the dragdrop of data, populating the solution
like image 893
Persistence Avatar asked Nov 16 '16 15:11

Persistence


1 Answers

You need to let the event run to completion. Due to the nature of the DragDrop operation both applications involved need to be sure the operation has completed. They can only tell if the DragDrop message is handled by either message pump. As long as that didn't happen, Explorer can only assume the user is still dragging and dropping. So as already suggested in the comments, use a BackgroundWorker:

private void Form1_DragDrop(object sender, DragEventArgs e)
{    
    // gets all data from Explorer
    string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); //Get the filepaths for the dragdrop data

    // don't block the UI thread, that prevents the 
    // processing of both Windows messages in Explorer and your app
    // to complete, effectively blocking both of them
    backgroundWorker1.RunWorkerAsync(files);

    // if you want your form to be unavailable
    // while processing takes place
    // set Enabled to false on your form
    this.Enabled = false;
}

You're DoWork eventhandler will now do the heavy lifting. Notice how I set the Result property of the DoWorkEventArgs to send your JobData to the completed event.

// this all runs on a background, non-UI threed
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    ClearTempFiles(); //Clear all files before populating

    string[] files = (string[])e.Argument; // this is passed in from RunWorkerAsync

    List<string> toParse = new List<string>();
    foreach (string file in files)
    {
        FileAttributes attr = File.GetAttributes(file);
        if (attr.HasFlag(FileAttributes.Directory)) //If a folder has been included, add all files from within
        {
            toParse.AddRange(DirSearch(file));
        }
        else
        {
            toParse.Add(file); //Add files
        }
    }

    e.Result = new JobData(toParse); //Create new JobData from these files <---- Takes up to a few minutes with hundreds of files.
}

In the RunWorkerCompleted eventhandler you're back on the UI thread, where you can fill and update your TreeView. Notice how at the end the Form is enabled again, making sure the user can interact with the application.

// this runs on the UI thread
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    CurrentJobData = (JobData) e.Result;

    CurrentJobData.ToTree(treeView1); //Push this data to the TreeView

    // set Enabled to true on your controls
    this.Enabled = true;
}

If you want to report progress, make sure to use the ProgressChanged eventhandler so you're on the UI thread and call the ReportProgress method on the backgroundworker to invoke that handler.

like image 196
rene Avatar answered Sep 24 '22 17:09

rene