How do I close a form when the ESC key was hit, but only if no Control handled it?

13.2k views Asked by At

I have a Form that closes itself when the ESC key is pressed, thanks to KeyPreview, ProcessKeyEventArgs, ProcessCmdKey or whatever. But I have a Control on that form that does very relevant things when ESC is pressed (it hides itself) and the Form should not be closed when that happens.

The control uses the KeyDown event and sets the SuppressKeyPress flag to true, but that happens after the aforementioned form key preview, thus having no effect.

Is there some sort KeyPostview ?

How do I not close the form when a Control has a relevant use of a key hit ?

Edit: The control handling ESC is a textbox embedded in a hand-maid ListView. The textbox appears when the user clicks a cell, enabling edition. To validate the new text, ENTER would be nice (that already works, as giving the focus to anything else). To cancel edition, ESC seems most natural.

4

There are 4 answers

2
Hans Passant On BEST ANSWER

You are competing Big Time over the Escape key. Along with the Enter key, that's a very important key in the standard Windows user interface. Just drop a button on form and set the form's CancelButton property to some other button, that will suck the keystroke to that button.

To compete with that, you have to create a control that tells Winforms that you really think that the Escape key is more important. That requires overriding the IsInputKey property. Like this:

using System;
using System.Windows.Forms;

class MyTexBox : TextBox {
    protected override bool IsInputKey(Keys keyData) {
        if (keyData == Keys.Escape) return true;
        return base.IsInputKey(keyData);
    }
    protected override void OnKeyDown(KeyEventArgs e) {
        if (e.KeyData == Keys.Escape) {
            this.Text = "";   // for example
            e.SuppressKeyPress = true;
            return;
        }
        base.OnKeyDown(e);
    }
}
2
Moose On

Can you check to see what control has the focus first? If there's only one control on your form that does something relevant with the escape key, check to see if that's the control that has the focus before you close the form.

6
zsalzbank On

OK - this works:

class CustomTB : TextBox
{
    public CustomTB()
        : base()
    {
        CustomTB.SuppressEscape = false;
    }

    public static bool SuppressEscape { get; set; }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        CustomTB.SuppressEscape = (e.KeyCode == Keys.Escape);
        base.OnKeyUp(e);
    }
}

In your form:

    public Form1()
    {
        InitializeComponent();
        this.KeyPreview = true;
    }

    private void Form1_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Escape && !CustomTB.SuppressEscape)
        {
            this.Close();
        }
        CustomTB.SuppressEscape = false;
    }
0
Jamie Ide On

The basic problem is that the form's Dispose method is called when Close is called, so the form is going to close and there's not much you can do about it.

I would get around this by having the UserControl implement a marker interface, say ISuppressEsc. The form's KeyUp handler can then locate the currently focused control and cancel the close if the focused control implements ISuppressEsc. Be aware that you will have to do extra work to find the focused control if it may be a nested control.

public interface ISuppressEsc
{
    // marker interface, no declarations
}

public partial class UserControl1 : UserControl, ISuppressEsc
{
    public UserControl1()
    {
        InitializeComponent();
    }

    private void textBox1_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Escape)
        {
            textBox1.Text = DateTime.Now.ToLongTimeString();
        }
    }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        KeyPreview = true;
    }

    private void Form1_KeyUp(object sender, KeyEventArgs e)
    {
        var activeCtl = ActiveControl;
        if (!(activeCtl is ISuppressEsc) && e.KeyCode == Keys.Escape)
        {
            Close();
        }
    }
}