Dispatcher介绍[转]
WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢?
不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是子线程。在WPF或WinForm应用程序中,主线程负责接收输入、处理事件、绘制屏幕等工作,为了使主线程及时响应,防止假死,在开发过程中对一些耗时的操作、消耗资源比较多的操作,都会去创建一个或多个子线程去完成操作,比如大数据量的循环操作、后台下载。这样一来,由于UI界面是主线程创建的,所以子线程不能直接更新由主线程维护的UI界面。
Dispatcher的作用是用于管理线程工作项队列,类似于Win32中的消息队列,Dispatcher的内部函数,仍然调用了传统的创建窗口类,创建窗口,建立消息泵等操作。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher。对于线程来说,它对Dispatcher是一无所知的,Dispatcher内部维护了一个静态的 List<Dispatcher> _dispatchers, 每当使用CurrentDispatcher方法时,它会在这个_dispatchers中遍历,如果没有找到,则创建一个新的Dispatcher对 象,加入到_dispatchers中去。Dispatcher内部维护了一个Thread的属性,创建Dispatcher时会把当前线程赋值给这个 Thread的属性,下次遍历查找的时候就使用这个字段来匹配是否在_dispatchers中已经保存了当前线程的Dispatcher。
1) System.Object 类:大家都知道在.Net中所有类型的基类,DispatcherObject 就继承于它,所以它是WPF的基类。
3) System.Windows.DependencyObject类:对WPF中的依赖项属性承载支持与 附加属性承载支持,表示参与 依赖项属性 系统的对象。
4) System.Windows.Media.Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算等。
7) System.Windows.Controls.Control 类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。
8) System.Windows.Controls.ContentControl类:表示没有任何类型的内容表示单个控件。
WPF的绝大部分的控件,还包括窗口本身都是继承自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 类:表示可用于提供项目的集合的控件。
c.每种ItemsControl都对应有自己的条目容器(Item Container).
Menu | MenuBase | ContextMenu | ComboBox |
ItemsControl | ListBox | ListView | TabControl |
TreeView | Selector | StatusBar |
|
11)System.Windows.Sharps.Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。
在 WPF 中,DispatcherObject 只能通过与它关联的 Dispatcher 进行访问。 例如,后台线程不能更新由 UI 线程创建的 Label的内容。
优先级 | 说明 |
Invalid | 这是一个无效的优先级。 |
Inactive | 工作项目已排队但未处理。 |
SystemIdle | 仅当系统空闲时才将工作项目调度到 UI 线程。这是实际得到处理的项目的最低优先级。 |
ApplicationIdle | 仅当应用程序本身空闲时才将工作项目调度到 UI 线程。 |
ContextIdle | 仅在优先级更高的工作项目得到处理后才将工作项目调度到 UI 线程。 |
Background | 在所有布局、呈现和输入项目都得到处理后才将工作项目调度到 UI 线程。 |
Input | 以与用户输入相同的优先级将工作项目调度到 UI 线程。 |
Loaded | 在所有布局和呈现都完成后才将工作项目调度到 UI 线程。 |
Render | 以与呈现引擎相同的优先级将工作项目调度到 UI 线程。 |
DataBind | 以与数据绑定相同的优先级将工作项目调度到 UI 线程。 |
Normal | 以正常优先级将工作项目调度到 UI 线程。这是调度大多数应用程序工作项目时的优先级。 |
Send | 以最高优先级将工作项目调度到 UI 线程。 |
下面我们来用一个实例,来看看如何正确从一个非 UI 线程中更新一个由UI线程创建的对象。
<Window x:Class="WpfApp1.WindowThd"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowThd" Height="300" Width="400">
<Label x:Name="lblHello">欢迎你光临WPF的世界!</Label>
<Button Name="btnThd" Click="btnThd_Click" >多线程同步调用</Button>
<Button Name="btnAppBeginInvoke" Click="btnAppBeginInvoke_Click" >BeginInvoke 异步调用</Button>
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
public partial class WindowThd : Window
Thread.Sleep(TimeSpan.FromSeconds(2));
lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
private void btnThd_Click(object sender, RoutedEventArgs e)
Thread thread = new Thread(ModifyUI);
2、正确的更新方式,从上例中我们看到了从子线程中直接更新UI线程创建的对象,会报错。应该如何修改呢?我们把上面的代码修改成如下,再来看看会是什么效果。
Thread.Sleep(TimeSpan.FromSeconds(2));
//lblHello.Content = "欢迎你光临WPF的世界,Dispatcher";
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
lblHello.Content = "欢迎你光临WPF的世界,Dispatche 同步方法 !!";
当然Dispatcher类也提供了BeginInvoke方法,我们也可以使用如下代码,来完成对Lable的Content的更新。
private void btnAppBeginInvoke_Click(object sender, RoutedEventArgs e)
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
Thread.Sleep(TimeSpan.FromSeconds(2));
this.lblHello.Content = "欢迎你光临WPF的世界,Dispatche 异步方法!!"+ DateTime.Now.ToString();