我正在开发一个 Winforms 应用程序。用户将通过按下按钮来启动一项耗时的任务。我不希望我的 UI 冻结,而且我还希望信息不断从该任务返回,然后可用于更新 UI 控件和进度栏。
我进行了研究,看起来 BackgroundWorker 类适用于这些类型的用例。但是,我查看了很多示例,但不清楚如何将数据从工作线程传递回 UI 线程。大多数示例只是专注于通过 ReportProgress() 方法传回百分比完整数字,而 ProgressChanged 事件处理程序是我们被告知与 UI 交互的地方。
这是我到目前为止所做的:
任何见解都值得赞赏。
private Dictionary<int, string> _backgroundSteps = new Dictionary<int, string>()
{
[1] = "Preparing frobbles for {0}",
[2] = "Executing wibbles for {0}",
[3] = "Cleaning up {0} frobbles and {1} wibbles"
};
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//prepare frobbles
var database = new DB();
var preparingFor = database.GetUser();
backgroundWorker1.ReportProgress(1, new string[] { preparingFor });
PrepareFrobbles();
backgroundWorker1.ReportProgress(2, new string[] { preparingFor });
ExecuteWibbles();
backgroundWorker1.ReportProgress(3, new string[] { database.CountFrobbles().ToString(), database.CountWibbles().ToString() });
CleanUp();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_statusLabel.Text = string.Format(_backgroundSteps[e.ProgressPercentage], (string[])e.UserState);
}
在这里,我使用进度来计算 3 个步骤的过程,随消息一起传递一些变化的状态,并简单地用消息更新标签。我可以更多地参与 ProgressChanged,使用条件逻辑来根据传递的 int 做不同的事情,更好地利用状态对象等..或者我什至无法使用状态重载,只需报告 int,然后通过一些标签面板循环突出显示该过程处于哪个阶段等。关于如何使用它,这是相当开放的
private async Task DoSomeLongWork(){
statusLabel.Text = "Preparing frobbles";
await PrepareFrobblesAsync();
statusLabel.Text = "Executing wibbles";
await ExecuteWibblesAsync();
statusLabel.Text = "Executing wibbles";
await CleanUpAsync();
}
执行此操作的代码是 UI 线程,因此它设置文本是安全的。 UI 线程不会陷入准备 frobble 和执行 wibbles 的漫长工作中;当控制它的任务正在等待时,UI 线程会消失回到它原来的位置,继续正常的绘制 UI 工作。当 frobbles 准备好(例如)并且任务完成时,线程从 await PrepareFrobblesAsync
之后恢复执行此代码,设置另一个状态,然后在等待执行 wibbles 等时再次消失。