WPF 学习笔记 - 2. Dispatcher
2009-06-04 18:56
323 查看
WPF 使用一个专用的 UI 线程来完成界面的操作和更新,这个线程会关联一个唯一的 Dispatcher 对象,用于调度按优先顺序排列的工作项队列。Application.Run() 实际上就是对 Dispatcher.Run() 的间接调用。
Dispatcher 通过循环来处理工作项队列,这个循环通常被成为 "帧 (DispatcherFrame)"。Dispatcher.Run() 创建并启动这个帧,这也是 Application.Run() 启动消息循环的最终途径。
public sealed class Dispatcher
{
[SecurityCritical, UIPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public static void Run()
{
PushFrame(new DispatcherFrame());
}
}
DispatcherFrame 可以嵌套,并通过检查 Continue 属性来决定循环是否继续。我们可以通过调用 Dispatcher.ExitAllFrames() 来终止所有的帧循环,当然这种编程方式并不可取,可能会造成一些意外出现。
与 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();
}
按照 DispatcherObject 的限制原则,我们改用 Window.Dispatcher.Invoke() 即可顺利完成这个更新操作。
private void button1_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
this.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => this.label1.Content = DateTime.Now.ToString()));
}).Start();
}
如果在其他项目(比如类库)中,我们可以用 Application.Current.Dispatcher.Invoke(...) 完成同样的操作,它们都指向 UI Thread Dispatcher 这个唯一对象。
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();
}
凡事都有例外,WPF 还提供了一种继承自 Freezable 的类型,尽管 Freezable 也间接继承自 DispatcherObject,但当这类对象从修改状态变成冻结状态时,它即变成自由线程对象,不在具有线程关联。(有关 Freezable 详情可参考 MSDN)
Dispatcher 通过循环来处理工作项队列,这个循环通常被成为 "帧 (DispatcherFrame)"。Dispatcher.Run() 创建并启动这个帧,这也是 Application.Run() 启动消息循环的最终途径。
public sealed class Dispatcher
{
[SecurityCritical, UIPermission(SecurityAction.LinkDemand, Unrestricted=true)]
public static void Run()
{
PushFrame(new DispatcherFrame());
}
}
DispatcherFrame 可以嵌套,并通过检查 Continue 属性来决定循环是否继续。我们可以通过调用 Dispatcher.ExitAllFrames() 来终止所有的帧循环,当然这种编程方式并不可取,可能会造成一些意外出现。
与 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();
}
按照 DispatcherObject 的限制原则,我们改用 Window.Dispatcher.Invoke() 即可顺利完成这个更新操作。
private void button1_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
this.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => this.label1.Content = DateTime.Now.ToString()));
}).Start();
}
如果在其他项目(比如类库)中,我们可以用 Application.Current.Dispatcher.Invoke(...) 完成同样的操作,它们都指向 UI Thread Dispatcher 这个唯一对象。
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();
}
凡事都有例外,WPF 还提供了一种继承自 Freezable 的类型,尽管 Freezable 也间接继承自 DispatcherObject,但当这类对象从修改状态变成冻结状态时,它即变成自由线程对象,不在具有线程关联。(有关 Freezable 详情可参考 MSDN)
相关文章推荐
- WPF and Silverlight 学习笔记(五):WPF应用程序管理
- WPF and Silverlight 学习笔记(十三):依赖项属性和路由事件
- WPF and Silverlight 学习笔记(二十一):数据绑定值的自定义转换
- WPF and Silverlight 学习笔记(二十八):基本图形的使用(3)图形的操作
- WPF学习笔记之-WPF数据绑定
- wpf学习笔记-简单绑定
- wpf学习笔记-数据绑定功能总结
- 2013/09/13 C# WPF 学习笔记 ---- 块儿
- WPF学习笔记:(一)数据绑定与DataContext
- WPF学习笔记第一篇-------界面布局
- wpf 学习笔记
- WPF基础知识学习笔记(一)XAML基础
- WPF学习笔记(2)--样式(Style)
- WPF 学习笔记 - 5. DependencyProperty
- WPF 学习笔记 - 12. Binding (5)
- 黑马程序员---wpf学习笔记四---banding的那些事
- 学习笔记:WPF学习之X名称空间详解
- 【转载】wpf学习笔记1
- 黑马程序员_学习笔记3——wpf提示框
- WPF学习笔记-依赖属性