Stackoverflow Exception closing Form - c#

I have a problem in WinForms. I created a MDIParent-Form and i call a ChildForm from the Load of the MDIParent. And I want that if the ChildForm closes, the MDIParent must close and the Application exits. Thats why i wrote an event for the childForm in the MDIParent, so that if the ChildForm closes the FormClosed-Event would be fired in the MDIParent, but it throws a stack overflow exception. I know that there is a infinite loop, but I dont know why...
private void MDIParent1_Load(object sender, EventArgs e)
{
Form1 childForm = new Form1();
childForm.MdiParent = this;
childForm.FormClosed += childForm_FormClosed;
childForm.Show();
}
void childForm_FormClosed(object sender, FormClosedEventArgs e)
{
this.Close();
//{Cannot evaluate expression because the current thread is in a stack overflow state.}
}
but if i use
Application.Exit();
instead of this.Close()... everything works fine ... i want to know why...can someone explain??
Update:
I have tried the same without a MDIParent and everything works...but why is there a problem if I use a MDIParent

This is a bit of a bug, the problem is that the child still is present in the MDIParent1.MdiChildren collection when the FormClosed event fires. In other words, the FormClosed event fires a little too soon. So when you close the parent, it will try to close the child again. Which triggers the child's FormClosed event again. Which closes the parent again. Etcetera. Event firing order is never not a problem. Well, let's call it a bug :)
The workaround is to use the Disposed event instead, it fires later:
private void MDIParent1_Load(object sender, EventArgs e)
{
Form1 childForm = new Form1();
childForm.MdiParent = this;
childForm.Disposed += childForm_Disposed;
childForm.Show();
}
void childForm_Disposed(object sender, EventArgs e)
{
this.Close(); // Fine now
}

Related

Use Form.Show() but Not running code After Form.Show() Like ShowDialog()

