UI unresponsive until action is complete

与世无争的帅哥 提交于 2020-11-27 04:15:45

问题


I'm not sure if the Title is a good description of this issue or not. Essentially what I have is a WinForm app that retrieves a list of files from a folder into a ListView, then a button is clicked to upload them via FTP to a remote server.

Functionally speaking, the app works as expected:

  1. Open app
  2. Review list of files in ListView control
  3. Click Upload button
  4. Files listed in ListView are uploaded; after each successful upload the ListView is updated to show 'Success'
  5. After all files are uploaded, operation stops.

My issue is, after clicking the upload button the UI is pretty much unresponsive until the operation finishes. The ListView updates as expected as each file is uploaded and even keeps the active row in focus. Here is the for loop that processes the files. A little background - in the code excerpt below, each for...loop processes 2 files - the primary file is the only one that shows in the ListView. The 2nd file in each loop is a trigger file that is sent after its primary is sent, ie: .primary, .trigger. Both files have to send in order to register a success. If a primary file does not have a corresponding trigger file, it won't be available in the ListView for upload.

foreach (ListViewItem item in lvSourceFiles.Items)
{
    int rowIndex = item.Index;
    string fileName = item.SubItems[2].Text;

    lvSourceFiles.EnsureVisible(rowIndex);

    transferStatus = "Failed"; // Set this as a default

    // Transfer the source file first
    transferResult = session.PutFiles(readyFile, destFile, false, transferOptions);

    // Throw on any error
    transferResult.Check();

    // If the source file transfer was successful, then transfer the trigger file
    if (transferResult.IsSuccess)
    {
        transferResult = session.PutFiles(triggerFile, destFile, false, transferOptions);
        transferResult.Check();

        if (transferResult.IsSuccess)
        {
            transferStatus = "Success";
        }
    }

    UpdateResultsToListView(lvSourceFiles, rowIndex, fileName, transferStatus);
}

Is this a situation where I need to implement some sort of asynchronous functionality, or is there a better way to do this so the UI doesn't freeze during the upload process? Essentially I want to be able to interact with the form while the upload is running, such as having a cancel button to stop the upload. As it stands, I can't do anything with the form until the job completes, or I terminate the app.

Thanks, James


回答1:


You could offload the long-running operation to a ThreadPool thread, by using async/await and the handy Task.Run method:

transferResult = await Task.Run(() => session.PutFiles(readyFile, destFile, false, transferOptions));

...and:

transferResult = await Task.Run(() => session.PutFiles(triggerFile, destFile, false, transferOptions));

You should also add the async modifier in the event handler, in order to enable the await operator.

Important: Avoid doing anything UI related in the offloaded method. If you want to communicate with the UI during the operation, for example for progress reporting, use the Progress<T> class.




回答2:


You cannot do lengthy operations on the GUI thread. Do them on a background thread.

The answer by @Theodor correctly shows that you can move the PutFiles to the thread pool.

Another option is to move all your upload logic to the thread pool and call back to the main thread using Control.Invoke for GUI updates only.

For a full example, see WinSCP article Displaying FTP/SFTP transfer progress on WinForms ProgressBar.

Pick the option that suits you better. I believe that my approach is easier to grasp for someone unexperienced with thread programming.



来源:https://stackoverflow.com/questions/64003533/ui-unresponsive-until-action-is-complete

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!