Monday, February 15, 2016

Windows Forms: Suppressing Control Validation when a Form is closing

Windows Forms developers have too often felt the sting of trying to close a window (a form) by clicking on the X in the form's upper right corner but instead of closing the form triggers the Validating event of a control contained on the form. The form does not close specifically because the focus is presently on a control that requires validation.The Validating event takes place before the FormClosing event so there is no way for the closing event to disable the validating event.

To demonstrate this consider the following form:



The Validating event of the textbox in the previous screenshot is handled as follows to insure the data entered in the textbox is of type integer:

private void textBoxToBeValidated_Validating(
                 object sender, CancelEventArgs e)
{
    int result;

    if (Int32.TryParse(textBoxToBeValidated.Text, out result))
    {
        // success
    }

    else
    {
        MessageBox.Show(
            "Data entered must be of type integer", 
            "Error", MessageBoxButtons.OK, 
            MessageBoxIcon.Error);
    }
}

The most obvious (but doomed to fail) way to allow the form to close when clicking on the X in the upper right corner while suppressing validation is as follows:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    AutoValidate = AutoValidate.Disable;
}

The Form class's AutoValidate property appears to be the solution as during the closing, all validations in the form and its descendant controls can be suppressed. The AutoValidate is of type AutoValidate (an enumeration). Yes, the property, AutoValidate, is the same name as its type, AutoValidate.

From MSDN (AutoValidate Enumeration) the AutoValidate enumeration is defined as follows:



Based on its definition the AutoValidate.Disable prevents the form and its descendants from triggering validations.

Attempting to close the form compiled with the previously shown Form1_FormClosing method results in the following when the TextBox is currently in focus:




What is shows is that handling the form's FormClosing event does not solve the issue because the TextBox's Validating event is raised before the FormClosing event is raised.

When the X in the upper right corner is clicked, windows raises a specific Windows message, WM_CLOSE, where the numeric value of WM_CLOSE is 0x10. To jump the queue of messages and handle WM_CLOSE (FormClosing) before the TextBox's Validating, the method Windows uses to process messages mush be overridden. A base class of the Form class is the Control class. The Control class exposes the method that Windows uses to process messages, WndProc:

The method that handles messages is WndProc defined in the Form class as follows (from MSDN Control.WndProc Method (Message)):




So in the derived class, the Form, it is possible to override the Control bass class' WndProc method. Am example of overriding WndProc is as follows were the code uses the WM_CLOSE (0x10) message number to detecting closing and to disable AutoValidate for the Form:

public const int WM_CLOSE = 0x10;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_CLOSE)
    {
        AutoValidate = AutoValidate.Disable;
    }

    base.WndProc(ref m);
}

When the previous code is added it is possible close the form by clicking the upper right corner's X and the form closes as validating is disabled.