首先,我们要知道两个概念。
一,WPF中有两个线程,一个用于界面UI的管理,一个用于图形的绘制与渲染(Render),
二,在UI线程中,使用了一个名为Dispatcher的类帮助UI线程处理任务。
下面这个例子
using System; using System.Windows; using System.Threading; namespace WpfApplication1 { /// <summary> /// Window1.xaml 的交互逻辑 /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { //错误的写法:直接更新 new Thread(() => { progressBar1.Value = 20; }).Start(); } private void button2_Click(object sender, RoutedEventArgs e) { //正确的写法:通知主线程去完成更新 new Thread(()=>{ this.Dispatcher.Invoke(new Action(()=>{ progressBar1.Value=20; })); }).Start(); } } }
Dispatcher,翻译过来是调度员的意思。所有控件都有Dispatcher属性,因为在WPF中,控件都是继承于DispatcherObject类,DispatcherObject暴露了Dispatcher属性用来取得创建对象线程对应的Dispatcher,我们通过Dispatcher直接调用Invoke或者BeginInvoke来投入任务。
同时,Dispatcher本身是一个单例模式,它被用于获得当前线程的Dispatcher。所以,无论我们使用哪个Dispatcher,其实都是在调用同一个UI线程,即本文开头所说的那个UI线程来处理任务。
Dispatcher暴露了两个方法,Invoke和BeginInvoke,这两个方法还有多个不同参数的重载。其实本质上,Invoke内部还是调用了BeginInvoke,区别在于后者是异步执行的,一个典型的BeginInvoke参数如下:
public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args);
DispatcherPriority定义了很多优先级,WPF将这些优先级主要分成两类。前台优先级和后台优先级,其中前台包括Loaded~Send,后台包括Background~Input。
给个例子:
namespace WpfApplication1 { public class WindowHelper { public static void SomeMethod() { var task = Application.Current.Dispatcher.BeginInvoke(new Action(() => { Application.Current.MainWindow.Title = "我修改过的窗体标题"; })); task.Completed += new EventHandler(task_Completed); } static void task_Completed(object sender, EventArgs e) { MessageBox.Show("任务已经完成"); }
参考文献:
https://www.cnblogs.com/chenxizhang/archive/2010/03/25/1694604.html
http://www.cnblogs.com/Zhouyongh/archive/2009/08/31/1557126.html