views:

72

answers:

2

I'm having trouble handling the scenario whereby an event is being raised to a closed form and was hoping to get some help.

Scenario (see below code for reference):

  1. Form1 opens Form2
  2. Form1 subscribes to an event on Form2 (let's call the event FormAction)
  3. Form1 is closed and Form2 remains open
  4. Form2 raises the FormAction event

In Form1.form2_FormAction, why does this return a reference to Form1 but button1.Parent returns null? Shouldn't they both return the same reference?

If we were to omit step 3, both this and button1.Parent return the same reference.

Here's the code I'm using...

Form1:

public partial class Form1 : Form
{
    public Form1 ()
    {
        InitializeComponent();
    }

    private void button1_Click ( object sender , EventArgs e )
    {
        // Create instance of Form2 and subscribe to the FormAction event
        var form2 = new Form2();
        form2.FormAction += form2_FormAction;
        form2.Show();
    }

    private void form2_FormAction ( object o )
    {
        // Always returns reference to Form1
        var form = this;

        // If Form1 is open, button1.Parent is equal to form/this
        // If Form1 is closed, button1.Parent is null
        var parent = button1.Parent;
    }
}

Form2:

public partial class Form2 : Form
{
    public Form2 ()
    {
        InitializeComponent();
    }

    public delegate void FormActionHandler ( object o );
    public event FormActionHandler FormAction = delegate { };

    private void button1_Click ( object sender , EventArgs e )
    {
        FormAction( "Button clicked." );
    }
}

Ideally, I would like to avoid raising events to closed/disposed forms (which I'm not sure is possible) or find a clean way of handling this in the caller (in this case, Form1).

Any help is appreciated.

+3  A: 

When you close form1 you should unsubscribe from the event:

form2.FormAction -= form2_FormAction;
ChrisF
The scope of form2 doesn't allow for this.
Steve Dignan
@Steve - then you'll have to extend the scope of `form2` so that it does. Unless you unsubscribe from the event your handler will be called.
ChrisF
Why can't you change the scope? Do you want to be able to open multiple Form2s?
tanascius
Changing the scope is an option and I know how to handle that case. I'm just asking if the scenario I described (scope considered) can be handled.
Steve Dignan
@Steve Dignan - Depending on what parameters are passed to the event handler, you could check to see if the form is closed and unsubscribe to the event at the beginning of the handler. But that's ugly. Keeping up with what events the form is subscribed to, and removing the subscriptions when the form is closed is the *correct* approach.
Jeffrey L Whitledge
+2  A: 

A simple solution would be to show your instance of Form2 using the override that takes an owner:

form2.Show(this);

This will ensure that form2 is closed when form1 is closed. It's generally good practice anyway in a multi-form application, so that you don't have any ownerless forms floating around.

Update: the event model is just one way of handling communication between forms. In your case it's not really suitable because the form receiving the event can close without the form raising the event being aware of it.

An alternative would be to write Form2 with a constructor that includes an instance of Form1 as a parameter (which would be saved as a form-level member like _Receiver or something). Instead of raising an event, Form2 would call a method defined in Form1 after checking to see if _Receiver has been disposed already:

if (!_Receiver.IsDisposed)
{
    _Receiver.SomeMethod("some method");
}

If your instance of Form1 is still around, its method will be called; if it's been closed and disposed, its method won't be called. Since Form2 is still around, you can also make _Receiver into a public property, and set its "owner" to some other Form1 at any time.

MusiGenesis
The design of the application allows for child forms to remain open after the parent has closed. Unfortunately, I'm not in a position to change the design.
Steve Dignan