ObjectDisposedException when .Show()'ing a for

2019-05-10 05:51发布

问题:

ive checked out some of the other questions and obviously the best solution is to prevent the behavior that causes this issue in the first place, but the problem is very intermittent, and very un-reproduceable.

I basically have a main form, with sub forms. The sub forms are shown from menus and/or buttons from the main form like so:


private void myToolStripMenuItem_Click(object sender, EventArgs e)
{
    try
    {
        xDataForm.Show();
        xDataForm.Activate();
    }
    catch (ObjectDisposedException)
    {
        MessageBox.Show("ERROR 10103");
        ErrorLogging newLogger = new ErrorLogging("10103");
        Thread errorThread = new Thread(ErrorLogging.writeErrorToLog);
        errorThread.Start();
    }
}

and the sub forms are actually in the main form(for better or worse. i would actually like to change this but would be a considerable amount of time to do so):


public partial class FormMainScreen : Form
{
    Form xDataForm = new xData();
    ...(lots more here)

    public FormMainScreen(int pCount, string pName)
 {
        InitializeComponent();
        ...
 }
    ...
}

The Dispose function for the sub form is modified so that, the 'close' and 'X' buttons actually hide the form so we dont have to re-create it every time. When the main screen closes, it sets a "flag" to 2, so the other forms know that it is actually ok to close;


protected override void Dispose(bool disposing)
{
    if (FormMainScreen.isExiting == 2) 
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
    else
    {
        if (xData.ActiveForm != null)
        {
            xData.ActiveForm.Hide();
        }
    }
}

So, the question is, why would this work over and over and over again flawlessly, but, literally, about every 1/1000 of the time, cause an exception, or rather, why is my form being disposed?

I had a suspicion that the garbage collector was getting confused, because it occurs slightly more frequently after it has been running for many hours.

回答1:

No offense, but this seems to be a very convoluted solution to a problem that was solved a very long time ago.

You shouldn't be doing anything in the Dispose() method other than disposing other disposables (and even then only if the disposing flag is true.) So I would not modify the method that the designer generates for you.

So the immediate answer to your question as to why this is happening is almost certainly related to the timing of the garbage collector calling your Dispose method.

Instead you should probably consider using a MDI (multiple document interface) parent form and your "sub forms" are called MDI children. You could then handle the FormClosing event in the children like so.

(Note that if you are opposed to MDI, you can do basically the same thing by using form Owners.)

// MDI child
private void Form_FormClosing(object sender, FormClosingEventArgs e) {
    if (e.CloseReason == CloseReason.UserClosing) {
        e.Cancel = true;
        Hide();
    }
}

When the form is closing because of various reasons such as closing in code, the parent form is closing, Windows is shutting down, etc. then the closing will not be cancelled. Only when the window is being closed because the user closed the child form directly will you hide it.

To show a MDI child inside of a MDI parent, you can do the following:

MyParentForm parentForm = new MyParentForm();
parentForm.IsMdiContainer = true;
parentForm.Show();

MyChildForm childForm = new MyChildForm();
childForm.MdiParent = parentForm;
childForm.Show();


回答2:

try 
{
    // Validate form not disposed before using. Initialize as needed. 
    if  (xDataForm == null || xDataForm.IsDisposed)
    {
        xDataForm = new MyDataFormName();
    }
    xDataForm.Show(); 
    xDataForm.Activate(); 
}