题记
在编写有GUI的程序时,会遇到这样一种情形:用户点击了一个按钮,程序处理这个事件,然而这个处理过程耗时间较长。我们不想让软件卡在这里,而是让用户可以继续使用其他的软件功能。这种问题可以用多线程的事件响应来解决。这里,我就WPF的多线程事件响应做一个简单的归纳。
一、简单的异步的事件响应
在WPF中,针对简单的多线程处理过程,我们可以使用.NET自带的BackgroundWork完成。BackgroundWork的处理过程就是异步的,不会让用户界面停止响应。
using System.ComponentModel; using System.Threading; namespace TestProject { public partial class MainWindow : Window { //... private void Button1_Click(object sender, RoutedEventArgs e) { //声明 BackgroundWorker worker = new BackgroundWorker(); // worker 要做的事情 使用了匿名的事件响应函数 worker.DoWork += (o, ea) => { //WPF中线程只能控制自己创建的控件, //如果要修改主线程创建的MainWindow界面的内容, //可以委托主线程的Dispatcher处理。 //在这里,委托内容为一个匿名的Action对象。 this.Dispatcher.Invoke((Action)(() => { this.TextBox1.Text = "worker started"; })); Thread.Sleep(1000); }; // worker 完成事件响应 worker.RunWorkerCompleted += (o, ea) => { this.Dispatcher.Invoke((Action)(() => { this.TextBox1.Text = "worker finished"; })); }; //注意:运行了下面这一行代码,worker才真正开始工作。上面都只是声明定义而已。 worker.RunWorkerAsync(); } //... } }
二、自定义事件的多线程处理过程
有时候,在我们创建的新的线程中,可能有一些事件需要主线程处理。对于这种比较复杂的异步处理,可以自定义事件,用C#中的委托(delegate)实现。
MyThread.cs (自定义事件)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace TestProject { //定义 自定义事件的参数类型 public class MyEventArgs : EventArgs { //参数可以携带值,方便处理程序使用 public readonly int value; public MyEventArgs(int v) { value = v; } } class MyThread { //... //定义delegate public delegate void MyEventHandler(object sender, MyEventArgs e); //声明 自定义事件 public event MyEventHandler MyEvent; //... //触发事件 protected virtual void OnMyEvent(MyEventArgs e) { if (MyEvent != null) { MyEvent(this, e); } } //... //测试函数 public void Test() { System.Threading.Thread.Sleep(1000); this.OnMyEvent(new MyEventArgs(100)); System.Threading.Thread.Sleep(2000); } } }
MainWindows.xaml.cs
using System.ComponentModel; using System.Threading; namespace TestProject { public partial class MainWindow : Window { //... //测试 private void Button2_Click(object sender, RoutedEventArgs e) { MyThread myThread = new MyThread(); //为myThread的MyEvent事件声明一个响应函数 myThread.MyEvent += MyThread_MyEvent; //定义新的线程 Thread thread = new Thread(new ThreadStart(myThread.Test)); //开始新的线程 thread.Start(); } // 新的线程中 MyEvent 的响应函数 public void MyThread_MyEvent(object sender, MyEventArgs e) { //同样,如果想要修改主界面的控件, //需要委托主线程的Dispatcher来处理。 this.Dispatcher.Invoke((Action)(() => { this.TextBox2.Text = "MyEvent triggered"; })); } //... } }
总结
以上介绍了两种情况下,WPF多线程的实现方法。第一种可以满足一般的需求,例如:后台加载文件。第二种主要针对后台处理时有特殊的事件需要前台响应,例如:后台加载文件,每当加载满1M时,在MainWindow展示特定内容给用户。
时间: 2024-10-12 18:35:53