如何检测当前按下的键?

Posted

技术标签:

【中文标题】如何检测当前按下的键?【英文标题】:How to detect the currently pressed key? 【发布时间】:2010-11-09 04:11:01 【问题描述】:

在Windows Forms 中,由于Cursors 类,您可以随时知道光标的当前位置。

键盘似乎没有同样的功能。是否有可能知道,例如,是否按下了 Shift 键?

是否绝对有必要跟踪每个键盘通知(KeyDown 和 KeyUp 事件)?

【问题讨论】:

您是在 WPF 环境中工作还是在其他环境中工作? @epotter:第二个词是 WinForms。 【参考方案1】:
if ((Control.ModifierKeys & Keys.Shift) != 0) 

如果 Ctrl+Shift 按下,这也是如此。如果要检查是否单独按下了 Shift,

if (Control.ModifierKeys == Keys.Shift)

如果你在一个继承Control的类中(比如表单),你可以去掉Control.

【讨论】:

除非我遗漏了什么,否则您没有正确回答问题。 OP 询问所有键,仅以 Shift 键为例。那么如何检测其他键,例如 A 到 Z、0 到 9 等。 鉴于他接受了答案,看来他只需要修饰键。如果您需要其他密钥,则需要调用GetKeyState API 函数。 不需要 GetKeyState。您只需要添加一个消息过滤器。看我的回答。 对于 WPF 解决方案,您可以使用 Keyboard.Modifiers == ModifierKeys.Shift(对于那些来到这里进行搜索的人) 可以用(Control.ModifierKeys & Keys.Shift) != 0代替Control.ModifierKeys.HasFlag(Keys.Shift)【参考方案2】:

下面的代码是如何检测几乎所有当前按下的键,而不仅仅是 Shift 键。

private KeyMessageFilter m_filter = new KeyMessageFilter();

private void Form1_Load(object sender, EventArgs e)

    Application.AddMessageFilter(m_filter);



public class KeyMessageFilter : IMessageFilter

    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private bool m_keyPressed = false;

    private Dictionary<Keys, bool> m_keyTable = new Dictionary<Keys, bool>();

    public Dictionary<Keys, bool> KeyTable
    
        get  return m_keyTable; 
        private set  m_keyTable = value; 
    

    public bool IsKeyPressed()
    
        return m_keyPressed;
    

    public bool IsKeyPressed(Keys k)
    
        bool pressed = false;

        if (KeyTable.TryGetValue(k, out pressed))
        
            return pressed;
        

        return false;
    

    public bool PreFilterMessage(ref Message m)
    
        if (m.Msg == WM_KEYDOWN)
        
            KeyTable[(Keys)m.WParam] = true;

            m_keyPressed = true;
        

        if (m.Msg == WM_KEYUP)
        
            KeyTable[(Keys)m.WParam] = false;

            m_keyPressed = false;
        

        return false;
    

【讨论】:

GetKeyState 会更有效率。当 Windows 已经为您完成跟踪时,跟踪所有键是没有意义的。 @Slaks,除非您有一些基准数据,否则您只是在猜测。此外,GetKeyState 会告诉您按键的状态,如果您可以首先捕获该键盘事件。我对这个问题的解读是,OP 想知道如何随时获取密钥的状态。所以 GetKeyState 本身是没有用的。 您将如何使用它来显示被按下的键? Gabriel:在表单中创建一个 KeyMessageFilter 实例作为字段。将其传递给 Form_Load 中的 Application.AddMessageFilter()。然后在此实例上为您感兴趣的每个/任何键调用 IsKeyPressed()。 @Ash 感谢您的回答 - 您能做一个代码示例来检查上面的 SHIFT 键等吗?【参考方案3】:

如果你使用WPF或者参考System.Windows.Input也可以看看下面的

if (Keyboard.Modifiers == ModifierKeys.Shift)

Keyboard 命名空间也可用于通过 Keyboard.IsKeyDown(Key) 检查其他键的按下状态,或者如果您正在订阅 KeyDownEvent 或类似事件,则事件参数携带当前按下的键的列表。

【讨论】:

实际上 Keyboard.Modifiers 并不总是能正常工作。必须找到困难的方法:discoveringdotnet.alexeyev.org/2008/09/… 除了不使用 Forms 修饰符之外,System.Windows.Input 修饰符是一个不同的命名空间,并且每次都对我们很好。【参考方案4】:

