[英]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.