I can't use Form.ShowDialog() I want use Form.Show() but I want execute code after From.Show() after Form.Closed
public void Tools(){
var frm=new ToolFrm();
frm.Show();
//do something
}
I want return true and hold execution while the form frm is open.
If you want to run some code when a modeless form is closed, then you can subscribe to to the form's FormClosed event:
public bool Tools()
{
var frm = new ToolFrm();
frm.FormClosed += Form_FormClosed;
frm.Show();
return true;
}
private static void Form_FormClosed(object sender, FormClosedEventArgs e)
{
((Form)sender).FormClosed -= Form_FormClosed;
MessageBox.Show("Form was closed");
}
Note that if you don't unsubscribe from the FormClosed event, then the subscribing object will keep alive a reference to the form until the subscribing object is garbage collected.
To prevent that behaviour, you can unsubscribe from the event in the handler, as I have done with the line:
((Form)sender).FormClosed -= Form_FormClosed;
If I understand your question correctly, you want to run code when the form is re-opened. The showdialog is actually a option you want and need to use.
private bool Tools()
{
this.Hide();
using (Form frm = new ToolFrm())
frm.ShowDialog();
DoSomethingYouWant(); //For example return true/false;
this.Show();
}
In the method DoSomethingYouWant() you can do anything you want to do.
The form will be hidden while the user untill the ToolFrm is closed.
If you want to run of example a new FormLoad() event, you can subscribe to it.
By replaceing the DoSomethingYouWant() with FormName_Load(this, e);
In the loading event you could do :
private void Form1_Load(object sender, EventArgs e)
{
if(Tools){
Messagebox.Show("true")
}
if(!Tools{
Messagebox.Show("false")
}
}

Closing multiple windows form with single click

I want to close two forms at the same time. I have one main form that starts with program. when user click button, the main form will hide and other form will pop up. on the second form if user click "back to main " button, it should hide second form and show main form. But the problem is if user tries to close the second form it should close the main form as well. How can i close the main form as well
I would just use the Application.Exit() for what is requested by this thread.
Application.Exit();
UPDATE: corrected
I had said this will not call the form closing events but in documentation it does actually call it here is a link to the documentation
http://msdn.microsoft.com/en-us/library/ms157894(v=vs.110).aspx
It was better if you specified what codes you wrote for going back to main form, so I could help you by changing your codes. But now because I don't know how you did it, I have to write codes for both of those tasks.
It can be possible using a Boolean variable to do what you want. Follow bellow codes:
public partial class MainForm : Form
{
//"Click" event of the button that should opens the second form:
private void goToSecondForm_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2(); //Or you can write it out of this method.
this.Hide(); //Hides the main form.
f2.ShowDialog(); // Shows the second form.
this.Show(); // Shows the main form again, after closing the second form using your own button.
}
}
public partial class Form2 : Form
{
bool selfClose = false; //False shows that user closed the second form by default button and true shows that user closed it by your own button.
//"Click" event of the button that should closes just the second form and returns user to the main form:
private void ownCloseButton_Click(object sender, EventArgs e)
{
selfClose = true; //Means user clicked on your own button.
this.Close(); //So the program closes the second form and runs f2_FormClosed method, but because selfClose became true here, happened nothing there and program will go back to goToSecondForm_Click method in the main form and will run this.Show() .
}
//"FormClosed" event of the second form :
//Whether user clicked on your own button or on the default one, this method will run.
private void f2_FormClosed(object sender, FormClosedEventArgs e)
{
if (!selfClose) //It means user didn't click on your own button and both of forms must be closed .
Application.Exit(); //So the program closes all of forms (actually closes the program) and couldn't access to any other commands (including this.Show() in goToSecondForm_Click method).
}
}
As others have said, you need to somehow call .Close() on the main form when your child form is closed. However, as you've pointed out, you don't have a reference to the main form automatically in your child form! That leaves you with a few options.
1. Exit the application immediately.
This is done by calling Application.Exit(); in your child form's "back to main" button's click event handler. It will immediately close all forms, which might simply be what you want.
// .. ChildForm code ..
void OnBackToMainClicked(object sender, EventArgs e)
{
Application.Exit();
}
2. Pass a reference to the main form to the child form.
This is probably the most common way to solve this problem in general. When you create your child form in your main form, you'll need to pass a reference as follows:
// .. MainForm code ..
void OnGoToChildForm(object sender, EventArgs e)
{
var childForm = new ChildForm(this);
childForm.Show();
}
// .. ChildForm code ..
private MainForm mainForm; // This is where the child form will keep a reference to
// the main form that you can use later
public ChildForm(MainForm mainForm)
{
// This is the child form's constructor that we called above,
// and it's where we'll save the reference to the main form
this.mainForm = mainForm;
}
// This also needs to be the event handler for the close event
void OnBackToMainClicked(object sender, EventArgs e)
{
this.Close();
mainForm.Close();
}
3. Add an event handler on the child form's FormClosed event. This is a safe way to solve the problem if you are concerned about keeping your main application logic under the control of the main form. It's similar to the solution suggested by Lamloumi above, but it's all done in the main form's code.
// .. MainForm code ..
void OnGoToChildForm(object sender, EventArgs e)
{
var childForm = new ChildForm(this);
childForm.FormClosed += new FormClosedEventHandler(SecondForm_FormClosed);
childForm.Show();
}
void SecondForm_FormClosed(object sender, EventArgs e)
{
// Perform any final cleanup logic here.
this.Close();
}
Form1 _FirstForm = New Form1();
Form2 _SecondForm = New Form2();
MainForm _MainForm = new MainForm();
_FirstForm.Close();
_SecondForm.Close();
_MainForm.Show();
Normally , in the Home form you have some ting like this :
SecondForm second= new SecondForm ();
second.Show();
this.Hide();
In the SecondForm you must ovveride the event of closure like this :
public class SecondForm :Form{
public SecondForm()
{
InitializeComponent();
this.FormClosed += new FormClosedEventHandler(SecondForm_FormClosed);
}
void SecondForm_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
}
To be sure that your application is closed after you close the form. because the Home from is still active and hidden if you don't.
use this in Form2
private void button1_Click(object sender, EventArgs e)
{
Form1.FromHandle(this.Handle);
}
There Handle are the same now ;
hope this work.

