C# – WinForms Validating event prevents Escape key closing the form

cnetvalidationwinforms

I have a simple Form with a single TextBox, plus OK and Cancel buttons. The Form's AcceptButton and CancelButton are set correctly, and the OK and Cancel buttons have their DialogResult set to 'OK' and 'Cancel'.

I want to add validation to the TextBox which will prevent the user from OK-ing the form when validation fails, but which will also allow them to cancel as usual.

The CausesValidation property is True by default on all the controls, but I have changed this to False on the Cancel Button.

Sure enough, clicking OK or pressing the Enter key will run the Validating event I wired up to the TextBox. Pressing the Cancel button bypasses Validating, which is perfect.

However, pressing Escape to cancel the form does not perform the same as pressing the Cancel button – it raises the Validating event and prevents the user from exiting.

Is there any way of making the Escape key perform as intended, i.e. not raise the Validating event, just as if the Cancel button had been pressed?

A complete worked solution is:

Create a new Windows Forms app. Add a second Form to the project.

Paste this code into Form1's constructor, after InitializeComponent():

MessageBox.Show((new Form2()).ShowDialog().ToString());

This shows the DialogResult passed back from our second form.

Paste this code into Form2's constructor, after InitializeComponent():

TextBox txtName = new TextBox();

txtName.Validating +=
    new CancelEventHandler((sender, e) =>
    {
        if (txtName.Text.Length == 3)
        {
            MessageBox.Show("Validation failed.");
            e.Cancel = true;
        }
    });

Button btnOk = new Button
{
    Text = "OK",
    DialogResult = DialogResult.OK
};
Button btnCancel = new Button
{
    Text = "Cancel",
    CausesValidation = false,
    DialogResult = DialogResult.Cancel
};
FlowLayoutPanel panel = new FlowLayoutPanel();
panel.Controls.AddRange(new Control[] 
{
    txtName, btnOk, btnCancel 
});

this.AcceptButton = btnOk;
this.CancelButton = btnCancel;

this.Controls.Add(panel);

In this simplified example the textbox will not let you proceed if there are 3 characters input. You can press the Cancel button or close the form directly even if there are 3 characters present; however pressing the Escape key will not do the same – it fires the Validating event whereas it should be doing the same as pressing Cancel.

Best Answer

Yes, this an awkward quirk of the ValidateChildren method. It doesn't know that canceling was intended. Paste this code to fix the problem:

    protected override void OnFormClosing(FormClosingEventArgs e) {
        base.OnFormClosing(e);
        e.Cancel = false;
    }

To avoid having a Validate event handler running that causes side-effects, like a message box, add this statement to the top of the method:

    private void txtName_Validating(object sender, CancelEventArgs e)
    {
        if (this.DialogResult != DialogResult.None) return;
        // etc..
    }

Paste this code into your form to get the DialogResult set before it tries to validate the form:

    protected override bool ProcessDialogKey(Keys keyData) {
        if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
        return base.ProcessDialogKey(keyData);
    }