C#尝试在窗体上捕获KeyDown事件

27
我正在创建一个小游戏,它被绘制在窗体面板上。现在我想捕获按键事件以查看是否按下了箭头键,但问题是我似乎无法捕获它。
让我解释一下,在窗体上我有四个按钮和其他各种控件,如果用户按下其中一个按钮(触发游戏事件),那么该按钮就会获得焦点,我就无法使用箭头键捕获移动。
我尝试了以下代码:
private void KeyDown(KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Left)
        {
            game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.E);
            game.DrawObjects(panel1.CreateGraphics());
        }
        else if (e.KeyCode == Keys.Right)
        {
            game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.W);
            game.DrawObjects(panel1.CreateGraphics());
        }
        else if (e.KeyCode == Keys.Up)
        {
            game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.N);
            game.DrawObjects(panel1.CreateGraphics());
        }
        else if (e.KeyCode == Keys.Down)
        {
            game.MovePlayer(DonutWarsLibrary.GameObjects.Direction.S);
            game.DrawObjects(panel1.CreateGraphics());
        }
    }

然后当表单按键事件被按下时,我使用了这个

private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
        KeyDown(e);
    }

我还为窗体上的按钮和其他控件添加了keydown,但是没有得到任何响应。我已经在函数内设置了断点以查看它是否被调用了,但是该断点从未触发?

有什么想法吗?

最优化的方法是具有通用的KeyDown事件触发器(无论当前拥有焦点的控件是什么),然后调用KeyDown方法。

4个回答

31

您是否将窗体的 KeyPreview 属性设置为 true?这将使窗体在处理键盘事件时具有“先行权”。

更新:Button 获得焦点时,让它正常工作似乎有点棘手。 Button 控件拦截箭头键按下并以一种方式将焦点移动到 Tab 顺序中的下一个或上一个控件,从而不会引发 KeyDown, KeyUpKeyPress 事件。但是,PreviewKeyDown 事件仍然会触发,因此可以使用它:

private void Form_KeyDown(object sender, KeyEventArgs e)
{
    e.Handled = ProcessKeyDown(e.KeyCode);
}

// event handler for the PreViewKeyDown event for the buttons
private void ArrowButton_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
    ProcessKeyDown(e.KeyCode);

}

private bool ProcessKeyDown(Keys keyCode)
{
    switch (keyCode)
    {
        case Keys.Up:
            {
                // act on up arrow
                return true;
            }
        case Keys.Down:
            {
                // act on down arrow
                return true;
            }
        case Keys.Left:
            {
                // act on left arrow
                return true;
            }
        case Keys.Right:
            {
                // act on right arrow
                return true;
            }
    }
    return false;
}

然而,焦点的移动方式相当丑陋...


它半工作,现在我得到了按键事件,但当我按下按钮时,它们仍然变得聚焦,然后它停止工作,除非我按顶部箭头键直到它们全部循环并失去焦点。 - Patrick
@Patrick:是的,箭头键和按钮控件似乎有非常紧密的关系。我已经更新了我的答案,将其推进了一步。 - Fredrik Mörk

18

我认为解决这个问题最简单的方法是通过覆盖表单的ProcessCmdKey()方法。这样,无论何时按下键,您的键处理逻辑都会被执行。此外,您甚至可以选择在处理完键后是否将其传递给当前拥有焦点的控件(返回false)或不传递给它(返回true)。


你的小游戏示例可以像这样重写:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == Keys.Left)
    {
        MoveLeft(); DrawGame(); DoWhatever();
        return true; //for the active control to see the keypress, return false
    }
    else if (keyData == Keys.Right)
    {
        MoveRight(); DrawGame(); DoWhatever();
        return true; //for the active control to see the keypress, return false
    }
    else if (keyData == Keys.Up)
    {
        MoveUp(); DrawGame(); DoWhatever();
        return true; //for the active control to see the keypress, return false
    }
    else if (keyData == Keys.Down)
    {
        MoveDown(); DrawGame(); DoWhatever();
        return true; //for the active control to see the keypress, return false
    }
    else
        return base.ProcessCmdKey(ref msg, keyData);
}

很棒,但空格键无法处理。 - Singlet
2
很棒的解决方案。可以捕获所有按键,包括空格键。 - Kibbee
这是解决那个问题最简单的方法,对我也完美地起作用了。 - Dean Seo

17

覆盖 IsInputKey 行为


您必须覆盖 IsInputKey 行为,以通知系统您希望将右箭头键视为输入键而不是特殊功能键。为此,您必须为每个控件重写该方法。 我建议您创建自己的按钮,例如 MyButton

下面的类创建了一个自定义按钮,它覆盖了 IsInputKey 方法,使得右箭头键不会被视为特殊功能键。从这里开始,您可以轻松地将其应用于其他箭头键或任何其他内容。

    public partial class MyButton : Button
    {
        protected override bool IsInputKey(Keys keyData)
        {
            if (keyData == Keys.Right)
            {
                return true;
            }
            else
            {
                return base.IsInputKey(keyData);
            }
        }
    }

之后,你可以在每个不同的按钮中或在表单本身中处理你的keyDown事件:

在按钮的KeyDown方法中尝试设置这些属性:

private void myButton1_KeyDown(object sender, KeyEventArgs e)
{
  e.Handled = true;
  //DoSomething();
}

处理表单中的常见行为:(不要在按钮中设置 e.Handled = true;

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    //DoSomething();
}

1
它半工作,现在我得到了按键事件,但当我按下按钮时,它们仍然会变为焦点,然后它停止工作,除非我按顶部箭头键直到它们全部循环并失去焦点。 - Patrick
你是如何捕获“Keys.Left”和“Keys.Right”按键事件的? - Luis Filipe
@bentsai:谢谢。不知道为什么我不能全部纠正,总有一个漏掉 :) - Luis Filipe

7
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        KeyPreview = true;
        KeyDown += new KeyEventHandler(Form1_KeyDown);
    }

    void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        System.Diagnostics.Debug.Write(e.KeyCode);
    }
}

1
你不知道你的回答节省了我很多时间。非常感谢。 - Prakash Kunwar
应该是“KeyPreview = true;”,这样才会触发KeyDown事件。 - Carlos Liu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接