Closing a form from the Load handler
Asked Answered
F

14

14

I have a very strange behavior that only seems to happen on one form.

Basically I am creating an instance of a Form, and calling Show() to display the form non-blocking. In that form's Load event handler, I have some logic that may call this.Close() under certain circumstances. This closes the form, but then the form Show() method in the client code throws an ObjectDisposedException.

The stack trace from the ObjectDisposedException is as follows:

at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.Form.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.ContainerControl.FocusActiveControlInternal()
at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
at System.Windows.Forms.Control.Show()
...etc.

This is what I'm seeing happen:

  1. Control.Show() is called
  2. my form is launched
  3. the OnFormLoad method is called
  4. the FormLoad event handler is called, inside of which I call this.Close()
  5. the OnFormClosing method is called
  6. the FormClosing event handler is called
  7. Dispose is called on my form and all it's user controls

and then somewhere toward the end of the Control.Show() method, it tries to get a handle to the form, which freaks out and throws an exception because the object is marked disposed.

My real question is, why can I do this exact same thing on every other form I have without exceptions? Is it a GC issue? I've tried putting a GC.Collect() call right after the this.Close() and it makes no difference. Like I said, it happens 100% of the time on this form, and never anywhere else, regardless of child user controls, scope of the form variable, etc.

Any ideas?

Fagot answered 8/4, 2009 at 17:51 Comment(0)
V
8

I know this is an old issue but no one seemed to have posted the obvoius answer.

You say you call Control.Show() and then Form.Close() and then the form is Disposed of. Well, unless you use MDI or use ShowDialog that's just as documented. Though, the short version of the Close() documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.

See the remarks section: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx

If you want to show a form again. Use the Hide() method instead of Close().

Hope that helps other searching souls.

And guys, don't stop searching at "I don't know why it works sometimes". That becomes buggy software with lots of defensive "I'll call this method again just in case" stuff. Not good.

Vaccination answered 29/10, 2011 at 13:33 Comment(1)
I don't do windows dev anymore unfortunately, but this sounds right. Thanks!Fagot
F
38

The best way to do so :

 this.BeginInvoke(new MethodInvoker(this.Close));

this is the most simple way you wont get ObjectDisposedException

Flashcube answered 22/7, 2013 at 8:49 Comment(1)
This works but the form maybe flicker, because BeguinInvoke puts the command to close at the end of the event queue of the form.Odell
V
8

I know this is an old issue but no one seemed to have posted the obvoius answer.

You say you call Control.Show() and then Form.Close() and then the form is Disposed of. Well, unless you use MDI or use ShowDialog that's just as documented. Though, the short version of the Close() documentation is "Closes the form", it actually also disposes it implicitly under certain conditions.

See the remarks section: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.close.aspx

If you want to show a form again. Use the Hide() method instead of Close().

Hope that helps other searching souls.

And guys, don't stop searching at "I don't know why it works sometimes". That becomes buggy software with lots of defensive "I'll call this method again just in case" stuff. Not good.

Vaccination answered 29/10, 2011 at 13:33 Comment(1)
I don't do windows dev anymore unfortunately, but this sounds right. Thanks!Fagot
F
7

Ok, hate to answer my own question, but this was driving me nuts, and it was one of the hardest bugs to reproduce I've ever seen.

On my form I'm overriding the OnFormLoad and OnFormClose methods, where I save/restore the form's Size, Location, and WindowState to/from the registry. I took this code out and it fixed the problem. The weird thing is, I put it back and the problem didn't come back.

I finally reproduced the problem: you have to let the form open fully, maximize it, and then close it so that the Maximized state is saved to the registry. Then when you open it again, it will set it to Maximized, and if it closes in the Load handler, it tries to access the Size/Location as it's closing. Apparently accessing these values in the OnFormClosing method causes the form to try to focus IF AND ONLY IF the form is maximized, which is illegal, since the form has been disposed.

So basically, you can't access Form display properties in the OnFormClosing method of a form, if that form is going to call Close from it's Load event.(Unless you check the Disposed prop first)

pretty specific piece of Winforms wisdom I know, but I'm writing it down anyway.

Fagot answered 8/4, 2009 at 19:35 Comment(0)
D
4

If you want to close a form as if the user pressed the cross in the upper right corner (usually means cancel), just add the following code.

this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Close();

This also works in the form load function:

private void MyForm_Load (object sender, EventArgs e)
{
    // do some initializations

    if (!ContinueLoadingForm())
    {
         this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
         this.Close();
         return;
    }
    // continue loading the form
}

If you don't want the form to be visible for a short while, set the Visible property false (for example in the designer or constructor), and set it back to true when you are certain the program can continue loading.

