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);
}