Introduction
Have you ever wondered what exactly DispatcherFrame
is? This article will explain it to you.
DispatcherFrame. Look in-Depth
I think that almost all of you heard about DispatcherFrame
. Let’s try to understand what is it and where it can be used? MSDN said: “Represent an execution loop in the Dispatcher
”. Not enough information to understand and use it as I think. Also, there is an example of a very useful procedure, but also, not obvious to understand. Here it is:
[SecurityPermissionAttribute(SecurityAction.Demand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame),
frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
This procedure is DoEvents
. As you can guess from its title – it forces events to be performed. So, let’s understand DispatcherFrame
and how this method works.
Let’s start with the Dispatcher
class. I think everybody who works with WPF knows what it is. It is a class that works with UI thread in WPF. It contains a queue of items that should be performed. I hope that everybody knows about Dispatcher
and I don’t need to describe it in detail.
To understand how Dispatcher
works, I use ildasm.exe utility to disassemble WindowsBase
assembly. I found Dispatcher
class and look at its IL. There is a method Run()
which starts the Dispatcher
work. Let’s look inside this method:
IL_0000:newobj
instance void System.Windows.Threading.DispatcherFrame ::.ctor()
IL_0005:call
void System.Windows.Threading.Dispatcher::PushFrame
(class System.Windows.Threading.DispatcherFrame)
New DispatcherFrame
is created and PushFrame()
method is called. Let’s go to the implementation of this method (I include here only the important lines):
IL_0039:ldarg.0
IL_0043:call
instance bool System.Windows.Threading.Dispatcher::GetMessage(valuetype
System.Windows.Interop.MSG&, int, int32, int32)
IL_004d:
call instance void
System.Windows.Threading.Dispatcher::TranslateAndDispatchMessage(valuetype
System.Windows.Interop.MSG&)
IL_0053:
callvirt instance bool System.Windows.Threading.DispatcherFrame::get_Continue()
IL_0058:
brtrue.s IL_0039
In a friendly manner, it looks like:
While (dispatcherFrame.Continue)
{
Dispatcher.GetMessage() Dispatcher.TranslateAndDispatch()}
So, while dispatcherFrame
’s Continue
property is true
, this cycle of reading system messages will be executed.
Let’s return to the MSDN definition: “Represent an execution loop in the Dispatcher
”. Exactly this WHILE
statement is represented by DispatcherFrame
.
When the application is started, Dispatcher.Run()
is called. Dispatcher
creates a default DispatcherFrame
which represents the main message loop in application. You can call Dispatcher.PushFrame(DispatcherFrame frame)
to start a new loop described above. Main
loop will be “paused” until new DispatcherFrame.Continue = false
.
Now, let's return to DoEvents()
for WPF.
[SecurityPermissionAttribute(SecurityAction.Demand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
Let’s skip the security attribute and go forward. Say, we have a very huge set of operations that should be performed in UI thread and while this application GUI should be updated. Since our operations are performed in UI thread, GUI will not be updated because Dispatcher
will be busy by these operations.
How Can This Problem be Solved?
In DoEvents()
, there is created new DispatcherFrame
(frame variable) and one “Invoke
” for dispatcher with Background
priority, which sets that frame shouldn’t be continued (frame.Continue=false
). Background priority is lowest.
When you call DoEvents()
, there is a set of items in Dispatcher
that should be performed. Also, this set contains an item which should update our GUI and should be performed ASAP. When PushFrame()
is called, the main loop is suspended and a new loop starts (as described above). Dispatcher begins to get system messages AND to process items queue depending on its priority. Dispatcher will process all items and one of those items is operation that stops our new loop. It has the lowest priority and will be performed last. After the operation is invoked, new loop is stopped and the flow returns to the main loop and huge operations processing.
History
- 31st January, 2011: Initial post