I have a window that receives asynchronous events from a worker thread. Sometimes these events come in after the window is closed and when I call Invoke() to process the event, I get an exception.
How can I test to make sure the window is still good. Or to cause all events to be processed somewhere in the closing lifecycle?
thanks - dave
You can check IsHandleCreated before calling invoke, to make sure the form is created and is not destroyed:
if (this.IsHandleCreated)
{
//this.Invoke ...
}
The property returns false if the form handle is still not created or it has destroyed after closing the form. It also prevents the error of calling invoke before showing the form and before the handle is created.
If for any reason you just care about closing the form, you can check IsDisposed property.
Related
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?
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.
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.
Simple question. I have a MainForm and a settingsForm. The settings form is initialized once and then shown every time the user clicks a button. I need it to do something when this button is clicked.
m_inputSettings.ShowDialog(this); //edit settings selected, so show that form
This is the MainForm calling the settings form, which it does fine. But I need the SettingsForm to do something every time this happens. Presently, I cant figure out if this call actually triggers any events I can set handlers for. Does it trigger an event? If not, is there another way I can tell my SettingsForm to do something every time this call happens?
Note: Any code in the mainform after that line doesn't get executed until the SettingsForm returns, but that is intentional.
Thanks.
Edit: One of the things I want my form to do it select a specific control when this happens, but it seems that that is impossible until after the form is done loading everything.
You can override the OnVisibleChanged method in your settings form. Make sure to call base.OnVisibleChanged though as to not screw up any potential observers of the event (and anything else the base class may do inside of that method.)
FormShown event - raised only once when form is displayed first time.
OnPaint / OnActivate - every time form is activated, but these events raised even when you switch with other application, which probably you don't want to do.
If you are changing form visbility, then you can use OnVisibleChanged
If you are minimizing the form, you can use OnSizeChanged / OnLocationChanged event.
If none suits you, make a public property and set false when form is closed / hidded, and set true before showing it. OnActivate, use this property to do your task.
Maybe use VisibleChanged event.
Override OnShown() event in your form this will raise only once after the form is opened
The disadvantage of OnVisibleChanged is it will also get raised when the form is closed.
On Paint , On Activate and OnEnter will raise before form is shown to the user.
Let's say I have a form SomeForm that inherits from Form.
public class SomeForm : Form
{
private void SomeForm_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("FormClosed event in SomeForm class");
}
}
public class Consumer
{
SomeForm someForm = new SomeForm();
someForm = null; //Ideally the messagebox would display here
SomeForm someForm = new SomeForm();
someForm.Close(); //Messagebox would display in this case as well
}
I want to show that MessageBox whenever the form is closed. Should I stick that in the FormClosed event? Is it safe to assume that the FormClosed event will fire every time something like the destructor is run? Is there a better place to put this code that must occur when the form is closed?
EDIT
Someone made a good point in the comments below. It seems the event does not fire when the instance of the class is set to null. However, will the destructor or some other method still be called when the instance is set to null. I want to guarantee that my code runs when the user is finished with the class.
I'm also aware that forcibly shutting down the system, ending the process, acts of God, etc will not cause my code to run no matter what. =)
Yes, it will be called unless something exceptionably happens, like killing it via system commands or pulling the plug. Other cases include
setting the variable to null (not calling the Close method)
Exception in another delegate: All events are MulticastDelegates, and the delegates are executed in order of appearence in that MulticastDelegate, if one of the previous delegates before yours causes an exception, your delegate will not be called.
It will get called unless your executable is forcibly closed (via task manager or taskkill from the command line) or the program crashes.
EDIT: I did some experimentation and found the following:
If you set your reference to the form to null, the form remains visible, and when the form is closed, the event gets raised.
If your form is a child form and the main form gets closed, the child form closes WITHOUT the event being raised.
If you call the form's Hide() method, the event does NOT get raised. Subsequently closing the main form, as mentioned above, will not cause the event to get raised.
Overriding OnClosed() will not help because it's still not called if the main form is closed.
Calling the form's Dispose() method (which is what the GC would eventually do if nothings pointing at it) will also not cause the event to get raised.
It looks like there really isn't a way to GUARANTEE the code will get called from within the form. You can put the code after the Application.Run(); in your Main() and it will get called there. There's also an event for Application.ApplicationExit that would get called unless you have exceptional circumstances (forcibly closed or crash).
You can register a handler for the Application.ApplicationExit event in your form, but be aware that by this time, your object has already been disposed, so you can't do anything with it.
FormClosed event is raised by OnClosed method. So if you want certain level of guarantee, you can override OnClosed method.
In C#,
Suppose I am in a form, and I pressed a button I made to close it.
this.Close();
some_action(); //can I do this??
can I perform another action after I closed the form or does the thread die and everything after that is lost?
Depends on what you are trying to do and the context of the statement. If the form being closed is the main form which owns the message loop, you can't do any UI related stuff (e.g. you can't display another MessageBox). If you are not doing it from another window (which doesn't own the message loop), you could do anything (even UI related) as long as you aren't manipulating the closed form object (you'll get ObjectDisposedException just like any disposed object).
By the way, the thread doesn't die as a result of Close. Closing the main window causes the message loop to terminate and not the thread itself. For example, the following program
static void Main() {
Application.Run(new Form1());
Application.Run(new Form2());
}
will display Form2 after Form1 is closed (using a newly created message loop). This proves that the thread is not dead.
If you try to manipulate the form or any of it's controls after calling Close you're going to run into trouble. However there's nothing preventing you from calling some other method - for instance a logging method - after calling Close.
Handle FormClosing Event and do additional action in event handler.
I want to execute some code inside 'Form1' but after the dialog is closed and it's not visible. I know that I can do this outside the dialog after calling ShowDialog() but I don't like it.
I tried in 'FormClosed' but it seems that the dialog it's still visible when this event is fired. Also if I call 'Hide()' the main dialog it's minimized - something interesting happens
I would create a static method on the dialog, and in this method show the dialog, and execute whatever needs executing after it closes, e.g.
public static void ShowAndDoStuff()
{
MyDialog dialog = new MyDialog();
dialog.ShowDialog();
dialog.DoStuff();
}
You can try Closing Event, also the hide method is a good idea.
I suggest a mix solution
just use a boolean variable (ie: sysclose ) with false default value,
in the closing event if sysclose is false then cancel the close and hide the form then do your job set the sysclose to true and call this.close