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

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();
}

Related

Check for changes on a windows form before closing

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.

How to reload/reopen the same form C#

I want to reload the current form (not the main form) whenever both of the radio buttons are unchecked. I did this but it won't work.
StreamWriter sw;
using (sw = File.CreateText(path))
{
if (OnewayRadio.Checked == true)
{
sw.WriteLine("One Way Ticket");
}
else if (RoundRadio.Checked == true)
{
sw.WriteLine("Round Trip");
}
else
{
MessageBox.Show("You have not selected your type of trip!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
sw.Close();
File.Delete(path);
sw = File.CreateText(path);
}
sw.WriteLine("Name: " + name.Text);
sw.WriteLine("Number: " + number.Text);
}
A common practice is to open a form using ShowDialog
When your code in the form completes executing it will set a public DialogResult
The caller can then read the DialogResult and take any necessary action.
In your case, you can set the DialogResult to Retry for this specific instance. The opener can then run a loop and continue to show the form again while ShowDialog() == DialogResult.Retry
Form2 testDialog = new Form2();
// Show testDialog as a modal dialog and determine if DialogResult = OK.
while (testDialog.ShowDialog() == DialogResult. Retry)
{
testDialog.Dispose();
testDialog = null;
testDialog = new Form2();
}
The answer here assumes that the form to reopen is allowed to be modal. That is not always the case. I ran into the issue when making a form non-modal instead of modal.
Use .Hide() instead of .Close().
Place the Hide in the FormClosing() event like so,
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Hide();
e.Cancel = true;
}
After the form is "closed" in this way, a next call to Show() will have no issue.
In my case, some actions after closing the form had to be executed. If you normally open in Modal state, the ShowDialog() will return when the form is closed, like so
{
// ..
form1.ShowDialog();
OperationsAfterClose(form1);
// ..
}
A non-modal call form.Show() will return immediately. So any epilogue like my OperationsAfterClose() would be called immediately.
I used a delegate to move the action, as follows,
public delegate void OnFormHide(FrmMapFerryAnalysisSpec2 f);
public partial class Form1 : Form
{
public OnFormHide OnForm1Hide = null;
// ..
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (OnForm1Hide!=null) OnForm1Hide (this); // call the delegate
Hide();
e.Cancel = true;
}
// ..
}
In the calling form I can now pass OperationsAfterClose() as a delegate,
{
// ..
form1.Show();
form1.OnForm1Hide = OperationsAfterClose; // set the delegate
// ..
}
NOTES:
in order to use this safely, the form1 handle should exist and must not be created multiple times !
Also in most applications, the form may only open once. To do that, easiest is to introduce a IsOpenForm1 boolean in the calling form.

Manipulate control on other form

I'm trying to manipulate a PictureBox(pBATalk) to show whenever I close a form(PAInput). So basically I want to show a picture, whenever the 2nd form is closed.
2nd Form
public void PAInput_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
fPAMain.PATalkActive(false);
e.Cancel = true;
}
MainForm:
public void PATalkActive(bool active)
{
//MessageBox.Show("");
if (active == true)
{
pBPATalk.Hide();
}
if (active == false)
{
pBPATalk.Show();
}
}
Whenever I close PAInput it will trigger PATalkActive, but will only fire the MessageBox.Show(""); and not the pBATalk.Hide(); or pBATalk.Show();
Well, I'm not so sure about what you are trying to accomplish here but first of all you're keeping PAInput open, with this line e.Cancel = true . Besides that, MessageBox.Show(""); will open a modal window displaying a text, until it is closed the rest of the method won't run.

WinForms Form won't close on pressing X or Close() in C#

I'm having a bit weird problem with WinForm which seems to refuse to close for some weird reason. I've got very simple gui which sometimes doesn't react for me pressing X or when i use events on buttons it even reaches Close() and does nothing..
private void buttonZapisz_Click(object sender, EventArgs e) {
string plik = textBoxDokumentDoZaladowania.Text;
if (File.Exists(plik)) {
string extension = Path.GetExtension(plik);
string nazwaPliku = Path.GetFileName(plik);
SqlMethods.databaseFilePut(plik, comboBoxTypDokumentu.Text, textBoxKomentarz.Text, sKlienciID, sPortfelID, extension, nazwaPliku);
Close();
}
}
There are no events assigned to FormClosed or FormClosing. So how can I find out what's wrong. Sometimes X will work after the GUI is loaded but after i press Button to save some stuff to database it reaches Close() in that button event and it still is visible and does nothing. Can't use X, nor ALT+F4. I can go around GUI and choose other values for ComboBox without problem.
I call GUI like this:
private void contextMenuDokumentyDodaj_Click(object sender, EventArgs e) {
var lv = (ListView) contextMenuDokumenty.SourceControl;
string varPortfelID = Locale.ustalDaneListViewKolumny(listViewNumeryUmow, 0);
string varKlienciID = Locale.ustalDaneListViewKolumny(listViewKlienci, 0);
if (lv == listViewDokumentyPerKlient) {
if (varKlienciID != "") {
var dokumenty = new DocumentsGui(varKlienciID);
dokumenty.Show();
dokumenty.FormClosed += varDocumentsGuiKlienci_FormClosed;
}
} else if (lv == listViewDokumentyPerPortfel) {
if (varPortfelID != "" && varKlienciID != "") {
var dokumenty = new DocumentsGui(varKlienciID, varPortfelID);
dokumenty.Show();
dokumenty.FormClosed += varDocumentsGuiPortfele_FormClosed;
}
}
}
While I can't close GUI i can work on the main gui without problem too. I can open up same GUI and after opening new GUI i can quickly close it. GUI is very simple with few ComboBoxes,TextBoxes and one EditButton from Devexpress.
Edit: varDocumentsGuiPortfele_FormClosed code allows me to refresh GUI (reload ListView's depending on where the user is on now).
private void varDocumentsGuiPortfele_FormClosed(object sender, FormClosedEventArgs e) {
TabControl varTabControl = tabControlKlientPortfele;
if (varTabControl.TabPages.IndexOf(tabPageDokumentyPerKlient) == varTabControl.SelectedIndex) {
loadTabControlKlientPortfeleBezZmianyUmowy();
}
}
Paste this code into your form classes:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
base.OnFormClosing(e);
}
When that works, you want to find out why you have Validating event handlers that don't want the form to be closed.
Next thing you want to verify is Debug + Exceptions, tick the Thrown box for CLR Exceptions. This makes sure you don't swallow an exception that prevents a form from closing. Or worse, the operating system swallowing the exception, a nasty Windows 7 problem.
If you are getting an Exception in your close method, then the Base closing method is never called.
Put a try{}catch{} around everything

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