DispatcherObject,Dispatcher,Thread之间的关系
我们都知道WPF中的控件类都是从System.Windows.Threading.DispatcherObject继承而来, 而DispatcherObject又在构造时与当前线程的Dispatcher关联起来,CurrentDispatcher如果为null则会主动new一个Dispatcher并且在构造时和当前创建它的线程关联起来了。因此整个链为DispatcherObject <- Dispatcher <- Thread. 具体我们一起看看反编译的红色代码:
public abstract class DispatcherObject
{
    private Dispatcher _dispatcher;
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public Dispatcher Dispatcher
    {
      get
      {
        return this ._dispatcher;
      }
    }
    protected DispatcherObject()
    {
      base .\u002Ector();
      this ._dispatcher = Dispatcher.CurrentDispatcher;
    }
     ........................................................
}
public sealed class Dispatcher
{
      public static Dispatcher CurrentDispatcher
     {
      get
      {
        return Dispatcher.FromThread(Thread.CurrentThread) ?? new Dispatcher();
      }
     }
    private Dispatcher()
    {
     .............................
      Dispatcher._tlsDispatcher = this ;
      this ._dispatcherThread = Thread.CurrentThread;
     .............................
    }
     ..............................
}
这样设计的原则就保证:界面元素只有被创建它的线程访问
Dispatcher中Invoke,BeginInvoke和Win32中SendMessage,PostMessage的关系
上面我们提到wpf的界面元素只有被创建它的线程来访问,如果我们想在后台或者其他线程里该怎么办?
答案就是利用Dispatcher的Invoke和BeginInvoke,作用就是把委托放到界面元素关联的Dispatcher里的工作项里,然后此Dispatcher关联的线程进行执行。
所不同的是Invoke是在关联的线程里同步执行委托, 而BeginInvoke是在关联的线程里异步执行委托。
做过Win32或者MFC编程的童鞋们都知道win32中有SendMessage和PostMessage类似的概念,这些概念有什么内在关系呢?
其实Dispatcher的Invoke和BeginInvoke都是调用Win32的PostMessage传递窗体句柄和消息号,反编译代码如下:
    private bool RequestForegroundProcessing()
    {
      if (this._postedProcessingType >= 2)
        return true;
      if (this._postedProcessingType == 1)
        SafeNativeMethods.KillTimer(new HandleRef((object) this, this._window.Value.Handle), 1);
      this._postedProcessingType = 2;
      return MS.Win32.UnsafeNativeMethods.TryPostMessage(new HandleRef((object) this, this._window.Value.Handle), Dispatcher._msgProcessQueue, IntPtr.Zero, IntPtr.Zero);
    }
只不过Invoke在PostMessage后调用了内部返回值DispatcherOperation的wait方法,在执行结束后才返回。反编译代码如下:
      DispatcherOperation dispatcherOperation = this.BeginInvokeImpl(priority, method, args, isSingleParameter);
        if (dispatcherOperation != null)
        {
          int num = (int) dispatcherOperation.Wait(timeout);
          if (dispatcherOperation.Status == DispatcherOperationStatus.Completed)
            obj = dispatcherOperation.Result;
          else if (dispatcherOperation.Status == DispatcherOperationStatus.Aborted)
            obj = (object) null;
          else
            dispatcherOperation.Abort();
        }
WPF的消息泵和Win32的消息泵之间的关系
WPF的窗体程序都必须隐式或者显式调用Application.Run()来初始化WPF窗体。当Application.Run()调用时, 会在其内部调用Dispatcher.Run()方法。最终会在PushFrame()方法内初始化消息泵。
具体为:Application.Run() -> Dispatcher.Run() -> Dispatcher.PushFrame() -> Dispatcher.PushFrameImpl()
private void PushFrameImpl(DispatcherFrame frame)
    {
      MSG msg = new MSG();
      ++this._frameDepth;
      try
      {
        SynchronizationContext current = SynchronizationContext.Current;
        bool flag = current != this._dispatcherSynchronizationContext;
        try
        {
          if (flag)
            SynchronizationContext.SetSynchronizationContext((SynchronizationContext) this._dispatcherSynchronizationContext);
          while (frame.Continue && this.GetMessage(ref msg, IntPtr.Zero, 0, 0))
            this.TranslateAndDispatchMessage(ref msg);
          if (this._frameDepth != 1 || !this._hasShutdownStarted)
            return;
          this.ShutdownImpl();
        }
        finally
        {
          if (flag)
            SynchronizationContext.SetSynchronizationContext(current);
        }
      }
      finally
      {
        --this._frameDepth;
        if (this._frameDepth == 0)
          this._exitAllFrames = false;
      }
    }
