Introduction
In the previous article “Learn more about how WPF Dispatcher works. (Invoke and InvokeAsync)”, we found that Dispatcher.Invoke depends on Dispatcher.PushFrame method to wait without blocking. But how does Dispatcher.PushFrame work?
In this blog, we will introduce more details about Dispatcher.
Details
The original link is 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分). But this blog was written in Chinese, so I translate its content to English. Waterlv is an MVP(Microsoft Most Valuable Professional), and he is good at .NET Core\WPF\.NET. Here is his Blog.
Dispatcher.PushFrame
If you are a WPF developer, you must have known the ShowDialog method of the Window class. But do you know how does ShowDialog method work? Why does the method which calls ShowDialog continue to execute after the window returns?
1 | var w = new FooWindow(); |
To answer these questions mentioned above, we have to read the source code of Dispatcher.PushFrame method. But before reading, we should learn some knowledge about the DoEvents method in Windows Forms.
DoEvents
The DoEvents method in the Windows Forms allows you to insert a UI rendering operation during executing a time-consuming operation, and it makes your application look like it doesn’t stop responding.
Here are the codes of DoEvents.
1 | [] |
Firstly, we should know the conclusion mentioned above that Dispacther.PushFrame method can wait without blocking UI thread. ( The theory will be posted below, but now just remember this conclusion.)
And base on the conclusion, we now analyze the theory of DoEvents which is posted above. And its steps are as followed.
Add an instance of
DispatcherOperationwithBackgroundpriority (4) to execute theExitFramemethod.Call the
Dispatcher.PushFramemethod to wait without blocking UI thread.Due to the priority of user’s input is
Input(5) and the priority of the UI response isLoaded(6) and the priority of rendering isRender(7), they all are higher thanBackground(4), so theExitFramemethod can only be executed after all UI task have been executed.The value of
Dispatcher.Continuewill be set tofalsewhen executing theExitFramemethod.
Base on the function of DoEvents, we can guess that the goal of setting the value of DispatcherFrame.Continue to false is to end the wait of Dispatcher.PushFrame(frame), so that the subsequent codes can be executed.
So according to the guess, we may know the steps of waiting without blocking UI thread.
Call
Dispatcher.PushFrame(frame)to wait without blocking.Set
frame.Continue = falseto end the wait and execute the subsequent codes.
It is easier to read the source codes of the Dispatcher.PushFrame with the guess and information above.
Source codes of PushFrame
Here are the codes.
1 | [] |
There are two points which need to pay attention to :
_frameDepthfield.- The codes which are surrounded by
while.
Let’s begin with the _frameDepth field. Every time you call the PushFrame method, you should pass an instance of DispatcherFrame, and the _frameDepth field will add 1 when another PushFrame is called during the period of PushFrame. So, one by one, the DispatcherFrame is nested in layers.
Then, we read the codes surrounded by while.
1 | while(frame.Continue) |
Do you remember the guess mentioned above? After reading these codes we noticed that the condition of the while is frame.Continue, and if its value is false the loop will be exited, and the PushFrame method will be returned, then the _frameDepth field will subtract 1. If all the frame.Continue are set to false, the Main method will be exited.
And, what will happen if the value of frame.Continue is always true? Obviously, it will enter a dead loop. But you can’t insert any UI operations to a dead loop, so how does it execute the UI operations? In the codes of this method, there are two possibilities, one is that GetMessage method allows the application to continue processing window messages, the other is that TranslateAndDispatchMessage method allows us to continue processing window messages. (The task queue of Dispatcher depends on the message mechanism of windows which is mentioned in the previous article).

Unfortunately, both methods call the unmanaged code, it is hard to know their theories by reading the source codes. But, we can debug the .NET Framework source code by source code debugging technology. And we found that GetMessage method is running all the time, and the TranslateAndDispatchMessage does not seem to be called. So we believe that the key to waiting without blocking is in the GetMessage method. For more information about .NET Framework source code debugging technology, visit 调试 ms 源代码 - 林德熙.
After reading the codes of GetMessage, we found messagePump which is an instance of UnsafeNagtiveMethods.ITfMessagePump. And we can know how does message loop work by using the source code debugging technology.
Debugging the source code to get how does PushFrame work
We add OnStylusDown method for MainWindow‘s StylusDown event.
1 | private void OnStylusDown(object sender, StylusDownEventArgs e) |
In these codes, both Dispatcher.Invoke and ShowDialog methods will call PushFrame. After running this application, every time we touch the MainWindow, we can found that there will add two PushFrame in the call-stack subwindow in VS. One is called by the Invoke method and the other is called by ShowDialog.
After each PushFrame executes, there is a transition between host and manage. Then message processing is followed, and the touching message is called from the message processing.
So, it’s sure that every time we execute PushFrame the unmanaged code will open a new message loop. When the window showed by ShowDialog closed or the Invoke has finished the message loop which is created by PushFrame will be exited. Therefore, the subsequent codes which are blocked by while can be executed. After the PushFrame of the Main method is exited, this application will close.

Conclusions
- Every time the
PushFrameexecuted, a new message loop will be created, and the_frameDepthwill add 1. - In the new message loop, it can handle all kinds of Window’s message, some of them are transmitted in the form of events, and some are tasks that are added to the
PriorityQueue<DispatcherOperation>. - After exiting the
PushFramemethod, the message loop which is created by this code will be exited too. And the subsequent code of previousPushFramewill be executed. - If all the
PushFrameexited, the application will be closed. - The
Whileloop inPushFramewill block the main thread, but it can handle the messages in theloop, so it looks like the main thread is not blocked.
The defects of PushFrame
PushFramedepends on the Windows’ message loop, and there are some bugs about multiple message loops. Like,Base on
PushFrame‘s block mechanism, the unexpected reentrancy problem may happen is a single thread application. visit 异步任务中的重新进入(Reentrancy) for more information about theReentrancy.
References
PushFrame/DispatcherFrame
Message loop of Windows
- c# - PushFrame locks up WPF window when user is moving window - Stack Overflow