Doubleness answered 30/10, 2013 at 9:25 Comment(1)
Can't seem to avoid the form being displayed for a moment though.Delftware
M
3

In load event is not realy good idea close the form. Do it after the Activated event.

Mazza answered 8/4, 2009 at 17:56 Comment(0)
S
3
protected override void CreateHandle()
   {
        base.CreateHandle();

        if (FormMustClose)  //FormMustClose is a variable in the loadevent.
        {
            Close();
        }
    }
Sezen answered 15/5, 2020 at 16:22 Comment(1)
Although this code might solve the problem, a good answer should also explain what the code does and how it helps.Workroom
A
1

It seems to me, without looking closely at it, that the cleanest way to accomplish what you want might be to make a custom form class deriving from Form, and override OnFormLoad(...) and/or Show() to check for your condition and cancel out early.

That said, I don't know why it would work sometimes and not other times.

Autoroute answered 8/4, 2009 at 17:55 Comment(1)
actually, that is the case here. I didn't mention it b/c all of my forms are that way. The base form overrides OnFormLoad and OnFormClose, but it just does registry stuff in those methods. The call sequence is the same in all forms.Fagot
I
1

One possibility:

They may have a timer on this form, that is being initialized and enabled in their FormLoad event. The timer would need to be disabled and stopped as well, before the form was closed, if the timer is trying to access the form when it's fired.

I've seen forms before that do this...

Illegitimacy answered 8/4, 2009 at 17:56 Comment(1)
good thought, but no timers on this form. several user controls though, but I checked to make sure all of their Dispose methods are being called.Fagot
P
0

Have you tried stepping into the .net code to see what line of code is being called when the exception is occuring? If you have VS 2008 you can do so by going to Tools --> Options --> Debugging and select the Enable .NET Framework Source Stepping. Be warned, this may take a while to download all of the necessary files, but this way you can step into the form.Show() and see exactly what is going on.

Piderit answered 8/4, 2009 at 18:10 Comment(1)
no, I wish. I'm using VS 2005. I checked reflector though, and it's just like the call stack says: right near the end of the Control.Show it calls FocusActiveControlInternal, which calls the Handle getter, which throws the exception if it's disposed.Fagot
F
0

Ok, it turns out it's a little simpler and more generic than I thought, but still weird and obscure.

If you're saving/loading the form Size/Location/WindowState when the form loads/closes like we do, you have to make sure that the OnLoad method calls base.OnLoad first so that the Form Load event handler fires, and THEN set the properties. Not doing so will only cause a problem if the form calls Close from inside the Load method. You'll get an ObjectDisposedException on the Show call after the form closing event is done.

My head hurts.

Fagot answered 8/4, 2009 at 20:8 Comment(0)
A
0

Form.Shown() Is the trick too.

Antihero answered 11/2, 2012 at 18:57 Comment(0)
D
0

As I understand it, setting the DialogResult of the form will close the form - may have to be other than DialogResult.None. (i.e. you don't need to then call the Form.Close() method).

The issue is also in part that if elsewhere in code, you are accessing a property of the form or control within it, that may prevent the form from closing.

It may also be best if as has been suggested, you have a property e.g.

private bool _loadedOk = false; 

in your form which you set in your initialisation code. In one of the later events after Form_Loaded, you then interrogate this and close the form if it's false.

Perhaps someone can suggest the best event to do this in??

Dross answered 18/11, 2015 at 23:44 Comment(0)
O
0

If you want to close the form without flicker, the best way I found was override SetVisibleCore Method:

public partial class MyForm : Form
{

...
    protected override void SetVisibleCore(bool value)
    {
        if (value && !IsHandleCreated && !ContinueLoadingForm())
        {
            base.SetVisibleCore(false);
            this.Close();
            return;
        }

        base.SetVisibleCore(value);
    }
}

Then you can simply do:

...
var myForm = new MyForm();
myForm.Show();
...

The Form only will appear if ContinueLoadingForm() be true, this works with ShowDialog() and Application.Run() as well.

Odell answered 13/11, 2019 at 18:47 Comment(0)
C
0

Expanding on RCMAN's answer in this thread (which got me 99% of the way to the finish line) ...

Here is the code I ended up using which also avoids the screen flicker:

Me.FormBorderStyle = FormBorderStyle.None
Me.Opacity = 0.01
Me.MinimumSize = New Size(1, 1)
Me.Size = Me.MinimumSize
Me.Location = New Point(1, 1)
BeginInvoke(New MethodInvoker(AddressOf Me.Close))

Additionally, to avoid the message "This program might not have run correctly" I applied a manifest change as described by mik here:

How to prevent "This program might not have installed correctly" messages on Vista

Carrnan answered 1/4, 2022 at 23:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.