IM.
IM.

Reputation: 673

C# Winforms Threading - Delay Child form event until form is closed

I have an interesting problem.. I have a Form that launches another Form (2) through a Button. Before Form2 Closes, it sometimes fires an Event which Invalidates Form 1 and forces Form 1 to refresh it's data. The problem I have is After Form 2 fires the event, Form 1 seems to get it and handles it, and refreshes it's data and Only then Form 2 Closes. I want Form 2 to Fire the event and Close, BEFORE Form1's event handler catches and processes the event. I have a feeling it is related to BackgroundWorker (sort of like SwingUtilities.InvokeLater in Java) .. but I am not that experienced with it ..

public class Frm1{

void LaunchForm2(){
   Frm2 form2 = new Frm2();
   form2.dataChanged += new DataChangeListener(myListener);
   form2.showDialog();
}
private void myListener(){
  //get my data again
}

}

public class Frm2{

 private void Close(){
    if(myDataHasChanged){
      if(dataChanged != null) { 
        dataChanged(); 
      }
      this.Close();
    }
 }
}

Upvotes: 2

Views: 2461

Answers (5)

csharptest.net
csharptest.net

Reputation: 64218

You can do this in OnHandleDestroyed as it it done after final destruction of the window handle. You can be certain that:

  1. No more processing will be done by Frm2
  2. The call will always be fired when the form closes

Thus do something like the following:

public class Frm2 : Form
{
    protected override void OnHandleDestroyed(EventArgs e)
    {
        base.OnHandleDestroyed(e);
        if (myDataHasChanged)
        {
            if (dataChanged != null)
                dataChanged();
        }
    }
    private void Close()
    {
        if (myDataHasChanged)
            this.Close();
    }
}

UPDATE:

Test to verify that HandleDisposed is called prior to returning from ShowDialog():

        bool called = false;

        Form test = new Form();
        test.Shown += delegate (Object o, EventArgs e) { test.Close(); };
        test.HandleDestroyed += delegate(Object o, EventArgs e) { called = true; };
        test.ShowDialog();

        Assert.IsTrue(called);

Upvotes: 2

SLaks
SLaks

Reputation: 887489

You can raise the event after calling Close - calling Form.Close will not exit the method that called it.

EDIT: Try using BeginInvoke to wait for the next message loop before handling the event, like this:

form2.dataChanged += delegate { BeginInvoke(new DataChangeListener(myListener)); };

2nd EDIT: To give Form1 a chance to repaint, call BeginInvoke twice to wait two message loops before updating (One to close Form2, and a second to repaint Form1), like this:

form2.dataChanged += delegate(parameters) { 
    BeginInvoke(new Action(delegate { 
        BeginInvoke(new DataChangeListener(myListener), parameters);
    }));
    //Or,
    BeginInvoke(new Func<Delegate, object[], IAsyncResult>(BeginInvoke),
                new object[] { parameters }
    );
};

Upvotes: 0

Jon Seigel
Jon Seigel

Reputation: 12401

Is there a specific reason why you're using events in this situation?

Expose a property on Form2 that allows you to check whether the data has changed. After the ShowDialog() call returns, check the value of the property and do your update if necessary.

(Edited to remove my now-useless code sample.)

Upvotes: 2

Ed Chapel
Ed Chapel

Reputation: 6932

Try changing the event call to:

public class Frm2{

 private void Close(){
    if(myDataHasChanged){
      if(dataChanged != null) { 
        dataChanged.BeginInvoke(); 
      }
      this.Close();
    }
 }
}

This won't guarantee that the form closes before myListener() completes. However, it should allow the form to close without waiting for myListener() to complete.

Upvotes: -1

Beatles1692
Beatles1692

Reputation: 5320

Why don't you raise DataChanged Event in Closed event of form2?

Upvotes: 0

Related Questions