[英]Adding text to RichTextBox Async #C / WPF
我想要實現的是在每次操作后將文本添加到RichTextBox。 問題是,這些操作需要一些時間,而不是在每個操作完成后查看附加文本,我會在例程結束時查看它們。
半偽代碼:
RichTextBox richTextBox = new RichTextBox()
if (Operation1())
{
richTextBox.AppendText("Operation1 finished");
if (Operation2())
{
richTextBox.AppendText("Operation2 finished");
if (Operation3())
{
richTextBox.AppendText("Operation3 finished");
}
}
}
問題是我在操作3完成后查看操作1和2的附加文本。
我在某處讀到了我需要使用的東西叫做BackgroundWorker ???
使用BackgroundWorker,您只需將后台工作放入DoWork
,並將更新放入RunWorkerCompleted
:
var bw1 = new BackgroundWorker();
var bw2 = new BackgroundWorker();
var bw3 = new BackgroundWorker();
bw1.DoWork += (sender, args) => args.Result = Operation1();
bw2.DoWork += (sender, args) => args.Result = Operation2();
bw3.DoWork += (sender, args) => args.Result = Operation2();
bw1.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation1 ended\n");
bw2.RunWorkerAsync();
}
};
bw2.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation2 ended\n");
bw3.RunWorkerAsync();
}
};
bw3.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation3 ended\n");
}
};
bw1.RunWorkerAsync();
你會發現這與“干”相反。 您始終可以考慮使用以下內容為每個步驟抽象任務:
var operations = new Func<bool>[] { Operation1, Operation2, Operation3, };
var workers = new BackgroundWorker[operations.Length];
for (int i = 0; i < operations.Length; i++)
{
int locali = i; // avoid modified closure
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => args.Result = operations[locali]();
bw.RunWorkerCompleted += (sender, args) =>
{
txt.Text = string.Format("Operation{0} ended\n", locali+1);
if (locali < operations.Length - 1)
workers[locali + 1].RunWorkerAsync();
};
workers[locali] = bw;
}
workers[0].RunWorkerAsync();
您可以執行上述操作3次,或使用 ReportProgress
在一個后台線程中運行所有任務,並定期報告進度。
WPF(以及大多數其他UI框架的工作方式)是有一個UI線程,它處理所有UI事件(例如按鈕點擊)和UI繪圖。
如果UI忙於做其他事情,UI就無法繪制內容。 這是怎么回事:
RichTextBox
RichTextBox
RichTextBox
這就是為什么你看到一個暫停,然后所有3個更新在一起。
您需要做的是使需要很長時間的代碼在不同的線程上運行,以便UI線程可以保持自由重繪和更新的時間。 這個示例程序適合我 - 它需要.NET 4.5才能編譯和運行
using System.Threading.Tasks;
...
// note we need to declare the method async as well
public async void Button1_Click(object sender, EventArgs args)
{
if (await Task.Run(new Func<bool>(Operation1)))
{
richTextBox.AppendText("Operation1 finished");
if (await Task.Run(new Func<bool>(Operation2)))
{
richTextBox.AppendText("Operation2 finished");
if (await Task.Run(new Func<bool>(Operation3)))
{
richTextBox.AppendText("Operation3 finished");
}
}
}
}
這里發生的是我們使用C#魔法async
功能,操作順序如下:
Operation1
,而是將它傳遞給Task.Run
。 這個輔助函數將在線程池線程上運行您的Operation1
方法。 await
關鍵字等待線程池完成執行operation1。 這背后的事情在道德上與此相當:
因為我們現在在線程池中運行長操作,所以UI線程可以在需要時繪制它的更新,並且您將看到消息按照您的預期添加。
但是,這有一些潛在的缺點:
Operation1
方法未在UI線程上運行,如果它需要訪問任何UI相關數據(例如,如果它想要從文本框中讀取某些文本等),則它不能再執行此操作。 您必須首先執行所有UI內容,並將其作為參數傳遞給Operation1方法 await Task.Factory.StartNew<bool>(_ => Operation1(), null, TaskCreationOptions.LongRunning)))
而且每個任務都將在它自己的線程中運行而不是使用線程池了。 雖然有點丑陋:-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.