这些答案中的大多数要么太复杂,要么似乎对我不起作用(例如 System.Windows.Input 似乎不存在)。然后我找到了一些可以正常工作的示例代码: http://www.switchonthecode.com/tutorials/winforms-accessing-mouse-and-keyboard-state

如果以后页面消失,我会在下面发布相关的源代码:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MouseKeyboardStateTest

  public abstract class Keyboard
  
    [Flags]
    private enum KeyStates
    
      None = 0,
      Down = 1,
      Toggled = 2
    

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    private static extern short GetKeyState(int keyCode);

    private static KeyStates GetKeyState(Keys key)
    
      KeyStates state = KeyStates.None;

      short retVal = GetKeyState((int)key);

      //If the high-order bit is 1, the key is down
      //otherwise, it is up.
      if ((retVal & 0x8000) == 0x8000)
        state |= KeyStates.Down;

      //If the low-order bit is 1, the key is toggled.
      if ((retVal & 1) == 1)
        state |= KeyStates.Toggled;

      return state;
    

    public static bool IsKeyDown(Keys key)
     
      return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
    

    public static bool IsKeyToggled(Keys key)
     
      return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
    
  

【讨论】:

System.Windows.Input 存在;对于其他为此苦苦挣扎的人,您需要添加对PresentationCore 的引用,以及对WindowsBase 的附加引用以访问System.Windows.Input.Key 枚举。此信息始终可以在 MSDN 上找到。 这个类应该是static,而不是abstract 链接已损坏 (404)。 “万一以后页面消失,我在下面贴出相关源代码”【参考方案5】:

从 .NET Framework 3.0 版开始,可以使用新的 System.Windows.Input 命名空间中的 Keyboard.IsKeyDown 方法。例如:

