views:

831

answers:

4

I have a form that can open a sub form (with ShowDialog). I want to make sure that the sub form is being disposed properly when the main form is done. I tried adding the subform to the components member of the main form, but at the moment I got a ArgumentNullException.
I know I can just instantiate the components myself, but isn't that a big dangerous? One day I'll add a component in the designer view, and that will generate the new Container() line in the designer.cs file, and I'll never know I have two component instanses running around the heap.
Is there an easier way to make sure the sub form is being disposed?

EDIT - moved my solution to an answer

+2  A: 

If you're using the form in showdialog, one could assume that after you've received the result you could dispose the form there?

using(MyDialog dlg = new MyDialog())
{
   result = dlg.ShowDialog();
}
Spence
The sub form can be opened more than once, with different data. I don't want to create a new instance each time I open it.
Noam Gal
But why are you worried about disposing it then? What resources are you using on it that wouldn't be disposed after a show dialog call?
Spence
After ShowDialog the form is not automatically disposed. This way I can access the DialogResult (Which is the reason for not automatically disposing it), and just ShowDialog it again and again (which is why I am using it).I want to make sure the sub form is being disposed when the main form is disposing.
Noam Gal
A: 

As per MSDN, Modal forms invoked via ShowDialog() are not disposed automatically and the onus is on the developer to dispose them:

When a form is displayed as a modal dialog box, clicking the Close button (the button with an X at the upper-right corner of the form) causes the form to be hidden and the DialogResult property to be set to DialogResult.Cancel. Unlike modeless forms, the Close method is not called by the .NET Framework when the user clicks the close form button of a dialog box or sets the value of the DialogResult property. Instead the form is hidden and can be shown again without creating a new instance of the dialog box. Because a form displayed as a dialog box is not closed, you must call the Dispose method of the form when the form is no longer needed by your application.

The relevant point here is "when the form is no longer needed". In your case, you seem to need the form for subsequent actions, therefore wrapping the ShowDialog() call in a using construct would not serve the purpose.

My suggestion would be to dispose of the Dialog in the Dispose method of your Main form, or as early as possible:

protected override void Dispose(bool disposing)
{
  if (disposing && (components != null))
  {
    components.Dispose();
  }
  // dlg is a variable of type Form2(the dialog)
  if (dlg != null)
  {
    dlg.Dispose();
    dlg = null;
  }
  base.Dispose(disposing);
}
Cerebrus
I think the point is how to get along with the designer here. Normally dispose is in .Designer.cs an not declared as partial.
Henk Holterman
Yes, I'm aware of that. And class level variables are visible in Designer.cs.
Cerebrus
A: 

You usually cannot override the Dipose methods of a form, because it's alread defined in the Form.Designer.cs file. There's a little trick how to add your own disposing logic to a form.

Using the following class:

public class Disposer : Component
    {
        private readonly Action<bool> disposeAction;               

        public Disposer(Action<bool> disposeAction)
        {
            this.disposeAction = disposeAction;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            this.disposeAction(disposing);
        }

        public static Disposer Register(ref IContainer container, Action<bool> disposeAction)
        {
            Disposer disposer = new Disposer(disposeAction);
            if (container == null)
                container = new System.ComponentModel.Container();

            container.Add(disposer);
            return disposer;
        }
    }

Keep a list of subforms and add the following line to constructor of the mainform:

Disposer.Register(ref this.components, this.MyDisposeAction);

When your mainform is disposed, all your subforms will also be disposed, e.g.:

private void MyDisposeAction(bool disposing)
{
  if (disposing)
  {
    foreach (var subForm in this.subForms)
    {
      subForm.Dispose(disposing);
    }
  } 
}
Rauhotz
Thanks for the idea, but I don't see why I should create a wrapper class that holds all my sub forms, and then disposed them one after the next, if in the process I register it in the components (and create it along the way, if needed) - I can just register my sub forms on the components straight away. That is what its there for.About re-writing the dispose method - I've read somewhere else on SO, that if I copy it out from the designer code into my own file, it won't be regenerated.
Noam Gal
A: 

My current workaround:
I've added the Components property to the form, and am using it to access the collection

private IContainer Components{    get { return components ?? (components = new Container());}}
Noam Gal