-1

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.

  1. No changes, Close Button -> Works
  2. No changes, X Button -> Loops
  3. Discard changes, Close Button -> Works
  4. Discard changes, X Button -> Loops
  5. Don't Discard changes, Close Button -> Works
  6. 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();
    } 

}
David P
  • 411
  • 7
  • 21
  • You'll probably want to check the [CloseReason](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.formclosingeventargs.closereason?view=netframework-4.8#System_Windows_Forms_FormClosingEventArgs_CloseReason). – ProgrammingLlama Aug 09 '19 at 06:51
  • 1
    @John "but it goes into some weird loop" suggests calling `Form.Close()` from within the `Closing` handler. Just sayin'. – AlwaysLearning Aug 09 '19 at 06:56
  • Possibly, but David hasn't shown us that code, and hopefully checking the close reason will help solve that too (...or just not calling Form.Close in the Closing event) – ProgrammingLlama Aug 09 '19 at 07:00

2 Answers2

1

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.

TCZ8
  • 121
  • 5
-1

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

David P
  • 411
  • 7
  • 21