if (((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && Keyboard.IsKeyDown(Key.F))

    // CTRL + F is currently pressed

即使它是 WPF 的一部分,该方法也适用于 WinForm 应用程序(前提是您添加了对 PresentationCore.dllWindowsBase.dll 的引用)。然而不幸的是,Keyboard.IsKeyDown 方法的 3.0 和 3.5 版本不适用于 WinForm 应用程序。因此,如果您确实想在 WinForm 应用程序中使用它,则需要以 .NET Framework 4.0 或更高版本为目标才能使其工作。

【讨论】:

请注意,这仅适用于 WPF @DiegoVieira 实际上,这不是真的。该功能是作为 WPF 的一部分添加的,它要求引用这些 WPF 库,但 Keyboard.IsKeyDown 方法有效,即使在 WinForm 项目中也是如此。 确实,你必须添加 PresentationCore.dll 请注意,如果针对 .Net 3.5 或更早版本(仅 4.0+),这不起作用(在 WinForms 中),因为 Win32KeyboardDevice.GetKeyStatesFromSystem(Key) 的实现发生了变化:( @LMK 不错的收获。我自己测试并验证了你所说的。我更新了答案以反映该信息。谢谢!【参考方案6】:

您可以P/Invoke 下到 Win32 GetAsyncKeyState 来测试键盘上的任何键。

您可以将 Keys 枚举中的值(例如 Keys.Shift)传递给此函数,因此只需几行代码即可添加它。

【讨论】:

Keyboard 未被编译器识别,但 user32 中的 GetAsyncKeystate 工作得很好。谢谢!【参考方案7】:
if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))

     // CTRL+F pressed !

【讨论】:

【参考方案8】:
if (Control.ModifierKeys == Keys.Shift)
    //Shift is pressed

光标 x/y 位置是一个属性,而按键(如鼠标单击/鼠标移动)是一个事件。最佳实践通常是让界面由事件驱动。如果您尝试执行 shift + mouseclick 操作,则您需要上述内容的唯一时间。

【讨论】:

【参考方案9】:

我发现在 Windows 窗体窗体上管理键盘输入的最佳方法是在击键之后和焦点控件接收事件之前对其进行处理。微软维护了一个名为 .KeyPreview 的内置 Form-level 属性来促进这个精确的事情:

public frmForm()

    // ...
    frmForm.KeyPreview = true;
    // ...

然后表单的 _KeyDown、_KeyPress 和/或 _KeyUp 事件可以被编组以在焦点表单控件看到它们之前访问输入事件,并且您可以应用处理程序逻辑来捕获那里的事件或允许它传递到重点表单控件。

虽然在结构上不如XAML's 事件路由架构那么优雅,但它使 Winforms 中表单级功能的管理更加简单。请参阅MSDN notes on KeyPreview 了解警告。

【讨论】:

【参考方案10】:
if (Form.ModifierKeys == Keys.Shift)

如果上面的代码在表单的 keydown 事件中并且没有其他控件捕获 keydown 事件的 keydown ,则对文本框有效。

也有人可能希望停止进一步的密钥处理:

e.Handled = true;

【讨论】:

【参考方案11】:

在 WinForms 中:

if( Form.ModifierKeys == Keys.Shift )

这听起来像是 Stack Overflow 问题的副本Detect Shift key is pressed without using events in Windows Forms?

【讨论】:

他可能不在乎,但我只想说出来:p【参考方案12】:

如果您需要监听任何通用类中的按键,当“表单”窗口按下时,这就是您的代码。它不监听全局windows按键事件,所以当窗口不活动时它不能用于查看按键。

Form.cs

public partial class Form1 : Form

    public Form1()
    
        // Some other Code
        // Register all Keys pressed
        this.KeyPreview = true;
        KeyHandler.Instance.Init();
        this.KeyDown += Form1_KeyDown;
        this.KeyUp += Form1_KeyUp;
        // Some other Code in the constructor
    

    private void Form1_KeyUp(object sender, KeyEventArgs e)
    
        // Fire event when a key is released
        KeyHandler.Instance.FireKeyUp(sender, e);
    

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    
        // Fire event when a key is pressed
        KeyHandler.Instance.FireKeyDown(sender, e);
    

KeyHandler.cs KeyHandler是一个Singleton Class,可以通过Handler.Instance在任何其他Object中访问...简单对了。

public class KeyHandler

    #region Singleton
    private static KeyHandler instance;
    private KeyHandler()
    
        currentlyPressedKeys = new List<Keys>();
    

    public static KeyHandler Instance
    
        get
        
            if (instance is null)
            
                instance = new KeyHandler();
            
            return instance;
        
    
    #endregion Singleton

    private List<Keys> currentlyPressedKeys;
    public List<Keys> GetCurrentlyPressedKeys  get  return currentlyPressedKeys;  

    public void FireKeyDown(object sender, KeyEventArgs e)
    
        if (!currentlyPressedKeys.Contains(e.KeyCode))
        
            currentlyPressedKeys.Add(e.KeyCode);
            KeyEventKeyPressed(sender, e);
        
    

    public void FireKeyUp(object sender, KeyEventArgs e)
    
        currentlyPressedKeys.Remove(e.KeyCode);
        KeyEventKeyReleased(sender, e);
    

    public event EventHandler<KeyEventArgs> KeyPressed;
    protected virtual void KeyEventKeyPressed(object sender, KeyEventArgs e)
    
        EventHandler<KeyEventArgs> handler = KeyPressed;
        handler?.Invoke(sender, e);
    

    public event EventHandler<KeyEventArgs> KeyReleased;
    protected virtual void KeyEventKeyReleased(object sender, KeyEventArgs e)
    
        EventHandler<KeyEventArgs> handler = KeyReleased;
        handler?.Invoke(sender, e);
    

    public void Init()
    
        // Nothing to initialize yet
    

// 在任何其他类/对象中,现在可以接收在“表单”处于活动状态时触发的 KeyEvent。所以它可以监听任何 Control 对象或其他任何东西中的关键事件。有可能看看例如按下多个键,例如 Shift+Ctrl+Q 或类似的东西。

public class SomeClass

    public SomeClass()
    
        KeyHandler.instance.KeyPressed += Instance_KeyPressed
        KeyHandler.Instance.KeyReleased += Instance_KeyReleased;
    

    public void SomeMethod()
    
        if (KeyHandler.Instance.GetCurrentlyPressedKeys.Contains(Keys.ShiftKey))
        
            // Do Stuff when the method has a key (e.g. Shift/Control...) pressed
        
    

    private void Instance_KeyPressed(object sender, KeyEventArgs e)
    
        // Any Key was pressed, do Stuff then
    

    private void Instance_KeyReleased(object sender, KeyEventArgs e)
    
        // Do Stuff when a Key was Released
    

【讨论】:

以上是关于如何检测当前按下的键?的主要内容,如果未能解决你的问题,请参考以下文章

检测按下的键何时是第一个字符

我试图检测某个被按下的键(Python)

检测功能键被按下的最佳方法是啥?

KeyboardState.GetPressedKeys()是如何组织的?

如何知道 iPhone 键盘中按下的键

如何检查在VB中按下的键