向上、向下、向左和向右箭头键不会触发KeyDown事件。

89

我正在构建一个应用程序,所有关键的输入必须由Windows自己处理。

对于每个能够获取焦点的控件,我将tabstop设置为false,但不包括一个面板(但我不知道它是否有效)。

我将KeyPreview设置为true,并在此表单上处理KeyDown事件。

我的问题是有时箭头键不再响应:

  • 当我只按下箭头键时,keydown事件不会触发。

  • 如果我按下带有控制修改器的箭头键,则会触发keydown事件。

您有想法为什么我的箭头键突然停止触发事件吗?


你能否发布一下你在KeyDown事件处理程序中的代码? - ChrisF
也许这会对你有所帮助?https://dev59.com/N0fRa4cB1Zd3GeqP_baI - Maxim Zaslavsky
3
@Maxim,如果一个窗口包含任何子控件,箭头键的关键事件将被抑制,我很确定。你链接的问题是处理没有控件的表单。Daniel Waltrip 的问题并不完全相同。 - snarf
@Snarfblam 我不确定我理解了 - 为什么这会成为一个问题呢? - Maxim Zaslavsky
1
http://support.microsoft.com/kb/320584 - sam yi
10个回答

87

我遇到了完全相同的问题。我考虑了@Snarfblam提供的答案; 然而,如果你阅读MSDN上的文档,ProcessCMDKey方法是用来覆盖应用程序菜单项中的键事件。

我最近发现了这篇来自微软的文章,看起来很有前途:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.previewkeydown.aspx。根据微软的说法,最好的做法是在检测到箭头键后,在PreviewKeyDown事件中设置e.IsInputKey=true; 这样将会触发KeyDown事件。

这对我很有效,并且比重写ProcessCMDKey更不那么hack。


2
这应该是最佳答案,它更加简洁,而且运行得非常出色。 - AStackOverflowUser
没错,它可以工作!答案非常明确,但我想指出IsInputKeye类中:e.IsInputKey=true - T30
3
如果在不检测箭头键的情况下对所有键进行操作,会发生什么情况? - T30
@T30 你将失去菜单的Alt键 - google dev

65
    protected override bool IsInputKey(Keys keyData)
    {
        switch (keyData)
        {
            case Keys.Right:
            case Keys.Left:
            case Keys.Up:
            case Keys.Down:
                return true;
            case Keys.Shift | Keys.Right:
            case Keys.Shift | Keys.Left:
            case Keys.Shift | Keys.Up:
            case Keys.Shift | Keys.Down:
                return true;
        }
        return base.IsInputKey(keyData);
    }
    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);
        switch (e.KeyCode)
        {
            case Keys.Left:
            case Keys.Right:
            case Keys.Up:
            case Keys.Down:
                if (e.Shift)
                {

                }
                else
                {
                }
                break;                
        }
    }

3
请注意,所有控件的制表位应该为false。(我遇到了这样的问题:D) - Iman
4
这实际上是一个黑客技巧。请查看Rodolfo Neuber的答案获取正确的答案。 - Ghigo

18

我正在使用PreviewKeyDown

    private void _calendar_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e){
        switch (e.KeyCode){
            case Keys.Down:
            case Keys.Right:
                //action
                break;
            case Keys.Up:
            case Keys.Left:
                //action
                break;
        }
    }

2
对我来说运行得非常好,不需要继承/覆盖。 - Paul Kapustin
这应该是被接受的答案!!!只需使用PreviewKeyDown事件即可。不需要任何黑客技巧即可工作。更加简洁和简单。 - Joe Gayetty

18

参见Rodolfo Neuber的回答,得到最佳答案。


(我的原始回答):

从控件类派生,您可以覆盖ProcessCmdKey方法。 Microsoft选择从KeyDown事件中省略这些键,因为它们会影响多个控件并移动焦点,但这使得应用程序在任何其他方式下对这些键做出反应非常困难。


似乎ProcessCmdKey是处理键盘输入的唯一准确方式。 - Martin Delille
4
这个答案完全错误。如果我没有覆盖已经处理箭头键的某些基本控件的OnKeyDown(以改变行为),我就不会知道这件事,并且会采用更困难的方法来实现它。请参见下面alpha的答案。 - Joshua
1
即使Alpha的答案对我的情况无效,但Snarfblam的答案有效。谢谢! - zionpi

2

当我从WinForms调用WPF窗口时,遇到了类似的问题。

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.Show();

然而,将窗口显示为对话框时,它起作用了。

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.ShowDialog();

希望这可以帮助到您。

2

很遗憾,由于KeyDown事件的限制,使用箭头键实现这一点相当困难。但是,有几种方法可以解决:

  • 正如@Snarfblam所述,您可以覆盖ProcessCmdKey方法,该方法保留了解析箭头键按下的能力。
  • 正如这个问题的被接受答案所述,XNA有一个名为Keyboard.GetState()的内置方法,允许您使用箭头键输入。但是,WinForms没有这个功能,但可以通过P/Invoke或者使用帮助它的类来实现。

我建议尝试使用那个类。这很简单:

var left = KeyboardInfo.GetKeyState(Keys.Left);
var right = KeyboardInfo.GetKeyState(Keys.Right);
var up = KeyboardInfo.GetKeyState(Keys.Up);
var down = KeyboardInfo.GetKeyState(Keys.Down);

if (left.IsPressed)
{
//do something...
}

//etc...

如果您将此与KeyDown事件结合使用,我认为您可以可靠地完成您的目标。

1
为了在表单控件中捕获按键,您必须派生一个新类,该类基于您想要的控件类,并覆盖 ProcessCmdKey() 方法。
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    //handle your keys here
}

例子:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    //capture up arrow key
    if (keyData == Keys.Up )
    {
        MessageBox.Show("You pressed Up arrow key");
        return true;
    }

    return base.ProcessCmdKey(ref msg, keyData);
}

完整的源代码...C#中的箭头键

Vayne


0

我认为最好的方法是按照MSDN上所说的方式处理,链接如下: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.previewkeydown.aspx

但是要根据实际需要进行处理。我的方法(如下面的示例)是捕获每个KeyDown事件;-)

    /// <summary>
    /// onPreviewKeyDown
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
    {
        e.IsInputKey = true;
    }

    /// <summary>
    /// onKeyDown
    /// </summary>
    /// <param name="e"></param>
    protected override void OnKeyDown(KeyEventArgs e)
    {
        Input.SetFlag(e.KeyCode);
        e.Handled = true;
    }

    /// <summary>
    /// onKeyUp
    /// </summary>
    /// <param name="e"></param>
    protected override void OnKeyUp(KeyEventArgs e)
    {
        Input.RemoveFlag(e.KeyCode);
        e.Handled = true;
    }

0
protected override bool IsInputKey(Keys keyData)
{
    if (((keyData & Keys.Up) == Keys.Up)
        || ((keyData & Keys.Down) == Keys.Down)
        || ((keyData & Keys.Left) == Keys.Left)
        || ((keyData & Keys.Right) == Keys.Right))
        return true;
    else
        return base.IsInputKey(keyData);
}

0

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