ApplicationEx.ShowDialog and Form.Dispose

M

Myron Marston

First off, I want to say a big thank you to all the guys that created
the OpenNET SDF--even though I'm not using the entire library directly
in my app, I've found much of it to be extremely useful and helpful,
and I'm using several bits of source code in my project directly.

Recently, I've started using ApplicationEx and the IMessageFilter
interface so that I can prefilter windows messages (which works
wonderfully, by the way!). The problem I am running in to is the use
of ApplicationEx.ShowDialog(). This method disposes of the dialog form
when it's done with it. However, I don't want it to do this--I keep a
reference to my dialog around and show it again when the user clicks
the button again (besides, as I understand it, Form.ShowDialog() on
both the desktop and compact framework doesn't dispose the form). I
added a boolean flag parameter to ApplicationEx.ShowDialog so that I
could prevent it from disposing the form. However, when the user hits
the button and I call ApplicationEx.ShowDialog again, I get an
ObjectDisposedException. Specifically, this happens on Form.Show(). I
put a breakpoint in my dialog form's Dispose method, but it is never
being reached.

What could be causing the ObjectDisposedException when my form's
dispose is never being called? Is it possible to change
ApplicationEx.ShowDialog to allow it to be called multiple times on the
same form?

Thanks!
 
M

Myron Marston

Thanks to Lutz Roeder's .NET Reflector, I was able to diagnose the
problem and come up with a work around. In a nutshell, the problem is
that ApplicationEx.ShowDialog() calls Form.Show() rather than
Form.ShowDialog(). When this occurs, the behavior of Form.Close() is
different then if ShowDialog() is used. Form.Close() disposes of the
form by PInvoking AGL.WL.Close() (note that Form.Close() does not
explicitly call Form.Dispose(), which is why my Dispose method never
ran--must be a bug in the CF). In fact, when the form is non-modal,
calling AGL.WL.Close() is the only thing that Form.Close() does. When
I try to reuse the form, the ObjectDisposedException is thrown.

To work around it, Form.Close() should not be called after using
Form.Show(). However, ApplicationEx listens for the closed event of
the modal form; so in this case we need to listen for the closing event
instead and cancel the close.

static void ModalForm_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
ModalForm_Closed(sender, e);
e.Cancel = true;
}

public static DialogResult ShowDialog(Form form, bool dispose)
{
#if !NDOC && !DESIGN
if (form == null) throw new ArgumentNullException("form");

IntPtr lastWnd = Win32Window.GetActiveWindow();

// it seems that Form.Close() method processes Window Queue itself
that is why it is
// only way to be noticed the a dialog form was closed
if (dispose) form.Closed += new EventHandler(ModalForm_Closed);
else form.Closing += new
System.ComponentModel.CancelEventHandler(ModalForm_Closing);
form.Show();
form.Capture = true;
IntPtr hwnd = Win32Window.GetCapture();
form.Capture = false;

ThreadWindows previousThreadWindows = threadWindows;
threadWindows = new ThreadWindows(hwnd);
threadWindows.previousThreadWindows = previousThreadWindows;
threadWindows.Enable(false);

// enters dialog window loop
LocalModalMessageLoop();

if (threadWindows != null) threadWindows =
threadWindows.previousThreadWindows;

if (dispose)
{
form.Closed -= new EventHandler(ModalForm_Closed);
form.Dispose();
}
else form.Closing -= new
System.ComponentModel.CancelEventHandler(ModalForm_Closing);

if (IsWindow(lastWnd) && IsWindowVisible(lastWnd))
{
SetActiveWindow(lastWnd);
}
#endif
return form.DialogResult;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top