Avoiding `ObjectDisposedException` while calling `Invoke`

I have 2 forms, one is MainForm and second is DebugForm. The MainForm has a button that sets up and shows the DebugForm like this, And passes a reference to an already opened SerialPort:
private DebugForm DebugForm; //Field
private void menuToolsDebugger_Click(object sender, EventArgs e)
{
if (DebugForm != null)
{
DebugForm.BringToFront();
return;
}
DebugForm = new DebugForm(Connection);
DebugForm.Closed += delegate
{
WindowState = FormWindowState.Normal;
DebugForm = null;
};
DebugForm.Show();
}
In the DebugForm, I append a method to handle the DataReceived event of the serialport connection (in DebugForm's constructor):
public DebugForm(SerialPort connection)
{
InitializeComponent();
Connection = connection;
Connection.DataReceived += Connection_DataReceived;
}
Then in the Connection_DataReceived method, I update a TextBox in the DebugForm, that is using Invoke to do the update:
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
But I have a problem. As soon as I close the DebugForm, it throws an ObjectDisposedException on the Invoke(new EventHandler(AddReceivedPacketToTextBox)); Line.
How can I fix this? Any tips/helps are welcome!
UPDATE
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
private void button1_Click(object sender, EventArgs e)
{
Connection.DataReceived -= Connection_DebugDataReceived;
this.Close();
}
Closing a form disposes of the Form object but cannot forcibly remove references that other classes have to it. When you register your form for events, you are basically giving a reference to your form object to the source of the events (the SerialPort instance in this case).
This means that, even though your form is closed, the event source (your SerialPort object) is still sending events to the form instance and the code to handle these events is still being run. The problem then is that when this code tries to update the disposed form (set its title, update its controls, call Invoke, &c.) you will get this exception.
So what you need to do is ensure that the event gets deregistered when your form closes. This is as simple as detecting that the form is closing and unregister the Connection_DataReceived event handler. Handily you can detect the form is closing by overriding the OnFormClosing method and unregistering the event in there:
protected override OnFormClosing(FormClosingEventArgs args)
{
Connection.DataReceived -= Connection_DataReceived;
}
I would also recommend moving the event registration to an override of the OnLoad method as otherwise it may receive events before the form has been fully constructed which could cause confusing exceptions.
You haven't shown the code for the AddReceivedPacketToTextBox method.
You could try checking for a disposed form in that method:
private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{
if (this.IsDisposed) return;
...
}
Detaching the DataReceived event handler when closing the form is probably a good idea, but isn't sufficient: there is still a race condition which means your AddReceivedPacketToTextBox can be called after the form is closed/disposed. The sequence would be something like:
Worker thread: DataReceived event fired, Connection_DataReceived starts executing
UI thread: Form closed and disposed, DataReceived event detached.
Worker thread: calls Invoke
UI thread: AddReceivedPacketToTextBox executed while form is disposed.
I found out if I remove the event in a button event click , and close the form in that button click, everything is fine and my debugform gets closed without any exception...how odd!
That's not odd. Multithreading bugs ("Heisenbugs") are timing-related and small changes like that can affect the timing. But it's not a robust solution.
The problem could be solved by adding a timer:
bool formClosing = false;
private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (formClosing) return;
_buffer = Connection.ReadExisting();
Invoke(new EventHandler(AddReceivedPacketToTextBox));
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (formClosing) return;
e.Cancel = true;
Timer tmr = new Timer();
tmr.Tick += Tmr_Tick;
tmr.Start();
formClosing = true;
}
void Tmr_Tick(object sender, EventArgs e)
{
((Timer)sender).Stop();
this.Close();
}
Thanks to JohnWein from MSDN

Close All application click X Winform

