FormClosing with CloseReason = UserClosing doesn't work expectedly? - c#

I have a main form, in the class of this form, I declare another form. This form lives with the main form until the main form is unloaded. There is a button on the main form, clicking this button will show the member form (I mentioned above). I want to prevent the member form from closing when user closes that form and I added the following FormClosing event handler for that form:
private void MemberForm_FormClosing(object sender, FormClosingEventArgs e) {
if(e.CloseReason == CloseReason.UserClosing){
e.Cancel = true;
Hide();
}
}
That works OK for that form. However if user closes the main form, this form is not closed, and it's hidden somewhere making my application seem to run silently. I want this form also to be closed. This is very simple by adding some FormClosed event handler for my main form to close the member form manually. Closing it manually is OK, but why do I have to do that? It seems that when user closes the main form, the FormClosing event of the member form is fired with a parameter FormClosingEventArgs passed in and the CloseReason is the same as the CloseReason of the main form (which is UserClosing). I don't understand this, I thought the CloseReason of the form is UserClosing only when user clicks on the X button, I thought the CloseReason for my member form is something like "MainFormClosing".
Is there some way to close the member form automatically as by default?
UPDATE
Here is the method showing the member form (showing it as a dialog):
private void ShowMemberForm_Click(object sender, EventArgs e){
memberForm.ShowDialog();
}
But I don't think this matters, because when I load my main form, even I don't need to click on the ShowMemberForm button, and try closing my main form first, it doesn't still close the member form.
Thanks!
UPDATE
There is something strange here, I've tried commenting out the line e.Cancel = true, or even all the FormClosing event handler and the problem is still there. This is so strange, it works OK before, I've just added the member form and this form relates to some Thread handling, but the thread starts only when a button on the member form is clicked. I didn't click that button.

What I have done in the past is set a flag when programatically closing
so in the MemberForm
private bool _ForceClose = false;
public void ForceClose()
{
_ForceClose = true;
this.Close();
}
private void MemberForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(!_ForceClose)
{
e.Cancel = true;
Hide();
}
}
Then in your MainForm you can call
memberForm.ForceClose();
From within your MainForms FormClosing method or from your MainForms Dispose() or Deconstructor.
It's low tech, but it works. Im not sure if you should put _ForceClose = true in your MemberForm's Dispose method, i'm fairly certain when it gets there its already been closed BUT it couldn't really hurt.

Related

Why isn't the Validated event firing for controls on a form created in the main form when the child form is closed?

Say I have a winforms application with two forms, the main form that starts up when the program runs, and another form. Here is the code for the main form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var f2 = new Form2();
f2.ShowDialog();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Main Form: Validated!");
}
}
And this is the child form:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Child Form: Validated!");
}
}
When I run the application, I can put the focus in the textbox on the main form, and of course when I tab out, it fires the Validated event and prints Main Form: Validated! to the output. This also happens if I have the focus in the textbox and close the main form (i.e. end the program).
When I click the button on the main form which pops up an instance of the child form, I can put the focus in the textbox on the child form, and the Validated event fires as it should when I tab out of it. However, unlike the main form behavior on closing the form, if I have the focus in the textbox on the child form and I close the child form, the Validated event never fires.
Why doesn't the validated event fire, and is there a way I can make it fire.
I'm relying on the validated event of certain controls to update my view models. I want to make sure they always fire even when the loss of focus is due to a form closing or even the application itself ending.
This is caused by ShowDialog(). It is a documented bug, a mistake in .NET 1.x that they could not fix anymore. From the Form.cs source code:
// NOTE: We should also check !Validate(true) below too in the modal case,
// but we cannot, because we didn't to this in Everett (bug), and doing so
// now would introduce a breaking change. User can always validate in the
// FormClosing event if they really need to. :-(
So just follow the guidance:
protected override void OnFormClosing(FormClosingEventArgs e) {
if (e.CloseReason == CloseReason.UserClosing && this.DialogResult != DialogResult.Cancel) {
if (!base.Validate(true)) e.Cancel = true;
}
base.OnFormClosing(e);
}
With the assumptions that you don't need the event when the dialog is dismissed and you'd want the dialog to stay open when validation fails. The latter clause is new behavior.
Documentation says:
Unlike non-modal forms, the Close method is not called by the .NET
Framework when the user clicks the close form button of a dialog box
or sets the value of the DialogResult property. Instead the form is
hidden and can be shown again without creating a new instance of the
dialog box.
Based on the docs, when you call the ShowDialog method of the second form (Form2) and you close it by clicking on the close button (the button with an X at the upper-right corner of the form) the form is hidden (not closed). This is why the Validated event never fires.
Note: Closing and Closed events will fire anyway.
Note2: Closing the form using ALT + F4 is the same as clicking on the X button of the form.
Demonstration
Form as modal (your example)
The second form is opened by calling ShowDialog.
Add a button on the second form and set the click event as follow:
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
If you close the form by clicking in that new button the Validated event will fire. If you close the form by clicking the button with an X at the upper-right corner of the form, the Validated event won't fire (because the form has never closed, it has been hidden).
Form as non-modal
The second form is opened by calling Show.
In this case, when you click the button with an X at the upper-right corner of the form the latter will be really closed (not hidden) and the Validated event fires.
Workaround
Hiding the ControlBox
The simplest method is to hide the X button of the form setting the ControlBox property to false (by designer or by code). However, the user could close the form using ALT + F4.
Using Hans Passant solution
Hope to be clear.
If you want, all your modal dialog controls validated, while closing .. probably you can approach below way.... which definitely fires the text box validated event.
Add an event for form closing in Form2 and call ValidateChildren()
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
this.ValidateChildren();
}
MSN Documentation
According to MSN Documentation If the CausesValidation property is set to false, the Validating and Validated events are suppressed.
Verifies the value of the control losing focus by causing the Validating and Validated events to occur :According to MSN Documentation
You can also fired Validating and Validated events by using Validate() method that lose the focus of controls. So you can call Validate method within closing event of Form2. Bellow is the code
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void textBox1_Validated(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Child Form: Validated!");
}
private void Form2_Closing(object sender, FormClosingEventArgs e)
{
if (!Validate())
//Write your code that will execute if your form is not validated
}
}

