UI 在操作完成之前无响应
UI unresponsive until action is complete
我不确定标题是否很好地描述了这个问题。基本上我拥有的是一个 WinForm 应用程序,它从文件夹中检索文件列表到 ListView,然后单击一个按钮通过 FTP 将它们上传到远程服务器。
从功能上讲,该应用程序按预期运行:
- 打开应用程序
- 查看 ListView 控件中的文件列表
- 点击上传按钮
- ListView中列出的文件已上传;每次成功上传后,ListView 都会更新以显示
'Success'
- 所有文件上传完毕后,操作停止。
我的问题是,在单击上传按钮后,UI 在操作完成之前几乎没有响应。 ListView 在上传每个文件时按预期更新,甚至使活动行保持焦点。这是处理文件的 for 循环。一些背景知识 - 在下面的代码摘录中,每个 for...loop 处理 2 个文件 - 主文件是唯一显示在 ListView 中的文件。每个循环中的第二个文件是一个触发文件,在发送其主要文件后发送,即:.primary、.trigger。两个文件都必须发送才能注册成功。如果主文件没有相应的触发文件,则无法在 ListView 中上传。
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);
}
在这种情况下我需要实现某种异步功能,还是有更好的方法可以做到这一点 UI 不会在上传过程中冻结?本质上,我希望能够在上传 运行 时与表单进行交互,例如使用取消按钮来停止上传。就目前而言,在作业完成或我终止应用程序之前,我无法对表单执行任何操作。
谢谢,
詹姆斯
您可以使用 async/await 和方便的 Task.Run
方法将 long-running 操作卸载到 ThreadPool
线程:
transferResult = await Task.Run(() => session.PutFiles(readyFile, destFile, false, transferOptions));
...和:
transferResult = await Task.Run(() => session.PutFiles(triggerFile, destFile, false, transferOptions));
您还应该添加 async
modifier in the event handler, in order to enable the await
运算符。
重要提示: 避免在卸载方法中做任何 UI 相关的事情。如果你想在运行过程中与UI进行通信,比如for progress reporting, use the Progress<T>
class.
您不能在 GUI 线程上进行冗长的操作。在后台线程上执行它们。
@Theodor 的回答正确地表明您可以将 PutFiles
移动到线程池。
另一种选择是将所有上传逻辑移至线程池并使用 Control.Invoke
回调主线程,仅用于 GUI 更新。
有关完整示例,请参阅 WinSCP 文章 Displaying FTP/SFTP transfer progress on WinForms ProgressBar。
选择更适合您的选项。我相信对于没有线程编程经验的人来说,我的方法更容易掌握。
我不确定标题是否很好地描述了这个问题。基本上我拥有的是一个 WinForm 应用程序,它从文件夹中检索文件列表到 ListView,然后单击一个按钮通过 FTP 将它们上传到远程服务器。
从功能上讲,该应用程序按预期运行:
- 打开应用程序
- 查看 ListView 控件中的文件列表
- 点击上传按钮
- ListView中列出的文件已上传;每次成功上传后,ListView 都会更新以显示 'Success'
- 所有文件上传完毕后,操作停止。
我的问题是,在单击上传按钮后,UI 在操作完成之前几乎没有响应。 ListView 在上传每个文件时按预期更新,甚至使活动行保持焦点。这是处理文件的 for 循环。一些背景知识 - 在下面的代码摘录中,每个 for...loop 处理 2 个文件 - 主文件是唯一显示在 ListView 中的文件。每个循环中的第二个文件是一个触发文件,在发送其主要文件后发送,即:.primary、.trigger。两个文件都必须发送才能注册成功。如果主文件没有相应的触发文件,则无法在 ListView 中上传。
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);
}
在这种情况下我需要实现某种异步功能,还是有更好的方法可以做到这一点 UI 不会在上传过程中冻结?本质上,我希望能够在上传 运行 时与表单进行交互,例如使用取消按钮来停止上传。就目前而言,在作业完成或我终止应用程序之前,我无法对表单执行任何操作。
谢谢, 詹姆斯
您可以使用 async/await 和方便的 Task.Run
方法将 long-running 操作卸载到 ThreadPool
线程:
transferResult = await Task.Run(() => session.PutFiles(readyFile, destFile, false, transferOptions));
...和:
transferResult = await Task.Run(() => session.PutFiles(triggerFile, destFile, false, transferOptions));
您还应该添加 async
modifier in the event handler, in order to enable the await
运算符。
重要提示: 避免在卸载方法中做任何 UI 相关的事情。如果你想在运行过程中与UI进行通信,比如for progress reporting, use the Progress<T>
class.
您不能在 GUI 线程上进行冗长的操作。在后台线程上执行它们。
@Theodor 的回答正确地表明您可以将 PutFiles
移动到线程池。
另一种选择是将所有上传逻辑移至线程池并使用 Control.Invoke
回调主线程,仅用于 GUI 更新。
有关完整示例,请参阅 WinSCP 文章 Displaying FTP/SFTP transfer progress on WinForms ProgressBar。
选择更适合您的选项。我相信对于没有线程编程经验的人来说,我的方法更容易掌握。