Introduction

Back in the days when I used FoxPro, we would always pop up a wait window while running any long process. Come .NET, there is no built in wait window, so you have to roll your own. This has become an increasing issue as we issue calls to web services that may be fast or slow. In the mean time, our users need to know the application has not crashed. They should also not be able to click on any other part of the application until the process has finished, in the same manner as the old FoxPro wait window.

Background

There are a few details that need to be covered for a good wait window.

During our process, we want to provide an animation on the wait window to ensure the user believes the application has not crashed. For the purposes of this article, I have used a ProgessBar, but you could equally use an animated GIF, or some custom animation scheme of your own devising. The side effect is that the worker method must execute on a secondary thread or it will block the GUI updating the animation.

Also, we cannot use a BackgroundWorker for this as we want the execution to occur inline. For example, we want to call the wait window specifying the method to execute, then expect it to have finished by the time we hit the next line of code. Therefore we will have to roll our own threading code.

Using the Code

I have built the sample code with the wait window in a separate assembly so you can just reference the assembly if you want. However, I expect most people will copy the classes into their own project structure, which is fine.

Before anyone asks, the sample code includes the project in C# and in VB.NET, and while I have compiled it against the 3.5 framework, it should be fine built against any .NET Framework version, though you would have to manually copy the code into a Visual Studio 2005 project if you wanted to use the older solution format.

First, you will need to write your method to execute. Remember this is running on a separate thread, so pass everything in and return the results, or ensure any calls you make are guaranteed thread safe!

To make things easier, the event args for the method include a collection of arguments that you can pass in, and a reference to the WaitWindow object so that the message displayed can be updated during the process. The code below shows most of the things you may want to do. For more examples, see the demo project.

Next, you need to call the WaitWindow with the method you have created. The example below just uses the default, "Please wait..." message, but you can also pass in the message to display, and any arguments for the worker method. Again, there are more examples in the demo project.

object result = WaitWindow.Show(this.WorkerMethod);

There are two methods exposed by the WaitWindow object that can be called from within the worker method. These are the methods, Cancel() and the property, Message. These cancel the execution immediately, and update the message displayed in the WaitWindow respectively.

Points of Interest

In the first release, I did some clever stuff to make a modeless window appear modal, however it was pointed out that I did not actually need this at all.

If you do want to do this for some other reason, you have to capture the mouse and then capture all the windows messages and ensure that only the ones you want get passed on to the rest of the application.

We pass the Form into the AddMessageFilter method to hook up the form to receive all the windows messages first.

System.Windows.Forms.Application.AddMessageFilter(this._GUI);

Inside the code for the window, we can now implement the IMessageFilter interface giving us a PreFilterMessage method. All that remains is to return true for the mouse and keyboard events, rather than the default false and the window will now act as if it were modeless when in fact it is modal.

A shame that I ditched that rather neat piece of code because it was a pointless waste of time, but that's development for you, a constant learning process.

History

First release - March 2010

Release 2 - Updated for simpler threading model (Many thanks to John Brett for his input)

Share

About the Author

Unfortunately my real name was already in use as a code project login. For those of you who are wondering I am really Napoleon Solo. Sorry, I mean, Mark Jackson. Well, I do look a bit like him I think.

I downloaded the solution to make sure I hadn't uploaded some rubbish. I then repointed the demo project to reference the VB version and I did not get the error you describe.

However, I notice you have created your own calling code in VB (my demo project was C#)
Could you post the method declaration stubs for the worker methods please, as I thing you may have a typo there somewhere.

This just feels like the wrong approach to a fairly common problem. Hooking an event filter and calling DoEvents makes me cringe.
A far better (more common?) template would be to start the background thread, and hook its activity events in a please-wait modal form. No thread complexities (aside from the ubiquitous Invoke to the UI thread), no calls to DoEvents, nothing complex.