<h1>Dispatcher介绍 - <span class="color_h1">(三)</span></h1><p><br/></p><h1 style="font-size: 16px;font-weight:bold">一、Dispatcher介绍</h1><p> 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?</p><p> 不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是子线程。在WPF或WinForm应用程序中,主线程负责接收输入、处理事件、绘制屏幕等工作,为了使主线程及时响应,防止假死,在开发过程中对一些耗时的操作、消耗资源比较多的操作,都会去创建一个或多个子线程去完成操作,比如大数据量的循环操作、后台下载。这样一来,由于UI界面是主线程创建的,所以子线程不能直接更新由主线程维护的UI界面。</p><p> Dispatcher的作用是用于管理线程工作项队列。类似于Win32中的消息队列,Dispatcher的内部函数,仍然调用了传统的创建窗口类,创建窗口,建立消息泵等操作。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher。对于线程来说,它对Dispatcher是一无所知的,Dispatcher内部维护了一个静态的 List</p><p><br/></p><h1 style="font-size: 16px;font-weight:bold">二、Dispatcher的继承关系</h1><p> 在 WPF 的类层次结构中,大部分都集中派生于 DispatcherObject 类(通过其他类)。如下图 所示,您可以看到 DispatcherObject 虚拟类正好位于 Object 下方和大多数 WPF 类的层次结构之间。 要了解他们之间的关系可以参看下面这张类继承关系图: </p><p><img class="pimg" src="/upload/image/061730171423662.png"/></p><p><br/></p><p>对上图的一些说明:</p><p> 1)System.Object 类:大家都知道在.Net中所有类型的基类,DispatcherObject 就继承于它,所以它是WPF的基类。</p><p> 2)System.Windows.Threading.DispatcherObject 类:从图中看WPF 中的使用到的大部分控件与其他类大多是继承 DispatcherObject类,它提供了用于处理并发和线程的基本构造。</p><p> 3)System.Windows.DependencyObject类:对WPF中的依赖项属性承载支持与 附加属性承载支持,表示参与 依赖项属性 系统的对象。</p><p> 4)System.Windows.Media.Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算等。</p><p> 5)System.Windows.UIElement 类:UIElement 是 WPF 核心级实现的基类,该类是 Windows Presentation Foundation (WPF)中具有可视外观并可以处理基本输入的大多数对象的基类。</p><p> 6)System.Windows.FrameworkElement类:为 Windows Presentation Foundation (WPF) 元素提供 WPF 框架级属性集、事件集和方法集。此类表示附带的 WPF框架级实现,它是基于由UIElement定义的 WPF 核心级 API 构建的。</p><p> 7)System.Windows.Controls.Control 类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。</p><p> 8)System.Windows.Controls.ContentControl类:表示没有任何类型的内容表示单个控,WPF的绝大部分的控件,还包括窗口本身都是继承自ContentControl的。ContentControl族包含的控件:Button、ButtonBase、CheckBox、ComboBoxItem、ContentControl、Frame 、GridViewColumnHeader、GroupItem、Label、ListBoxItem、ListViewItem、NavigationWindow、RadioButton、RepeatButton、ScrollViewer、StatusBarItem、ToggleButton、ToolTip、UserControl、Window</p><p> 9)System.Windows.Controls.ItemsControl 类:表示可用于提供项目的集合的控件。 </p><p> 以条目集合位内容的控件 ItemsControl</p><p> 特点: </p><p> a.均派生自ItemsControl</p><p> b.内容属性为Items或ItemsSource</p><p> c.每种ItemsControl都对应有自己的条目容器(Item Container).</p><p> ItemsControl族包含的控件:Menu、MenuBase、ContextMenu、ComboBox、ItemsControl、ListBox、ListView、TabControl、TreeView、Selector、StatusBar</p><p> 10)System.Windows.Controls.Panel类:为所有 Panel 元素提供基类。 使用 Panel 元素定位和排列在 Windows Presentation Foundation (WPF) 应用程序的子对象。</p><p> 11)System.Windows.Sharps.Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。</p><p><br/><br/></p><h1 style="font-size: 16px;font-weight:bold">三、走进Dispatcher</h1><p> 所有 WPF 应用程序启动时都会加载两个重要的线程:一个用于呈现用户界面,另一个用于管理用户界面。呈现线程是一个在后台运行的隐藏线程,因此您通常面对的唯一线程 就是 UI 线程。WPF 要求将其大多数对象与 UI 线程进行关联。这称之为线程关联,意味着要使用一个 WPF 对象,只能在创建它的线程上使用。在其他线程上使用它会导致引发运行时异常。 UI 线程的作用是用于接收输入、处理事件、绘制屏幕以及运行应用程序代码。</p><p><br/></p><p> 在 WPF 中绝大部分控件都继承自 DispatcherObject,甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了 Dispatcher 的线程(通常指默认 UI 线程)才能直接对其进行更新操作。</p><p><br/></p><p> DispatcherObject 类有两个主要职责:提供对对象所关联的当前 Dispatcher 的访问权限,以及提供方法以检查 (CheckAccess) 和验证 (VerifyAccess) 某个线程是否有权访问对象(派生于 DispatcherObject)。CheckAccess 与 VerifyAccess 的区别在于 CheckAccess 返回一个布尔值,表示当前线程是否可以使用对象,而 VerifyAccess 则在线程无权访问对象的情况下引发异常。通过提供这些基本的功能,所有 WPF 对象都支持对是否可在特定线程(特别是 UI 线程)上使用它们加以确定。如下图。</p><p><img class="pimg" src="/upload/image/QQ截图20210920161208.jpg"/></p><p><br/></p><p> 在 WPF 中,DispatcherObject 只能通过与它关联的 Dispatcher 进行访问。 例如,后台线程不能更新由 UI 线程创建的 Label的内容。</p><p> 那么如何更新UI线程创建的对象信息呢?Dispatcher提供了两个方法,Invoke和BeginInvoke,这两个方法还有多个不同参数的重载。其中Invoke内部还是调用了BeginInvoke,一个典型的BeginInvoke参数如下:</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:c#;toolbar:false;">public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args);
// this.BeginInvoke(new Action(() => { textBox1.text = "abc"; }));</pre><p><br/></p><p> Invoke 是同步操作,而 BeginInvoke 是异步操作。 该这两个操作将按指定的 DispatcherPriority 添加到 Dispatcher 的队列中。 DispatcherPriority定义了很多优先级,可以分为前台优先级和后台优先级,其中前台包括 Loaded~Send,后台包括Background~Input。剩下的几个优先级除了Invalid和Inactive都属于空闲优先级。这个前台优先级和后台优先级的分界线是以Input来区分的,这里的Input指的是键盘输入和鼠标移动、点击等等。</p><p><br/></p><p>DispatchPriority 优先级别</p><p> Invalid:这是一个无效的优先级。</p><p> Inactive:工作项目已排队但未处理。</p><p> SystemIdle:仅当系统空闲时才将工作项目调度到 UI 线程。这是实际得到处理的项目的最低优先级。</p><p> ApplicationIdle:仅当应用程序本身空闲时才将工作项目调度到 UI 线程。</p><p> ContextIdle:仅在优先级更高的工作项目得到处理后才将工作项目调度到 UI 线程。</p><p> Background:在所有布局、呈现和输入项目都得到处理后才将工作项目调度到 UI 线程。</p><p> Input:以与用户输入相同的优先级将工作项目调度到 UI 线程。</p><p> Loaded:在所有布局和呈现都完成后才将工作项目调度到 UI 线程。</p><p> Render:以与呈现引擎相同的优先级将工作项目调度到 UI 线程。</p><p> DataBind:以与数据绑定相同的优先级将工作项目调度到 UI 线程。</p><p> Normal:以正常优先级将工作项目调度到 UI 线程。这是调度大多数应用程序工作项目时的优先级。</p><p> Send:以最高优先级将工作项目调度到 UI 线程。</p><p><br/></p><h1 style="font-size: 16px;font-weight:bold">四、使用Dispatcher</h1><p>下面我们来用一个实例,来看看如何正确从一个非 UI 线程中更新一个由UI线程创建的对象。</p><p>1、错误的更新方式</p><p>MainWindow.xaml 代码:</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:xml;toolbar:false;"><Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Label x:Name="lblHello">欢迎你光临WPF的世界!</Label>
<Button Name="btnThd" Click="btnThd_Click" >多线程同步调用</Button>
</StackPanel>
</Grid>
</Window></pre><p>MainWindow.xaml.cs代码:</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:c#;toolbar:false;">using System;
using System.Threading;
using System.Windows;
namespace WpfApp
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ModifyUI()
{
// 模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(2));
lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
}
private void btnThd_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(ModifyUI);
thread.Start();
}
}
}</pre><p>错误截图:</p><p><img class="pimg" src="/upload/image/QQ截图20210920163816.jpg"/></p><p><br/></p><p>2、正确的更新方式,从上例中我们看到了从子线程中直接更新UI线程创建的对象,会报错。应该如何修改呢?我们把上面的代码修改成如下,再来看看会是什么效果。</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:c#;toolbar:false;">private void ModifyUI()
{
// 模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(2));
//lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
lblHello.Content = "欢迎你光临WPF的世界,Dispatche 同步方法 !!";
});
}</pre><p><img class="pimg" src="/upload/image/QQ截图20210920164327.jpg"/></p><p><br/></p><p>当然Dispatcher类也提供了BeginInvoke方法,我们也可以使用如下代码,来完成对Lable的Content的更新。</p><p>MainWindow.xaml 补充代码:</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:xml;toolbar:false;"><StackPanel>
<Label x:Name="lblHello">欢迎你光临WPF的世界!</Label>
<Button Name="btnThd" Click="btnThd_Click" >多线程同步调用</Button>
<Button Name="btnAppBeginInvoke" Click="btnAppBeginInvoke_Click" >BeginInvoke 异步调用</Button>
</StackPanel></pre><p>MainWindow.xaml.cs补充代码:</p><pre style="overflow-x:auto;font-size: 15px;" class="brush:c#;toolbar:false;">private void btnAppBeginInvoke_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(2));
this.lblHello.Content = "欢迎你光临WPF的世界,Dispatche 异步方法!!" + DateTime.Now.ToString();
}));
}).Start();
}</pre><p>效果:</p><p><img class="pimg" src="/upload/image/QQ截图20210920164925.jpg"/></p><p><br/></p><h1 style="font-size: 16px;font-weight:bold">五、小结</h1><p> 在WPF中,所有的WPF对象都派生自DispatcherObject,DispatcherObject暴露了Dispatcher属性用来取得创建 对象线程对应的Dispatcher。DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。Dispatcher的一些设计思路包括 Invoke和BeginInvoke等从WinForm时代就是一直存在的,只是使用了Dispatcher来封装这些线程级的操作。</p><p>全部操作总结如下:</p><pre style="overflow-x: auto; font-size: 15px;" class="brush:c#;toolbar:false;">//方式一
new Thread(()=>{
this.Dispatcher.Invoke(new Action(()=>{
//通知主线程去完成更新
}));
}).Start();
//方式二
Application.Current.Dispatcher.Invoke(new Action(() => {
Application.Current.MainWindow.Title = "我修改过的窗体标题";
}));
//方式三:异步机制
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("任务已经完成");
}</pre>
Dispatcher介绍 - (三)
一、Dispatcher介绍 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?
不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是子线程。在WPF或WinForm应用程序中,主线程负责接收输入、处理事件、绘制屏幕等工作,为了使主线程及时响应,防止假死,在开发过程中对一些耗时的操作、消耗资源比较多的操作,都会去创建一个或多个子线程去完成操作,比如大数据量的循环操作、后台下载。这样一来,由于UI界面是主线程创建的,所以子线程不能直接更新由主线程维护的UI界面。
Dispatcher的作用是用于管理线程工作项队列。类似于Win32中的消息队列,Dispatcher的内部函数,仍然调用了传统的创建窗口类,创建窗口,建立消息泵等操作。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher。对于线程来说,它对Dispatcher是一无所知的,Dispatcher内部维护了一个静态的 List
二、Dispatcher的继承关系 在 WPF 的类层次结构中,大部分都集中派生于 DispatcherObject 类(通过其他类)。如下图 所示,您可以看到 DispatcherObject 虚拟类正好位于 Object 下方和大多数 WPF 类的层次结构之间。 要了解他们之间的关系可以参看下面这张类继承关系图:
对上图的一些说明:
1)System.Object 类:大家都知道在.Net中所有类型的基类,DispatcherObject 就继承于它,所以它是WPF的基类。
2)System.Windows.Threading.DispatcherObject 类:从图中看WPF 中的使用到的大部分控件与其他类大多是继承 DispatcherObject类,它提供了用于处理并发和线程的基本构造。
3)System.Windows.DependencyObject类:对WPF中的依赖项属性承载支持与 附加属性承载支持,表示参与 依赖项属性 系统的对象。
4)System.Windows.Media.Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算等。
5)System.Windows.UIElement 类:UIElement 是 WPF 核心级实现的基类,该类是 Windows Presentation Foundation (WPF)中具有可视外观并可以处理基本输入的大多数对象的基类。
6)System.Windows.FrameworkElement类:为 Windows Presentation Foundation (WPF) 元素提供 WPF 框架级属性集、事件集和方法集。此类表示附带的 WPF框架级实现,它是基于由UIElement定义的 WPF 核心级 API 构建的。
7)System.Windows.Controls.Control 类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。
8)System.Windows.Controls.ContentControl类:表示没有任何类型的内容表示单个控,WPF的绝大部分的控件,还包括窗口本身都是继承自ContentControl的。ContentControl族包含的控件:Button、ButtonBase、CheckBox、ComboBoxItem、ContentControl、Frame 、GridViewColumnHeader、GroupItem、Label、ListBoxItem、ListViewItem、NavigationWindow、RadioButton、RepeatButton、ScrollViewer、StatusBarItem、ToggleButton、ToolTip、UserControl、Window
9)System.Windows.Controls.ItemsControl 类:表示可用于提供项目的集合的控件。
以条目集合位内容的控件 ItemsControl
特点:
a.均派生自ItemsControl
b.内容属性为Items或ItemsSource
c.每种ItemsControl都对应有自己的条目容器(Item Container).
ItemsControl族包含的控件:Menu、MenuBase、ContextMenu、ComboBox、ItemsControl、ListBox、ListView、TabControl、TreeView、Selector、StatusBar
10)System.Windows.Controls.Panel类:为所有 Panel 元素提供基类。 使用 Panel 元素定位和排列在 Windows Presentation Foundation (WPF) 应用程序的子对象。
11)System.Windows.Sharps.Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。
三、走进Dispatcher 所有 WPF 应用程序启动时都会加载两个重要的线程:一个用于呈现用户界面,另一个用于管理用户界面。呈现线程是一个在后台运行的隐藏线程,因此您通常面对的唯一线程 就是 UI 线程。WPF 要求将其大多数对象与 UI 线程进行关联。这称之为线程关联,意味着要使用一个 WPF 对象,只能在创建它的线程上使用。在其他线程上使用它会导致引发运行时异常。 UI 线程的作用是用于接收输入、处理事件、绘制屏幕以及运行应用程序代码。
在 WPF 中绝大部分控件都继承自 DispatcherObject,甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了 Dispatcher 的线程(通常指默认 UI 线程)才能直接对其进行更新操作。
DispatcherObject 类有两个主要职责:提供对对象所关联的当前 Dispatcher 的访问权限,以及提供方法以检查 (CheckAccess) 和验证 (VerifyAccess) 某个线程是否有权访问对象(派生于 DispatcherObject)。CheckAccess 与 VerifyAccess 的区别在于 CheckAccess 返回一个布尔值,表示当前线程是否可以使用对象,而 VerifyAccess 则在线程无权访问对象的情况下引发异常。通过提供这些基本的功能,所有 WPF 对象都支持对是否可在特定线程(特别是 UI 线程)上使用它们加以确定。如下图。
在 WPF 中,DispatcherObject 只能通过与它关联的 Dispatcher 进行访问。 例如,后台线程不能更新由 UI 线程创建的 Label的内容。
那么如何更新UI线程创建的对象信息呢?Dispatcher提供了两个方法,Invoke和BeginInvoke,这两个方法还有多个不同参数的重载。其中Invoke内部还是调用了BeginInvoke,一个典型的BeginInvoke参数如下:
public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args);
// this.BeginInvoke(new Action(() => { textBox1.text = "abc"; }));
Invoke 是同步操作,而 BeginInvoke 是异步操作。 该这两个操作将按指定的 DispatcherPriority 添加到 Dispatcher 的队列中。 DispatcherPriority定义了很多优先级,可以分为前台优先级和后台优先级,其中前台包括 Loaded~Send,后台包括Background~Input。剩下的几个优先级除了Invalid和Inactive都属于空闲优先级。这个前台优先级和后台优先级的分界线是以Input来区分的,这里的Input指的是键盘输入和鼠标移动、点击等等。
DispatchPriority 优先级别
Invalid:这是一个无效的优先级。
Inactive:工作项目已排队但未处理。
SystemIdle:仅当系统空闲时才将工作项目调度到 UI 线程。这是实际得到处理的项目的最低优先级。
ApplicationIdle:仅当应用程序本身空闲时才将工作项目调度到 UI 线程。
ContextIdle:仅在优先级更高的工作项目得到处理后才将工作项目调度到 UI 线程。
Background:在所有布局、呈现和输入项目都得到处理后才将工作项目调度到 UI 线程。
Input:以与用户输入相同的优先级将工作项目调度到 UI 线程。
Loaded:在所有布局和呈现都完成后才将工作项目调度到 UI 线程。
Render:以与呈现引擎相同的优先级将工作项目调度到 UI 线程。
DataBind:以与数据绑定相同的优先级将工作项目调度到 UI 线程。
Normal:以正常优先级将工作项目调度到 UI 线程。这是调度大多数应用程序工作项目时的优先级。
Send:以最高优先级将工作项目调度到 UI 线程。
四、使用Dispatcher 下面我们来用一个实例,来看看如何正确从一个非 UI 线程中更新一个由UI线程创建的对象。
1、错误的更新方式
MainWindow.xaml 代码:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Label x:Name="lblHello">欢迎你光临WPF的世界!</Label>
<Button Name="btnThd" Click="btnThd_Click" >多线程同步调用</Button>
</StackPanel>
</Grid>
</Window> MainWindow.xaml.cs代码:
using System;
using System.Threading;
using System.Windows;
namespace WpfApp
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ModifyUI()
{
// 模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(2));
lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
}
private void btnThd_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(ModifyUI);
thread.Start();
}
}
} 错误截图:
2、正确的更新方式,从上例中我们看到了从子线程中直接更新UI线程创建的对象,会报错。应该如何修改呢?我们把上面的代码修改成如下,再来看看会是什么效果。
private void ModifyUI()
{
// 模拟一些工作正在进行
Thread.Sleep(TimeSpan.FromSeconds(2));
//lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
lblHello.Content = "欢迎你光临WPF的世界,Dispatche 同步方法 !!";
});
}
当然Dispatcher类也提供了BeginInvoke方法,我们也可以使用如下代码,来完成对Lable的Content的更新。
MainWindow.xaml 补充代码:
<StackPanel>
<Label x:Name="lblHello">欢迎你光临WPF的世界!</Label>
<Button Name="btnThd" Click="btnThd_Click" >多线程同步调用</Button>
<Button Name="btnAppBeginInvoke" Click="btnAppBeginInvoke_Click" >BeginInvoke 异步调用</Button>
</StackPanel> MainWindow.xaml.cs补充代码:
private void btnAppBeginInvoke_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(2));
this.lblHello.Content = "欢迎你光临WPF的世界,Dispatche 异步方法!!" + DateTime.Now.ToString();
}));
}).Start();
} 效果:
五、小结 在WPF中,所有的WPF对象都派生自DispatcherObject,DispatcherObject暴露了Dispatcher属性用来取得创建 对象线程对应的Dispatcher。DispatcherObject对象只能被创建它的线程所访问,其他线程修改 DispatcherObject需要取得对应的Dispatcher,调用Invoke或者BeginInvoke来投入任务。Dispatcher的一些设计思路包括 Invoke和BeginInvoke等从WinForm时代就是一直存在的,只是使用了Dispatcher来封装这些线程级的操作。
全部操作总结如下:
//方式一
new Thread(()=>{
this.Dispatcher.Invoke(new Action(()=>{
//通知主线程去完成更新
}));
}).Start();
//方式二
Application.Current.Dispatcher.Invoke(new Action(() => {
Application.Current.MainWindow.Title = "我修改过的窗体标题";
}));
//方式三:异步机制
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("任务已经完成");
}