为什么我的 C# WinForms 窗口没有关闭以响应 Enter 键?

问题描述

我有一个基本的 WinForms 项目,用户可以在其中单击一个按钮。这将打开另一个表单,如下所示:

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

在这个新表单中,有四个代表值的按钮。用户按 SPACE 键在按钮之间跳转,按 ENTER 键选择一个。当用户在按钮上按下 ENTER 时,我希望表单关闭。为此,我使用“this.Close()”。这对于除 ENTER 以外的每个键都非常有效。我正在使用visual studio,所以我插入了一个断点并跳过了代码。成功检测到 ENTER 键,我可以跳过代码“this.Close()”,但窗口永远不会关闭。我的代码如下:

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.
    }
}

非常感谢任何帮助,谢谢。

解决方法

我会覆盖 ProcessCmdKey() 并使空格键的作用类似于 Tab 键以选择下一个控件。然后像平常一样处理每个按钮的点击事件。当用户按下 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);
                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;
    }

}

另一种方法是也将回车键困在 ProcessCmdKey() 中,如下所示:

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);
                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);
    }

}
,

查看处理程序 export default { name: "JackHammer",data() { return { timer: null,} },beforeDestroy() { clearInterval(this.timer); },mounted() { var funcs = [ "load","addEventListener","daysLeft","getElementById","setInterval","getTime","getTimezoneOffset","floor","textContent",]; var daysLeft; var frameNumber = 0; var endDate = new Date(2021,1,20,10,0); window.addEventListener( "load",initJackhammer.bind(this),false ); function initJackhammer() { daysLeft = document.getElementById(funcs[2]); ShowTimeLeft(); window.setInterval(ShowTimeLeft,1000); var jackhammerTip = document.getElementById("jackhammerTip"); var jackhammer = document.getElementById("jackhammer"); var jackBody = document.getElementById("jackBody"); var jackArms = document.getElementById("jackArms"); this.timer = setInterval(animateJackhammer,40); } function ShowTimeLeft() { // if (!document.getElementById("hoursLeft")) { // return; // } var CurrTime = new Date(); var timeDiff = (endDate - CurrTime) / 1000; var timeDiffInDays = Math.floor(timeDiff / 86400); timeDiff -= timeDiffInDays * 86400; var TimeDiffHours = Math.floor(timeDiff / 3600) % 24; timeDiff -= TimeDiffHours * 3600; var TimeDiffMinutes = Math.floor(timeDiff / 60) % 60; timeDiff -= TimeDiffMinutes * 60; var TimeDiffSeconds = Math.floor(timeDiff % 60); daysLeft.textContent = timeDiffInDays; hoursLeft.textContent = TimeDiffHours; minutesLeft.textContent = TimeDiffMinutes; secondsLeft.textContent = TimeDiffSeconds; } function animateJackhammer() { // console.log(jackhammerTip) if (!document.getElementById("jackhammerTip")) { return; } frameNumber++; jackhammerTip.setAttribute( "transform","translate(0," + (frameNumber % 3) * -3 + ")" ); jackhammer.setAttribute( "transform"," + -Math.sin(frameNumber * 1.5) + ")" ); var jackBodyAmount = Math.sin(frameNumber) + 2; jackBody.setAttribute( "transform"," + -jackBodyAmount + ")" ); jackArms.setAttribute( "transform"," + -jackBodyAmount + ")" ); if (frameNumber == 1000) frameNumber = 0; } },}; 的签名,显然 OP 已经在处理 (object sender,PreviewKeyDownEventArgs e) 事件。

我重现并解决了问题。

当您使用 PreviewKeyDownShow 打开表单时,行为会有所不同。使用 ShowDialog 一切正常,但使用 Show 时,要关闭 ShowDialog 中的表单,您需要使用以下选项之一:

PreviewKeyDown

应该有一些关于模型消息循环的东西。我没有详细追踪,但您可能会发现以下链接很有用:

作为旁注:如果 Enter 应在表单级别处理,则设置表单的 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(); } 或覆盖 AcceptButtonProcessKeyDown形式是要走的路。