ShowDialog() after Application.Run()

  • Thread starter Thread starter Joe White
  • Start date Start date
J

Joe White

Okay, here's an odd one. If I do something like this:

// First, call Application.Run()...
Application.Run(new MyMainForm());
// then show a dialog afterward
(new MyDialog()).ShowDialog();

then the call to ShowDialog() doesn't work properly: the dialog box flashes
briefly onscreen, then immediately closes. How's that for peculiar? It's
like there's some "terminated" flag that never gets reset... except that
calling Application.Run() multiple times in succession works just fine, so
clearly the terminated flag *does* get reset (at least when you're calling
Run).

I've looked briefly at the System.Windows.Forms code using Reflector, but
it's a jungle in there -- undocumented classes galore, thread contexts and
local modal message loops, exit notifications going through two or three
levels of events firing other events -- and I just don't grok it all. Does
anyone know the details well enough to have some insight into why the
ShowDialog isn't working after Run?

I know I can work around this any number of ways (I'll probably either do an
ApplicationContext descendant, or call Run() on my dialog instead of
ShowDialog()). I'm just really puzzled -- I can't see any reason why this
shouldn't work as-is.

Here's some code that does a (very) slightly more detailed test. Paste it
into your Main() method and run. (I'm running VS.NET 2003.)

// This shows up just fine...
Form dialog1 = new Form();
dialog1.Text = "Dialog (before Run)";
dialog1.ShowDialog();

// Now run the application...
Form form = new Form();
form.Text = "Form (Run)";
Application.Run(form);

// Try to show another dialog. It flashes onscreen
// and then closes immediately. Why?
Form dialog2 = new Form();
dialog2.Text = "Dialog (after Run)";
dialog2.ShowDialog();
 
Thanks for the suggestion, but:
(a) The Application class doesn't have a Load event.
(b) If you meant the main Form's Load event, that fires *before* the form
is shown. I'm trying to show my dialog *after* the form is closed.

Your suggestion did lead me to try a couple of other things:
1. Put the call to ShowDialog() in the main form's Closed event. No
good -- Closed fires before the form is hidden.
2. Put the call to ShowDialog() at the end of the main form's Dispose(bool)
method, after the call to base.Dispose(disposing). The main form is indeed
hidden by that point, but ShowDialog() again does the
flash-the-form-onscreen-briefly-then-close-it-immediately thing.
3. Same as 2, but put the ShowDialog() call immediately before the call to
base.Dispose(disposing). This has the same effect as 1 -- the dialog
appears just fine, but it appears before the main form has closed.

So something about the main form's "base.Disposed(disposing)" call is
mucking things up for later ShowDialog() calls. Has anyone looked through
the Form.Dispose(bool) code (and the methods that it calls and the events
that they fire, and all that mess) and noticed anything odd that might
account for this behavior?

What I'm trying to accomplish is this: show an Options dialog, then (if the
user doesn't Cancel) run the main window; then, when the user closes the
main window, show the Options dialog again (and then the main form again,
and the Options dialog again). The two forms will take turns showing in a
loop, until the user cancels the Options dialog. (This is for a DirectX
game, so the "main window" might be running fullscreen, and closing it is
the easiest way to get back to a dialog box -- especially if you picked a
fullscreen video mode that doesn't work on your monitor.) Yes, there are
other ways of making this work; I'm just puzzled as to why the obvious way
(calling ShowDialog(), then Application.Run(), then ShowDialog() again)
doesn't work.
 
Joe,

The reason it doesn't work is to do with the architecture of a windows
application. All windows apps have a 'message loop' in which windows sends
messages to the app (cursor movements, key strokes, timer events etc). Each
time a message is received it is then passed to a form's windows function,
which processes that message. The application shuts down when the message
loop exits, and at that point no forms can be active or respond to messages
(events).

In WinForms, the message loop is in the Application.Run call, so when the
application shuts down this exits.

It is possible to create instances of new forms at this point, but as you
noticed you need to call Application.Run to start a new message loop to
process any incoming events.

You can use the Closed event and within it call the form's Hide method to
make it invisible. As far as the user is concerned, the form is then
closed, but you can pop up other forms. When you want to show the main form
again, call its Show method.

Hope this makes things clearer,

Neil.
 
The event Form1_close occurs after the form has been closed. To bring
up your
dialog, you need to say that in Form1_closing.

The closing event occurs before the form is closed. Here you can
specify if you want the form to be closed or cancel it. If you want
the form closed say e.cancel = false.


Priyanka
 
Sure, of course there has to be a message loop. That's why ShowDialog() has
its own.

This code works perfectly:

(new MyDialogForm()).ShowDialog();
Application.Run(new MyMainForm());

Even though Application.Run() hasn't yet been called, and therefore hasn't
started up its message loop, you can still call ShowDialog() and everything
works fine. That's because ShowDialog() starts up its own modal message
loop much like the one in Application.Run().

In fact -- I was curious, so I just opened Reflector and looked through the
code -- ShowDialog() and Application.Run() both end up calling the same
method, ThreadContext.RunMessageLoop(). Same message loop in both cases,
same implementation!

But switching the order of the above two lines of code causes ShowDialog()
to close the dialog immediately after it's shown. It's weird. I can work
around the problem, but I'd like to know why it's acting this way.

I wondered if the WM_QUIT was somehow hanging around the message queue and
never getting removed. But calling Application.Run() twice in a row works
with no problems, so that isn't likely the issue. Something isn't cleaning
up its state, but I don't know what.

Anyway, your suggestion of showing/hiding the main form isn't a bad idea.
I'll need to restructure the code a bit, but it'll probably end up being a
better solution than what I've got now. (Actually, if I go that route, I'll
probably keep the main form visible all the time, and just show/hide or
destroy/re-create its child control; that way the app's taskbar button won't
keep disappearing and reappearing. Cleaner than showing/hiding forms all
the time.)
 
Back
Top