0

I have tried what I saw in this post, which did not answer my question. See my code and further explanation below.

// In MyForm : Form
private void SetupForImportantTask()
{
    // Does a ton of stuff to set up for ImportantTask, then...
    this.Close();
}

private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
    // otherForm is an instance of OtherForm : Form
    otherForm.ImportantTask(variousData);
}

// In OtherForm class...
public void ImportantTask(object variousData) {
    // Does a bunch of stuff that takes a long time
    // Provides UI feedback to notify the user a process in under way
}

Explanation: As you can see in the code, the idea is to do a bunch of setup for ImportantTask in MyForm, then call back into OtherForm with the setup information established after MyForm has already closed.

What I am seeing instead:

Even though I am not calling ImportantTask until the Closed event on MyForm has already fired, meaning semantically that the Form is already closed, MyForm remains open while the UI thread hangs, until ImportantTask is complete. The UI notification of the long-running process is never displayed, and MyForm only closes after ImportantTask is complete.

If MyForm is not closed when the Closed event fires, how else can I determine that the Form is actually closed and cleared from the screen?

Or:

What silly thing have I missed?

Steverino
  • 2,099
  • 6
  • 26
  • 50
  • 1
    Is `ImportantTask` running synchronously or asynchronously? Also, is it running on the main (UI) thread or a worker thread? If it's running synchronously and on the main thread, that behavior is pretty much expected. – 41686d6564 stands w. Palestine Oct 24 '19 at 22:38
  • 1
    You need to move all long running tasks into some form of Multitasking. Otherwise the UI thread is taxed out and nothing can be drawn - including the last change (closing the form) – Christopher Oct 24 '19 at 22:39
  • 1
    Consider hiding the form **then** closing it. – mjwills Oct 24 '19 at 22:46
  • You can run a Task in `ImportantTask` (the method already has the right name :). You can also change it in `otherForm.ImportantTask(variousData); this.Close();` (unless `this` is the starting Form, but it doesn't appear to be the case). – Jimi Oct 24 '19 at 22:49
  • Many helpful notes here, thank you all. With respect to the question as written, I take it that the answer is that it is an idiosyncrasy of the WinForms event and threading system that the `Closed` event does not wait until a Form is visibly closed (and cleared from the screen) before firing, and that there is no way to detect that a Form has been cleared from the screen; and therefore, the alternative is to use threading (or other) constructs in absence of such detection capability. – Steverino Oct 24 '19 at 22:52
  • It's not *idiosyncrasy*. If `ImportantTask` is synchronous, you're blocking the UI thread. It will resume there when `ImportantTask` returns. If `ImportantTask` runs a Task (e.g. `Task.Run(async ()=> { await Task.Delay(4000); Console.WriteLine("Done"); });)` then it won't block and `MyForm` closes smoothly. – Jimi Oct 24 '19 at 22:54
  • For what its worth, if you change `otherForm.ImportantTask(variousData);` to `otherForm.BeginInvoke(new Action(() => otherForm.ImportantTask()));` `MyForm` will finish closing before `ImportantTask` is run. – TnTinMn Oct 25 '19 at 02:33

2 Answers2

2

To answer your question, a form will disappear from screen after Form_Closed event has fired, therefore after calling each method that has registered to that event. The Dispose() method is called right after firing the event (from ReferenceSource, check the end of this method).

If you want to make sure a form has disappeared, you have the IsHandleCreated property, which tells you if there is a handle associated with the form (from MSDN).

About application design, a Form should be doing only what is has been designed for : display graphical elements and handling user input.

  • User Input should be filtered and your form should be firing events that a controller class would catch and manage on another thread.
  • When controller is done with user input, it should tell the form that it is OK to refresh the display.
Martin Verjans
  • 4,675
  • 1
  • 21
  • 48
1

Your design as it is now is broken, if you call from a Form a method of another Form, until that method returns you are still inside the calling method of the first Form.

In other words, method A does not finish until method B ( called by method A ) returns the control flow to its caller.

There are various ways you can properly do this, you can simply hide your first form changing its visibility instead of closing it, or you can run the importanttask method on other form async, or you can even simply post a message to second form and terminate yoiur first form then second form will respond to that message.

of course, pay attention at objects life cycle as well and in the end the method you decide to use depends on the rest of the application and on your taste.

Davide Piras
  • 43,984
  • 10
  • 98
  • 147
  • An asynchronous approach is likely the best. The goal of this question, though, strictly speaking, is to understand a better way to detect that a Form has actually closed completely, including its UI representation, unless the answer is that this is completely impossible. – Steverino Oct 24 '19 at 22:49
  • 2
    @Steverino With respect, I disagree with you. That's not your goal (at least it shouldn't be). You have a bigger problem than that. Having a long-running task that blocks the UI thread is wrong, period. That _is_ the main problem and one side effect of that problem is that the form doesn't close. – 41686d6564 stands w. Palestine Oct 24 '19 at 23:23