对于一个开发人员来说,可能在开发一个程序的时候对图形界面的要求并不是很高,因为他们不是美工。那么,使用了WPF后,开发人员可以轻松的实现精美的图形界面。#t#
使用一个专用的 UI 线程来完成界面的操作和更新,这个线程会关联一个***的WPF Dispatcher 对象,用于调度按优先顺序排列的工作项队列。Application.Run() 实际上就是对 Dispatcher.Run() 的间接调用。
WPF Dispatcher通过循环来处理工作项队列,这个循环通常被成为 "帧 (DispatcherFrame)"。Dispatcher.Run() 创建并启动这个帧,这也是 Application.Run() 启动消息循环的最终途径。
复制
public sealed class Dispatcher { [SecurityCritical,
UIPermission(SecurityAction.
LinkDemand, Unrestricted=true)] public static void Run() { PushFrame(new DispatcherFrame()); } }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
DispatcherFrame 可以嵌套,并通过检查 Continue 属性来决定循环是否继续。我们可以通过调用 Dispatcher.ExitAllFrames() 来终止所有的帧循环,当然这种编程方式并不可取,可能会造成一些意外出现。
与WPF Dispatcher调度对象想对应的就是 DispatcherObject,在 WPF 中绝大部分控件都继承自 DispatcherObject,甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了 Dispatcher 的线程(通常指默认 UI 线程)才能直接对其进行更新操作。
当我们尝试从一个非 UI 线程更新一个标签,会看到一个如下的异常。
复制
private void button1_Click
(object sender, RoutedEventArgs e) { new Thread(() => this.label1.
Content = DateTime.Now.
ToString()).Start(); }
1.
2.
3.
4.
5.
6.
7.
按照 DispatcherObject 的限制原则,我们改用 Window.Dispatcher.Invoke() 即可顺利完成这个更新操作。
复制
private void button1_Click
(object sender, Routed
EventArgs e) { new Thread(() => { this.Dispatcher.Invoke
(DispatcherPriority.Normal, new Action(() => this.
label1.Content = DateTime.
Now.ToString())); }).Start(); }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
如果在其他项目(比如类库)中,我们可以用 Application.Current.Dispatcher.Invoke(...) 完成同样的操作,它们都指向 UI Thread Dispatcher 这个***对象。
WPF Dispatcher还提供了 BeginInvoke 这个异步版本。
复制
private void button1_Click(object
sender, RoutedEventArgs e) { new Thread(() => { Application.Current.Dispatcher.
BeginInvoke(DispatcherPriority.Normal, new Action(() => { Thread.Sleep(3000); this.label1.Content =
DateTime.Now.ToString(); })); MessageBox.Show("Hi!"); }).Start(); }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
凡事都有例外,WPF 还提供了一种继承自 Freezable 的类型,尽管 Freezable 也间接继承自 DispatcherObject,但当诸如WPF Dispatcher这类对象从修改状态变成冻结状态时,它即变成自由线程对象,不在具有线程关联。