ba126
ba126

Reputation: 109

Why is my C# WinForms window not closing in response to Enter key?

I have a basic WinForms project where the user clicks a button. This opens another form as follows:

form2 myForm2 = new form2();
myForm2.ShowDialog();

Inside this new form, there are four buttons which represent values. The user presses the SPACE key to jump between buttons and the ENTER key to select one. When the user presses ENTER on a button I want the form to close. For this is I use 'this.Close()'. This works for absolutely fine with every key other than ENTER. I am using visual studio so I have inserted a break point and stepped over the code. The ENTER key is detected successully and I can step over the code 'this.Close()' but the window never closes. My code is a followed:

private void button1_KeyDown(object sender, PreviewKeyDownEventArgs e)
{
    if (e.KeyCode == Keys.Space)
    {
         // Change colour of the button you are on. This works fine.
    }
    else if (e.KeyCode == Keys.Enter)
    {
        this.Close(); 
        // This will close the form with all keys other than the Enter key. Yet the enter key is 
        // successfully detected and the program enters this else if statement.
    }
}

Any help is much appreciated, Thanks.

Upvotes: 0

Views: 448

Answers (2)

Reza Aghaei
Reza Aghaei

Reputation: 125197

Looking into signature of the handler (object sender, PreviewKeyDownEventArgs e) apparently OP is already handling PreviewKeyDown event.

I reproduced and solved the problem.

The behavior is different when you open a Form using Show or ShowDialog. Everything works as expected with Show, but when using ShowDialog, to close the form in PreviewKeyDown, you need to use one of the following options:

private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
    e.IsInputKey = true; 
    this.Close();

    // OR 
    // if (e.KeyData == Keys.Enter)
    //     this.BeginInvoke(new Action(() => this.Close())); 

    // OR first hide, then close, without calling BeginInvoke
    // this.Hide();
    // this.Close();
}

There should be something about model message loop. I didn't traced in details, but you may find the following links useful:

Just as a side note: If the Enter should be handled at form level, then setting AcceptButton of the form or overriding ProcessKeyDown or ProcessDialogKey of the form is the way to go.

Upvotes: 0

Idle_Mind
Idle_Mind

Reputation: 39132

I'd override ProcessCmdKey() and make the space key act like the tab key to select the next control. Then just handle each the click event of each button like normal. The click handler for the buttons will fire when the user presses enter:

public partial class form2 : Form
{

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        Button btn = this.ActiveControl as Button;
        if (btn != null)
        {
            if (keyData == Keys.Space)
            {
                // possibly do something else with "btn"?...
                this.SelectNextControl(btn, true, true, true, true);
                return true; // suppress default handling of space
            }
        }            
        return base.ProcessCmdKey(ref msg, keyData);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // possibly set some value?
        Console.WriteLine("button1");
        this.DialogResult = DialogResult.OK;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        // possibly set some value?
        Console.WriteLine("button2");
        this.DialogResult = DialogResult.OK;
    }

    private void button3_Click(object sender, EventArgs e)
    {
        // possibly set some value?
        Console.WriteLine("button3");
        this.DialogResult = DialogResult.OK;
    }

    private void button4_Click(object sender, EventArgs e)
    {
        // possibly set some value?
        Console.WriteLine("button4");
        this.DialogResult = DialogResult.OK;
    }

}

An alternative approach would be to also trap the enter key in ProcessCmdKey() like this:

public partial class Form2 : Form
{

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        Button btn = this.ActiveControl as Button;
        if (btn != null)
        {
            if (keyData == Keys.Space)
            {
                Console.WriteLine("Space -> Tab");
                // possibly do something else with "btn"?...
                this.SelectNextControl(btn, true, true, true, true);
                return true; // suppress default handling of space
            }
            else if (keyData == Keys.Enter)
            {
                Console.WriteLine("Enter in ProcessCmdKey() for " + btn.Name);
                // possibly do something else with "btn"?...
                this.Close();
                // < or >
                this.DialogResult = DialogResult.OK;
                return true;
            }
        }            
        return base.ProcessCmdKey(ref msg, keyData);
    }

}

Upvotes: 1

Related Questions