Ok,从上述反编译的代码可以看到,WPF还是通过Dispatcher内部实现了传统的Win32消息循环, 如GetMessage,TranslateMessage,DispatchMessage。
下面是win32消息泵的实现, 大家可以对比下:
BOOL bRet;

while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

聊聊WPF中的Dispatcher的更多相关文章

  1. 聊聊WPF中字体的设置

    1. 今天帮同事调试一个字体的bug:TextBox中的中文显示大小不一致, 比如包含"杰","热". 原因是WPF针对点阵字体需要指定特定字体才能正确渲染, ...

  2. WPF:浅析Dispatcher

    本人文笔差.还是直接上代码吧.(本文假设你对WPF中的Dispatcher有一定的了解) 你觉得下面的代码可以正常执行吗? private void Button_Click(object sende ...

  3. WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)

    原文 WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object) 众所周知的,WPF 中多数对象都继承自 Dispatch ...

  4. WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null

    原文:WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null 在 WPF 程序中,可能会存在 Application.Curren ...

  5. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

  6. MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信

    MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...

  7. 【WPF】 Timer与 dispatcherTimer 在wpf中你应该用哪个?

    源:Roboby 1.timer或重复生成timer事件,dispatchertimer是集成到队列中的一个时钟.2.dispatchertimer更适合在wpf中访问UI线程上的元素 3.Dispa ...

  8. WPF线程(Step1)——Dispatcher

    使用WPF开发时经常会遇上自己建立的线程需要更新界面UI内容,从而导致的跨线程问题. 异常内容: 异常类型:System.InvalidOperationException 异常描述: "S ...

  9. WPF中Timer与DispatcherTimer类的区别

    前几天在WPF中写了一个轨迹回放的功能,我想稍微做过类似项目的,都晓得采用一个时间控件或者时间对象作为调度器,我在这么做的时候,出现了问题,于是将程序中的Timer换成了DispatchTimer,然 ...

随机推荐

  1. qt5.4.1的imx6编译

    2.到https://download.qt.io/archive/qt/5.4/5.4.1/single/下载源码包qt-everywhere-opensource-src-5.4.1.tar.gz ...

  2. linux 基础二---用户群租权限

    用户&群组&权限 一.用户 1.用户及passwd文件 1) 掌握/etc/passwd文件的功能:存储所有用户的相关信息,该文件也被称为用户信息数据库(Database). 2) / ...

  3. 使用ASP.Net MVC5 Web API OData和Sencha Touch 开发WebAPP

    使用ASP.Net MVC5 Web API OData和SenCha Touch 开发WebAPP Demo 效果 第一步 创建数据库 创建表 第二步 搭建MVC,并导入OData 第三步,写入We ...

  4. 继承Thread类与实现Runnable接口

    java中创建线程有两种方式: 1. 类继承Thread类,重写run方法,每创建一个实例对象即开启一个线程 2. 类实现Runnable接口,重写run方法,将实例对象传入新建Thread的方法: ...

  5. linux下安装jsp开发运行环境(centos7)

    1 开发环境包括 1)apache-tomcat 2)java-jdk 3)mysql 2 apache-tomcat安装(应该先装java再装tomcat) 1)到官网下载最新版本(不建议用yum安 ...

  6. Android系统--Binder系统具体框架分析(一)

    Binder系统具体框架分析(一) 一.Binder系统核心框架 1. IPC:Inter-Process Communication, 进程间通信 A进程将数据原原本本发送B进程,主要负责进程间数据 ...

  7. .gitignore 无效

    利用.gitignore过滤文件,如编译过程中的中间文件,等等,这些文件不需要被追踪管理. 现象: 在.gitignore添加file1文件,以过滤该文件,但是通过Git status查看仍显示fil ...

  8. JS中跨域问题

    这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被 ...

  9. js字符串和数组的相互转化

    一.数组转字符串 需要将数组元素用某个字符连接成字符串,示例代码如下: var a, b; a = new Array(0,1,2,3,4); b = a.join("-"); 二 ...

  10. img标签显示本地文件

    html: <img src="__IMG__/male.png" id="imgfpic1" style="height: 100%; wid ...