Introduction

One of my pet peeves is forms that don't instantly appear. You see it all the time: the user clicks a button, then waits a few seconds until the expected UI appears. This is typically due to the newly-created form performing some time-consuming (blocking) task in its Load() event handler (or, in the non-.NET world, the WM_INITDIALOG message handler). Aside from being poor UI design (never leave the user wondering what's happening!), it can have some really undesirable consequences: a button-click doesn't have an immediate effect, users will tend to click the button again, which can result in the action being invoked twice.

Since my earliest Windows programming, I've always implemented "instant feedback" in my Forms (f/k/a "dialog boxes"). My technique has evolved as Windows API has evolved; I'll discuss my older techniques (which can still be used in .NET), and end up with my current implementation using .NET WinForms APIs.

The Goal

Create a simple, reliable mechanism for doing post-form-load processing which ensures that the form is "fully rendered" before the post-load processing commences. This means that all the controls in the form itself as well as all child controls (in the dialog template) have been drawn as the user would expect to see them.

Background

This is a pretty basic technique; novice WinForms coders should be able to implement it.

Complicating the issue is the asynchronous, not-totally-predictable nature of messages. When a window is created (and thus its children are created), the exact sequence of the various windows' messages varies from instance to instance. Certainly, each window's messages occur in the same sequence every time, but the sequence of the parent/child messages is unpredictable, creating a race condition. The most unfortunate result of this being that sometimes a dialog will render properly, sometimes it won't. Removing this uncertainty - ensuring predictability - is a big part of this solution.

Finally, it's very important to perform your blocking processing after the form and all children are fully rendered. Blocking the UI thread (and message queue) mid-render, results in some pretty partial ugly UI that looks like your app crashed!:

Notice how the form's frame and unclipped client area have rendered but certain child controls' client areas still show the UI "below" the form. Also notice how the listbox has rendered but the other controls (GroupBoxes, ComboBoxes) have not (I haven't investigated why that is).

Here's how it should look fully rendered:

Solution: Before .NET

(If you wrote Windows code before .NET - with or without MFC - this should look familiar.)

Here's a simplified sequence of the messages generated upon window creation:

The theory here is essentially the same: Set a quick (50ms) timer in the initial activation. The WM_TIMER message occurs asynchronously and with some extra delay. The timer is immediately killed (we only wanted the first timer message), then the derived class' DoPostLoadProcessing() is called. (Yeah, I know that timers < 55ms are useless on a PC.)

Both techniques work pretty well, and could still be used in .NET, though they're messy.

Solution: Doing it in .NET

The basics

.NET's addition of the Shown() event simplifies things greatly. Now, in theory, all you'd need to do is handle the Shown() event and do your processing there:

Simple, right? Well, almost. It turns out that the Shown() event gets fired before all the form's child controls have fully rendered, so you still have the race condition. My solution to that is to call Application.DoEvents() to clear out the message queue before doing any additional post-processing:

A more complete solution

The problem with the basic solution above is that you must remember to call Application.DoEvents() in each form's Shown() event handler. While this is admittedly a minor nuisance, I've chosen to take the solution one step further by implementing a base dialog class that handles the Shown() event, calls DoEvents(), then invokes an event of its own:

(I named the event LoadCompleted so that it would appear immediately after the Load event in the events pane, making it easier to find.)

With that, you can do all your long-duration stuff in LoadCompleted() and be confident that the basic UI will be fully rendered while the user waits. Of course, you should still follow good UI practices such as showing a WaitCursor, maybe disabling the controls until they're usable (a useful visual cue to the user), and perhaps even showing a progressbar for really long waits (e.g.: show a marquee progressbar during a SQL call that takes 5+ seconds).

Demo project

The attached VS2008/C# project demonstrates the behavior and the solution. It implements a form with four buttons:

All four buttons do the same thing, which is pop up a child dialog containing:

A listbox that gets populated with 50K items. This population blocks the UI thread for a couple of seconds, which allows the app to demonstrate the solutions to the problem.

A few other controls, principally ComboBoxes. For whatever reason, ComboBoxes are particularly prone to incomplete rendering due to blocking code.

Each child form sets the WaitCursor in its Load() event as a visual feedback that things are happening.

However, each button causes a different form-loading behavior:

Do processing in Load event - the listbox is loaded in the form's Load() event handler, resulting in the form not appearing until the listbox is fully loaded (bad).

Open Form Without DoEvents - the listbox is loaded in the form's Shown() event handler, but Application.DoEvents() is not called, resulting in the form appearing partially rendered until listbox-load completes (very bad).

Open Form *with* DoEvents - the listbox is loaded in the form's Shown() event handler andApplication.DoEvents()is called, resulting in the form appearing fully rendered until listbox-load completes (good!!!).

Open Form derived from BaseForm - Same as Open Form *with* DoEvents, but implemented using a base class and custom event.

Conclusion

Like so many things in Windows (and in life!), the solution to this problem is pretty simple, but figuring it out takes a bit of trial and error. I hope that my solution outlined above saves you some time and helps you create more responsive UIs.

Hey!! this is a nice piece of work. Thank you very much for sharing these tricks.

I've implented this succesfully on my applications but I did find some curious problem that seems to appear randomly: On some forms the OnShown event won't be raised and I was not able to figure out why, this is most strange because it works fine on most cases and I use a class that inherits the Form class and handles the event for me, so there should be no way for me to mess up with the event handling.

Did anybody experience this problem?? Were you able to fix it?? I'd be most thankful if anyone could offer a piece of advise so I can solve this.

Historically, ListBoxes have been limited to 64k (0xFFFF) items. This was originally a short-int (signed 16 bit; <=32k, and anything over 32k rolled over to a negative number and caused bad things), then sometime around Win98 they fixed this to be a WORD (unsigned 16 bit) with the current limit of 64k.

I'd imagine that MSFT has never increased the limit beyond 64k because, beyond several hundred items, it's the wrong tool for the job and results in crapppy UI.

I once had a requirement for a listbox that needed to contain the Lotus Notes email address for *every* USPS employee. It was ridiculous, but that's how the USPS's IT manager wanted it (the guy was a mid-level, power-mad douche), so that's how it had to be. They had something like 300k addresses and had to be fetched via a slow-ish IP-based API (this was around 1995) that took about 6 minutes to pull all 300k items. So the challenge was to (a) list >32k items in the listbox, and (b) have responsive UI when the user did type-ahead (ie. when the user typed 'n' to start pulling at 'n', skipping a-m).
My solution (in non-MFC C++) was a custom ListBox with my own custom, discontinuous Items array. It worked really well, but was a ton of custom code. dotNET has made this much easier, though I've only done it for ListView not ListBox.
I've not searched, but I'm sure that there's an example out there for a >64k ListBox.