Detect CloseEvent through different forms

PROBLEM SOLVED
SHORT STORY
I want to detect "FormClosing()" event through different forms, ie, when form1 is closed that is instantiated within form2, can form2 detect when user presses exit in form1?
LONG STORY
My team and I are working on a windows form application. Project has two forms: one is the main form page and the other is accessed via this main form. Main form looks like this:
And the second one looks like this:
If you press "Ekle/Sil" buttons within the main form, you are directed to form 2 where you can edit database entries. When you press "Sayfayı Yenile" button in the main form, the content of the text areas are refreshed by re-fetching entries from the database.
My problem is, I want to automatically refresh the main form when the user closes the second form. My research suggests I should use an "FormClosing()" event to detect a closing form. However, I want to detect this from the main form. Instantiating main form in second form's source code doesn't seem to be a reliable solution. Anyone can tell me how to do this?
EDIT
I solved the problem:
1) Created a public method within the main form that refreshes the page.
2) Send "this" property from the main form when creating the second form.
3) Added an "FormClosed()" handler within the second form that invokes this public method.
Still, I'm looking for a better solution.
EDIT 2
Better solution InBetween's answer
Simply use the Form.Closed event of the new child windows form. Everything is handled from the main form:
void EkleSil_Clicked(object sender, EventArgs e) //or whatever method is called when button is clicked
{
var newChildForm = new ChildForm();
newChildForm.Closed += childFormClosed;
newChildForm.Show();
}
void childFormClosed(object sender, EventArgs e)
{
((Form)sender).Closed -=childFormClosed;
updateShownData();
}
You can create a event in the second form and raise it when the form is closing .
Handle the event in the main form and refresh the main form when the event is raised
Another option would be to pass Form1 as an argument to Form2. Then use the Form.Closing event in Form2 and use the Form1 reference to trigger something.
Form1 form1Ref;
public Form2(Form1 mainform)
{
form1Ref = mainform;
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
form1Ref.SomeMethod();
}

Do I need to call Form.Close(), or is it handled for me?

Using C# and WinForms, I have a base menu that calls a variety of forms on button clicks:
private void Some_Button_Click(object sender, EventArgs e)
{
var someForm = new SomeForm();
someForm.MdiParent = MenuForm;
someForm.Show();
someForm.BringToFront();
}
My concern is memory useage. When a user click's "x" to close the form, is Form.Close() already called when the window closes? Or, do I need to create an event in order to explicitly call Form.Close()?
When a user click's "x" to close the form, is Form.Close() already called when the window closes?
No, the FormClosing event is fired. Form.Close tells the system to close the form (the FormClosing event is fired in that case as well).
Close is command. FormClosing is called in response to a command.

c# WinForms form still closes after setting e.Cancel = true in Form_FormClosing event

This is the code in question:
private void FormAccounting_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.FormAccountingLocation = this.Location;
Properties.Settings.Default.Save();
if (IsEditing)
{
MessageBox.Show("Please save or cancel open transactions before closing the accounting window.", "Open Transactions", MessageBoxButtons.OK, MessageBoxIcon.Information);
e.Cancel = true;
}
}
I've added breakpoints to the e.Cancel = true; line to ensure it's being executed.
The form closes immediately after clicking Ok.
Here's the code that calls FormAccounting:
private void buttonAccounts_Click(object sender, EventArgs e)
{
FormAccounting NewFormAccounting = new FormAccounting();
NewFormAccounting.Show();
}
Canceling the form close event works to prevent:
User closing the form
Application.Exit from exiting the application
Code from calling Form.Close on the form
But it does not work to prevent:
User closing application's main form
Code calling Form.Dispose on the form
Code calling Form.Close on the application's main window
The last 3 cases don't even trigger the form close event on the non-main form, so the form goes away without a chance to cancel it. Perhaps your application is causing the form to first close in one of the first 3 ways, which triggers the event, and then in one of the second 3 ways (or something similar), which does not trigger the event and forces the form closed anyway.
Edit:
Add this function to your form's code and it will allow you to review in the debugger what the call stack looks like when your window is getting closed so you can see what is actually causing it:
protected override void DestroyHandle()
{
System.Diagnostics.Debugger.Break();
base.DestroyHandle();
}

How to show form after it was closed?

I have a MDI application with couple of windows. Is there any way to disable disposing form after it was closed by the user. I want to be able to reopen that form just by calling form.Show() method.
You could use Form.FormClosing Event.
The FormClosing event occurs as the form is being closed. When a form is closed, it is disposed, releasing all resources associated with the form.
If you cancel this event, the form remains opened. To cancel the closure of a form, set the Cancel property of the FormClosingEventArgs passed to your event handler to true.
This should do the same thing as you'd do with Form.Show()
You can save the values of the Form into a separate class before you call the Close() method of the form and load a new form which takes it's values from the saved class.
send the main form as a parameter to the child form, then the child form can reference the main form t any time
public Form MainForm = null;
public Sample(ref Form mainForm)
{
InitializeComponent();
MainForm = mainForm;
}
private void Sample_FormClosed(object sender, FormClosedEventArgs e)
{
MainForm.Show();
}

Categories

Resources