Department of InformatiX
Microsoft .NET Micro Framework Tools & Resources

The main problem of displaying a message box is to keep the parent window blocked. Well not exactly, as you could for example create an infinite loop which will wait until your message box is closed, which is rather a not very good idea, as it would stress the device completely away. A little bit better one would be to think about some kind of thread blocking or synchronization, but you really don't want to block the dispatcher thread, as it has much more important things to do than staring at your message box. In fact, the problem is to keep the window blocked yet responsive - for example it should keep redrawing itself when needed.

Dispatcher and the others

The WPF model introduced Dispatcher, DispatcherObject, DispatcherTimer and lots of other Dispatcher- things. The Dispatcher maintains a queue of tasks for one thread to execute. It is a list of delegates, called one after another. Typical tasks in the queue are event related - like repaint, or button down. In the .NET Framework, the Dispatcher orders the tasks by their priority, but here in .NET Micro Framework, all tasks are considered to be equally important.

Since the Dispatcher is de facto only a queue, there must be something to check the queue for items and execute them, in a loop. This is where DispatcherFrame comes in. The DispatcherFrame is actually a terminable while loop. Here is what the running dispatcher basically does:

while (true) { DispatcherOperation operation = DeQueue(); // check if we have any delegate waiting in the queue if (operation != null) // if yes, operation.Invoke(); // invoke it; else // if not, _event.WaitOne(); // wait for it! }

Please note that on the .NET Framework, there is a standard message pumping happening inside the loop (due to interopability with Win32 which we are free of) and the dequeuing is not that confessed. Now if you look on the code, you can see that it actually never returns, which is not exactly what we need. If anyone presses a button in your application and you display a message box, you would probably like to continue executing the button handler when the message box is closed. So how does the DispatcherFrame help? If you look on its list of members, you will find that it has just one single property, named Continue. You start the loop above by calling Dispatcher.PushFrame(DispatcherFrame frame) and the code is in fact with a little difference:

while (frame.Continue) { // dispatch the delegates here }

During execution of your application you can whenever set the frame.Continue value to false, the loop will terminate and your application will continue after the call to PushFrame.

Displaying a modal window

By knowing this, you might be able to figure out how to display a modal window now:

public static void ShowModal(/* this */ Window window) { // The constructor parameter exitWhenRequested sets if the frame will be a good one, // and will terminate the loop when requested, eg. on Application.Shutdown(). // This is the default setting and the frame is set to Continue by default, so the DispatcherFrame modalBlock = new DispatcherFrame(); // is equivalent to modalBlock = new DispatcherFrame(true); modalBlock.Continue = true; // Add a handler which could terminate the loop when appropriate window.IsVisibleChanged += delegate { Modal_VisibleChanged(modalBlock, window.Visibility); }; window.Visibility = Visibility.Visible; // or your other window showing logic here Dispatcher.PushFrame(modalBlock); // start the infinite loop and block the calling thread // until the loop is terminated // Detach the handler to free up resources window.IsVisibleChanged -= delegate { Modal_VisibleChanged(modalBlock, window.Visibility); }; window.Close(); // or your other winow closing logic here } private static void Modal_VisibleChanged(DispatcherFrame block, Visibility visibility) { if (visibility!= Visibility.Visible) // when the windows was hidden block.Continue = false; // set the frame to exit the loop }

You can put this static method wherever your want, eg. in your Program class. Create your window as usually, and in the button handler or wherever, call Program.ShowModal(yourWindow) and that's it! Your dialog box will now be showed, and will be executing the dispatcher operations while blocking the calling thread. To resume that thread, just set the modal window's Visibility to Hidden. The dispatcher will finish the currently executed operation and return from the PushFrame method.

By the way, the Dispatcher class maintains a depth of the frames, so your modal window could actually call this method as well and display another modal window, blocking it and the original calling thread too, and so on. When there is no frame to execute (depth is 0), the Dispatcher will shutdown, but this is another part of the story. Don't worry you will cause this unintentionaly, as running your application by calling Application.Run() pushes its own DispatcherFrame.

DoEvents

In Windows Forms model, you can call a DoEvents() method, which will cause the pending operations to complete. This is often used when you do some heavy work with the UI and want to wait until it repaints. The Windows Presentation Foundation is based on a completely different model and approach, so this method is not available (and actually not needed, if you make things the right way). However, it is technically possible to implement it using the DispatcherFrame:

public void DoEvents() { DispatcherFrame doEventsFrame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(new DispatcherOperationCallback(ExitFrame), doEventsframe); Dispatcher.PushFrame(doEventsFrame); } public object ExitFrame(object f) { ((DispatcherFrame)f).Continue = false; return null; }

It will put a delegate to the end of the queue, so it will be called when all other pending operations are completed. The delegate will then just tell the frame to exit, so that your application can continue after that. Although DoEvents may seem to help you in some situations, always ensure there is no better way to do your task.

Comments
Sign in using Live ID to be able to post comments.