Check for changes on a windows form before closing - c#

This should be an easy google search but I can't find the answer.
When I click the close button on my form I want to check and warn the user if information on the form has changed. For this my code is adequate.
But if the user clicks the X button on the top of the form no check is performed.
So I tried using the "FormClosing" event to execute the same code below but it goes into some weird loop, I think because i have used this.close which also triggers the same FormClosing event.
What is the correct way to check for form changes when either a button or the X is clicked.
private void buttonClose_Click(object sender, EventArgs e)
{
// Close the form and warn if record not saved
//Check for unsaved changes
if (formDataChanged == true)
{
DialogResult dr = new DialogResult();
dr = MessageBox.Show("Data on this form has changed, Click OK to discard changes", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
if (dr == DialogResult.OK)
{
this.Close();
}
}
else
{
this.Close();
}
}
Update
I think the linked example may be too simple?
When I looked at the CloseReason I received the same results when I closed the window either way so I'm not sure how I can use this to trigger different results.
CloseReason = UserClosing
Cancel = False
private void FormTelephoneLog_FormClosing(object sender, FormClosingEventArgs e)
{
System.Text.StringBuilder messageBoxCS = new System.Text.StringBuilder();
messageBoxCS.AppendFormat("{0} = {1}", "CloseReason", e.CloseReason);
messageBoxCS.AppendLine();
messageBoxCS.AppendFormat("{0} = {1}", "Cancel", e.Cancel);
messageBoxCS.AppendLine();
MessageBox.Show(messageBoxCS.ToString(), "FormClosing Event");
}
I tried adding a bool variable as was also suggested in the other post but it still loops. I know why because the this.close() command also triggers FormClosing event but I still can't figure out how I should do this correctly.
I see there are 6 possible scenarios.
No changes, Close Button -> Works
No changes, X Button -> Loops
Discard changes, Close Button -> Works
Discard changes, X Button -> Loops
Don't Discard changes, Close Button -> Works
Don't Discard changes, X button -> Fails ***
*** The form is still closed and the changes are discarded.
Here is the code as it is now. I'm going round and round including more and more conditions. This would seem like a very common thing to want to do. I'm missing something obvious.
private void buttonClose_Click(object sender, EventArgs e)
{
CloseButtonClicked = true;
checkFormChanges();
CloseButtonClicked = false;
}
private void FormTelephoneLog_FormClosing(object sender, FormClosingEventArgs e)
{
if (CloseButtonClicked == false)
{
checkFormChanges();
}
}
private void checkFormChanges()
{
// Close the form and warn if record not saved
//Check for unsaved changes
if (formDataChanged == true)
{
DialogResult dr = new DialogResult();
dr = MessageBox.Show("Data on this form has changed, Click OK to discard changes", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
if (dr == DialogResult.OK)
{
formDataChanged = false;
this.Close();
}
}
else
{
this.Close();
}
}
Update 2
I just kept adding conditions until all the possibilities worked.
I think it is a confusing solution but it works.
Working Code
private void buttonClose_Click(object sender, EventArgs e)
{
closeButtonClicked = true;
checkSaveChanges();
closeButtonClicked = false;
}
private void FormTelephoneLog_FormClosing(object sender, FormClosingEventArgs e)
{
if (closeButtonClicked == false)
{
if (formDataChanged == true)
{
checkSaveChanges();
}
e.Cancel = closeCancelled;
closeCancelled = false;
}
}
private void checkSaveChanges()
{
// Close the form and warn if record not saved
//Check for unsaved changes
if (formDataChanged == true)
{
DialogResult dr = new DialogResult();
dr = MessageBox.Show("Data on this form has changed, Click OK to discard changes", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
if (dr == DialogResult.OK)
{
formDataChanged = false;
this.Close();
}
else
{
closeCancelled = true;
}
}
else
{
this.Close();
}
}

One does not tell a closing form to close. You either let it close or cancel it, that's all there is to it.
Calling this.Close() will warp space-time around itself, cause an infinite loop, and annoy a certain Time lord. Pray that you don't get a Q, they're a bunch of pricks.
Here's an example:
bool UnsavedChanges; // Some bool your controls set true during change events.
// Set it false after saving and after loading things
// ie.: in a RichTextBox when loading a file, so doesn't count as a "change".
void FormClosingEvent(object sender, FormClosingEventArgs e) {
if (UnsavedChanges) {
var result = MessageBox.Show("You have unsaved changes!", "Quit without saving?", MessageBoxButtons.YesNo);
if (result == DialogResult.No) {
e.Cancel = true;
}
}
}
You were thinking too hard.

I ended up figuring it out.
Seems like an ugly solution to me but it works.
See Update 2 in my question above.

Related

Cancel form closing

I'm writing a WinForm desktop application. In the main form, the user clicks on a button which calls another form for the user to submit data to. At the end of this submission process, the user clicks on a "Save" menu item to close the subform.
This is the code for the subform calling:
private void btnSubmit_Click(object sender, EventArgs e)
{
// code for setting myFormArgs
myForm form = new myForm(myFormArgs);
form.ShowDialog();
// the user clicked "Yes" on a "Confirm" MessageBox
if (form.DialogResult == DialogResult.Yes)
{
// code for saving data
form.Dispose();
}
}
and this is the code for the "Save" menu item in the subform:
private void menuSave_Click(object sender, EventArgs e)
{
string message, title;
MessageBoxIcon icon;
MessageBoxButtons buttons;
if(DataSubmitted)
{
if(ValidData)
{
message = "Confirm?";
title = "Select an action";
icon = MessageBoxIcon.Information;
buttons = MessageBoxButtons.YesNo;
}
else
{
message = "Incomplete data";
title = "Error";
icon = MessageBoxIcon.Error;
buttons = MessageBoxButtons.OK;
}
}
else
{
message = "No data submitted";
title = "Error";
icon = MessageBoxIcon.Error;
buttons = MessageBoxButtons.OK;
}
this.DialogResult = MessageBox.Show(message, title, buttons, icon);
if (this.DialogResult == DialogResult.Yes) this.Close();
else this.OnFormClosing(new FormClosingEventArgs(CloseReason.None, true));
}
The problem is that the code will always get back to the calling method, thus closing (maybe just hiding?) the sub-form, even if the this.Close() method isn't called.
Thanks in advance.
you should not make a new event instance, those are things you would want to avoid
instead try:
DialogResult dialogResult = MessageBox.Show("Sure", "Some Title",
MessageBoxButtons.YesNo);
if(dialogResult == DialogResult.Yes)
{
Close();
}
Events are supposed to occur automatically, so 'OnFormClosing' will raise when the form will close.
also i recommend to use this.Close instead of Dispose
Form.Close() sends the proper Windows messages to shut down the win32 window. During that process, if the form was not shown modally, Dispose is called on the form. Disposing the form frees up the unmanaged resources that the form is holding onto.
for more organized code,
try making an instance of the form from the main form
and handle the dialog result like this:
using (SubForm form = new SubForm())
{
DialogResult dr = form.ShowDialog();
if(dr == DialogResult.Yes)
{
string studdToSave= form.StuffToSave;
SaveToFile(studdToSave);
}
}
I find it strange that you want to close the form when the user just wants to save the data. :)
Save should not close your form.
When you close the form, you should verify if there are unsaved changes.
If there are, ask the user the question if he wants to save his changes before closing and offer him the options Yes, No and Cancel, where Cancel means 'cancel closing the form'.
Depending on wether the user clicked Yes or No, you should or shouldn't save the changes.
If the user clicked cancel, you should cancel closing the form by having an event for the FormClosing event. This event allows you to cancel closing the form.
private void btnClose_Click(object sender, EventArgs e)
{
if (unsavedChanges)
{
var result = MessageBox.Show("Save changes?", "unsaved changes", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
SaveChanges();
}
if (result == DialogResult.Cancel)
{
cancelClose = true;
}
this.Close();
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = cancelClose;
cancelClose = false;
}
The code above is usefull when 'Form2' is not a modal form.
If you want Form2 to be shown modal, the code above will work as well. However, you can also use the DialogResult proprety of the Form in that case:
private void btnClose_Click(object sender, EventArgs e)
{
if (unsavedChanges)
{
var result = MessageBox.Show("Save changes?", "unsaved changes", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
SaveChanges();
}
if (result == DialogResult.Cancel)
{
result = DialogResult.None;
}
this.DialogResult = result;
}
}

FormClosing event cancel from within another event?

For a school project, I have been asked to create a web browser.
In the context menu I have an "Exit" button, which does a bunch of stuff like uploading data to an SQL database etc. If there is an error in the "try" block, the catch block offers the user a question if he wants to close or not.
In the "formclosing" event, I have connected my already existing "exit" functionality from the "exit" button, so that if the user presses the red "X" (on the window screen) to close the form it will have the same effect.
The problem is that because it's a "formclosing" event, it closes the form even if the user chose "no" in the question dialogue from the "Exit" button.
So my question is how do I cancel the "formclosing" request from within my "Exit" button?
P.S.
Please ignore strange things like the delete sql command, our teacher has some strange requests. Assume that that is indeed the requested functionality.
private void יציאהToolStripMenuItem_Click(object sender, EventArgs e)
{
Favorite fav;
SqlCommand delfav = new SqlCommand("DELETE FROM tblFavorites", conn);
SqlCommand addfav = new SqlCommand("INSERT INTO tblFavorites VALUES (#title, #url)", conn);
addfav.Parameters.Add("#title", SqlDbType.NVarChar, 100);
addfav.Parameters.Add("#url", SqlDbType.NVarChar, 300);
try
{
conn.Open();
delfav.ExecuteNonQuery();
for (int i=0; i<favorites.Items.Count; i++)
{
fav = (Favorite)favorites.Items[i];
addfav.Parameters["#title"].Value = fav.getsettitle;
addfav.Parameters["#url"].Value = fav.getseturl;
addfav.ExecuteNonQuery();
}
Application.Exit();
}
catch
{
DialogResult dialogResult = MessageBox.Show("There was a problem loading your favorites to the database. Do you still wish to quit? \n (All changes to the favorites list will be lost)", "", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
Application.Exit();
}
else
{
//Cancel the formclosing event here!
}
}
finally
{
conn.Close();
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
יציאהToolStripMenuItem_Click(null, null);
}
You should replace the functionalities for these two event handler. Also you should use this.Close() instead of Application.Exit()
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// your code
try
{
// your code
}
catch
{
DialogResult dialogResult = MessageBox.Show("There was a problem loading your favorites to the database. Do you still wish to quit? \n (All changes to the favorites list will be lost)", "", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
{
//Cancel the formclosing event here!
e.Cancel = true;
}
}
finally
{
conn.Close();
}
}

Validating user's input before continuing program execution

I have created a program that contains (for now) 2 forms.
In the first form I am asking the user for a file. After the user has selected a file, another form is called followed by closing of the current form.
An if statement indicates if the user has inserted a file when the Open File button is pressed, and if not, the second form will not be loaded.
The problem is that if the user clicks the Close button on the first form (the current one), the form closes and the next one is called.
The options of the next form are based on the user's input in the first form (where the user is asked to select a file), so if the second form is called when the user cancels the first form, it will create problems for methods in the second form.
Any ideas about how to handle the Close Button?
If you want to prevent closing the form you can handle the event
bool cancel = true;
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = cancel;
base.OnFormClosing(e);
}
Remember to change cancel to false when it's done to close the form.
There is an event named "FormClosing" on forms.
A quick sample:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("Are you sure you want to quit?", MessageBoxButtons.YesNo) == DialogResult.No)
{
e.Cancel = true;
}
}
I am assuming that you have an OpenfileDialog (to allow the user to select a file),and a button probably named Open File to pass the filename to next form.If this is the case,then you can try to to disable the open button if no file has been selected.
Consider the code below as the function where all logic takes place;
private void BrowseFile()
{
//dlgopenfile is the name of Openfiledialog that allows the user to browse for a file.
//string filename is the name of selected file if any.
//Form2 is the next form.
try
{
switch (dlgopenfile.ShowDialog())
{
case DialogResult.OK://If Ok(Yes) button is pressed on Openfiledialog.
filename = dlgopenfile.FileName;
break;
case DialogResult.Cancel://If Cancel button is pressed on Openfiledialog.
filename = "";
break;
}
if (filename.Length >= 1)
{
if (File.Exists(filename) == true)
{
ButtonOpenFile.Enabled = true;
}
else
{
ButtonOpenFile.Enabled = false;
throw new FileNotFoundException("The file you selected does not exist.");
}
}
}
catch (FileNotFoundException ex)
{
MessageBox.Show(ex.Message, "Form1", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
The next function occurs if user tries to close the form in mid session.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
switch (MessageBox.Show("Do you want to exit ?", "Form1", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk))
{
case DialogResult.Yes:
this.Close();
break;
case DialogResult.No:
e.Cancel = true;
break;
}
}
catch (Exception)
{
//Exception handling code goes here.
}
}
Finally the function below calls Form2's constructor with the selected file as argument.
private void ButtonOpenFile_Click(object sender, EventArgs e)
{
//This Button is enabled only if the file has been selected and if its exists.
Form2 form2 = new Form2(filename);//filename is the name of selected file,as decided in function BrowseFile().
this.Close();//Close Form1.
form2.ShowDialog();//Show Form2 as modal dialog.
}
Hope it would help you achieve what you need.Anything more,please let me know.
Proposally you start you form with form.ShowDialog(), which returns DialogResult. You should check whether it is DialogResult.Ok or whether form.DialogResult != DialogResult.None. In the form, if the user inserts the file you can set the form.DialogResult explicitely to DialogResult.Ok
Just you can handle the close event with your own logic
private void Form1_FormClosing_1(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show(text:"Are you sure you want to quit?",caption:string.Empty,buttons: MessageBoxButtons.YesNo) == DialogResult.No)
{
e.Cancel = true;
}
}

c# cancel application from closing if hidden form has unsaved changes

after searching google and stackoverflow I'm unable to find an answer that helped me in my situation. I've got an application with a start menu(form). when the user presses the X(close) of a (any) form I reload the start menu. Now when that(menu) form gets closed I want to check and notify the user wheter any now hidden forms are in editing mode (with or without unsaved changes) if user presses cancel I want to show that form and stop the application from closing. Now my problem is how do i stop the application from stopping in the code of the other forms. I have an override of the dispose method of the subforms that calls this.close so the ok/cancel messagebox shows but after the MB the start menu closes anyway stopping the program. Should I look for a different method of handling these things or is there a method or eventhandler to modify so this /\ can work?
EDIT:
ok here's parts of the code in order of being called. Where does i go wrong?
private void Menu_FormClosing(object sender, FormClosingEventArgs e)
{
Global.Forms.Remove(this);
if (!Global.Clean_Forms())
{
e.Cancel = true;
Global.Forms.Add(this);
}
}
public static void Clean_Forms()
{
foreach (Form f in Forms)
{
if (f is Menu)
{
//do nothing
}
else
{
if (!f.IsDisposed)
{
f.Close();
}
}
}
if (Forms.Count != 0)
{
isClean = false;
/* String a = "";
foreach (Form f in Forms)
{
a += f.ToString() + ": ";
}
MessageBox.Show(a);*/
}
else
{
isClean = true;
}
}
Yet this doesnt work, application just closes down.
the closing event handler of random form
private void persoon_form_FormClosing(object sender, FormClosingEventArgs e)
{
if (editing)
{
DialogResult dr;
dr = MessageBox.Show("uw wijzigingen gaan verloren. Doorgaan?", "sluiten", MessageBoxButtons.OKCancel);
if (dr == DialogResult.Cancel)
{
e.Cancel = true;
}
else if (dr == DialogResult.OK)
{
Global.size = this.Size;
Global.position = this.Location;
Global.Forms.Remove(this);
Form f = Global.menu();
f.Show();
this.Dispose();
}
}//somethingelse}
EDIT: #cody gray changed onclose and clean_form still no effect, (shouldn't the messagebox in the Closing event of the subform be shown anyway? cause it doesnt)
You should not be doing this in the Dispose method. Instead, try handling the FormClosing event.
This event occurs before the form is closed, so you can check whatever state you need to, and cancel the close if necessary by setting e.Cancel to True.
For example:
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
// Check to see if the user is allowed to close this form
if (!allowClose)
{
// Prevent this form from being closed
MessageBox.Show("This form cannot be closed yet!");
e.Cancel = true;
}
}
I think i figured it out! I changed two things. 1 I made sure that I didn't call this.dispose anywhere as that will result in disposing (not closing) any child forms hence no onclosing event is called. 2. the foreach loop when checking if main menu can close generated collection was modified errors. so with this I made sure it closes in the right order thnx Cody Gray for pointing me in the right direction:
Stack<Form> stack = new Stack<Form>();
foreach (Form f in Forms)
{
if (f is Menu)
{
//do nothing
}
else
{
if (!f.IsDisposed)
{
stack.Push(f);
}
}
}
for (int i = 0; i < stack.Count; i++)
{
Form temp = stack.Pop();
temp.Close();
}

Preventing a dialog from closing in the button's click event handler

I have a dialog that I show with <class>.ShowDialog(). It has an OK button and a Cancel button; the OK button also has an event handler.
I want to do some input validation in the event handler and, if it fails, notify the user with a message box and prevent the dialog from closing. I don't know how to do the last part (preventing the close).
You can cancel closing by setting the Form's DialogResult to DialogResult.None.
An example where button1 is the AcceptButton:
private void button1_Click(object sender, EventArgs e) {
if (!validate())
this.DialogResult = DialogResult.None;
}
When the user clicks button1 and the validate method returns false, the form will not be closed.
Given that you've specified you want a pop error dialog, one way of doing this is to move your validation into a OnClosing event handler. In this example the form close is a aborted if the user answers yes to the question in the dialog.
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// Determine if text has changed in the textbox by comparing to original text.
if (textBox1.Text != strMyOriginalText)
{
// Display a MsgBox asking the user to save changes or abort.
if(MessageBox.Show("Do you want to save changes to your text?", "My Application",
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// Cancel the Closing event from closing the form.
e.Cancel = true;
// Call method to save file...
}
}
}
By setting e.Cancel = true you will prevent the form from closing.
However, it would be a better design/user experience to display the validation errors inline (via highlighting the offending fields in some way, displaying tooltips, etc.) and prevent the user from selecting the OK button in the first place.
Don't use the FormClosing event for this, you'll want to allow the user to dismiss the dialog with either Cancel or clicking the X. Simply implement the OK button's Click event handler and don't close until you are happy:
private void btnOk_Click(object sender, EventArgs e) {
if (ValidateControls())
this.DialogResult = DialogResult.OK;
}
Where "ValidateControls" is your validation logic. Return false if there's something wrong.
You can catch FormClosing an there force the form to remain opened.
use the Cancel property of the event argument object for that.
e.Cancel = true;
and it should stop your form from closing.
This doesn't directly answer your question (other already have), but from a usability point of view, I would prefer the offending button be disabled while the input is not valid.
Use this code:
private void btnOk_Click(object sender, EventArgs e) {
if (ValidateControls())
this.DialogResult = DialogResult.OK;
}
The problem of it is that the user has to clic two times the buttons for closing the forms;
Just add one line in the event function
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
this->DialogResult = System::Windows::Forms::DialogResult::None;
}
I wish I had time to find a better example, but you would be much better off using the existing windows forms validation techniques to do this.
http://msdn.microsoft.com/en-us/library/ms229603.aspx
void SaveInfo()
{
blnCanCloseForm = false;
Vosol[] vs = getAdd2DBVosol();
if (DGError.RowCount > 0)
return;
Thread myThread = new Thread(() =>
{
this.Invoke((MethodInvoker)delegate {
picLoad.Visible = true;
lblProcces.Text = "Saving ...";
});
int intError = setAdd2DBVsosol(vs);
Action action = (() =>
{
if (intError > 0)
{
objVosolError = objVosolError.Where(c => c != null).ToArray();
DGError.DataSource = objVosolError;// dtErrorDup.DefaultView;
DGError.Refresh();
DGError.Show();
lblMSG.Text = "Check Errors...";
}
else
{
MessageBox.Show("Saved All Records...");
blnCanCloseForm = true;
this.DialogResult = DialogResult.OK;
this.Close();
}
});
this.Invoke((MethodInvoker)delegate {
picLoad.Visible = false;
lblProcces.Text = "";
});
this.BeginInvoke(action);
});
myThread.Start();
}
void frmExcellImportInfo_FormClosing(object s, FormClosingEventArgs e)
{
if (!blnCanCloseForm)
e.Cancel = true;
}
You can probably check the form before the users hits the OK button. If that's not an option, then open a message box saying something is wrong and re-open the form with the previous state.

Categories

Resources