I'm trying to change the names of the datagrid and labels in Form2 from Form1 based on the selection.
private void button1_Click_1(object sender, EventArgs e)
{
this.Hide();
Form2 frm = new Form2();
frm.Show();
}
private void button2_Click(object sender, EventArgs e)
{
Form2 frm = new Form2();
frm.dataGridView1.Columns["FirstName"].HeaderText = "Prenom";
frm.dataGridView1.Columns["LastName"].HeaderText = "Nom";
this.Hide();
frm.Show();
}
The above approach is working fine for me but I have a problem. When I click on X in the second/Form 2 it's just closing the Form2 not the Form1. How can close all the application when i click on the X.
Is there any better way of doing this??? The reason why i'm not using I'm using Telerik and I don't find any option to add resource file in that. Please correct me if i'm wrong. Thank you.
You simply need to attach an event handler to the Closed event of the newly created form so that it closes the main form when it's closed:
private void button1_Click_1(object sender, EventArgs e)
{
this.Hide();
Form2 frm = new Form2();
frm.FormClosed += (_, args) => this.Close(); //Added this method
frm.Show();
}
Add that same method to the other click handler as well.
The easiest way to close an application is to call the static method: Application.Exit
Another method if you only want to close one single form would be to add a handler for the FormClosing event and close the main form there.

C# Why does form.Close() not close the form?

I have a button click event handler with the following pseudo code:
private void btnSave_Click(object sender, EventArgs e)
{
if(txt.Text.length == 0)
this.Close();
else
// Do something else
// Some other code...
}
This is just some simple code, but the point is, when the text length equals zero, I want to close the form. But instead of closing the form the code executes the part // Some other code. After the click event handler is completely executed, then the form is closed.
I know, when I place return right after this.Close() the form will close, but I'd like to know WHY the form isn't direclty closed when you call this.Close(). Why is the rest of the event handler executed?
The rest of the event handler is executed because you did not leave the method. It is as simple as that.
Calling this.Close() does not immediately "delete" the form (and the current event handler). The form will be collected later on by the garbage collector if there are no more references to the form.
this.Close() is nothing than a regular method call, and unless the method throws an exception you will stay in the context of your current method.
Close only hides the form; the form is still alive and won't receive another Load event if you show it again.
To actually delete it from memory, use Dispose().
Answer is simple as you are executing your current method so this.Close() will be enqueued until either you explicitly returned or your current excuting method throws an exception.
Another possible solution is that if you open a new Form and want to close the current one: if you use newForm.ShowDialog() instead of newForm.Show() it doesn't close the currentForm with currentForm.Close() until the newForm is also closed.
Unless the Form is a modal form(opened with .ShowDialog()), Form.Close() disposes the form, as well. So, you cannot reopen it under any circumstances after that, despite of what others may have said. There is Form.Visible for this behavior(hiding/showing the form).
The point here is that .Close() does not return from the section it is called for several reasons. For example, you may call SomeForm.Close() from another form or a class or whatever.
Close() is just a method like any other. You have to explicitly return from a method that calls Close() if this is what you want.
Calling MessageBox.Show(frmMain,"a message","a title") adds the form "TextDialog" to the application's Application.OpenForms() forms collection, along-side the frmMain Main form itself. It remains after you close the Messagebox.
When this happens and you call the OK button delegate to close the main form, calling frmMain.Close() will not work, the main form will not disappear and the program will not terminate as it usually will after you exit the OK delegate. Only Application.Exit() will close all of the garbage messagebox "TextDialog"s.
private void btnCloseForm_Click(object sender, EventArgs e)
{
FirstFrm.ActiveForm.Close();
}
and if you want close first form and open secound form do this :
private void btnCloseForm_Click(object sender, EventArgs e)
{
FirstFrm.ActiveForm.Close();
}
private void FirstFrm_FormClosed(object sender, FormClosedEventArgs e)
{
SecounfFrm frm = new SecounfFrm ();
frm.ShowDialog();
}
or you can do somting like that :
private void btnCloseForm_Click(object sender, EventArgs e)
{
this.Hide();
}
private void FirstFrm_VisibleChanged(object sender, EventArgs e)
{
if(this.Visible == false)
{
this.Close();
}
}
private void FirstFrm_FormClosed(object sender, FormClosedEventArgs e)
{
SecounfFrm frm = new SecounfFrm ();
frm.ShowDialog();
}

Categories