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

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

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

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

【问题讨论】:

  • 您是在 WPF 环境中工作还是在其他环境中工作?
  • @epotter:第二个词是 WinForms。

标签: c# .net winforms keyboard


【解决方案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
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2022-11-17
            • 1970-01-01
            • 2014-07-29
            • 2013-04-07
            • 2022-01-03
            • 1970-01-01
            • 2012-07-04
            • 2011-02-26
            相关资源
            最近更新 更多