簡體   English   中英

如何檢測當前按下的鍵?

[英]How to detect the currently pressed key?

Windows Forms中,由於Cursors類,您可以隨時知道光標的當前位置。

鍵盤似乎沒有同樣的東西。 例如,是否可以知道是否按下了Shift鍵?

是否絕對有必要跟蹤每個鍵盤通知(KeyDown 和 KeyUp 事件)?

if ((Control.ModifierKeys & Keys.Shift) != 0) 

如果Ctrl + Shift按下,這也適用。 如果要檢查是否單獨按下 Shift,

if (Control.ModifierKeys == Keys.Shift)

如果您在繼承Control的類中(例如窗體),則可以刪除該Control.

下面的代碼是如何檢測幾乎所有當前按下的鍵,而不僅僅是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;
    }
}

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

if (Keyboard.Modifiers == ModifierKeys.Shift)

Keyboard 命名空間還可用於通過 Keyboard.IsKeyDown(Key) 檢查其他鍵的按下狀態,或者如果您正在訂閱 KeyDownEvent 或類似事件,則事件參數包含當前按下的鍵的列表。

這些答案中的大多數要么過於復雜,要么似乎對我不起作用(例如,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);
    }
  }
}

從 .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 或更高版本為目標才能使其正常工作。

您可以P/Invoke到 Win32 GetAsyncKeyState以測試鍵盤上的任何鍵。

您可以將 Keys 枚舉(例如 Keys.Shift)中的值傳遞給此函數,因此只需幾行代碼即可添加它。

if ((ModifierKeys == Keys.Control) && ((e.KeyChar & (char)Keys.F) != 0))
{
     // CTRL+F pressed !
}
if (Control.ModifierKeys == Keys.Shift)
    //Shift is pressed

光標 x/y 位置是一個屬性,按鍵(如鼠標單擊/鼠標移動)是一個事件。 最佳實踐通常是讓接口由事件驅動。 關於您唯一需要上述內容的時間是您嘗試執行 shift + mouseclick 操作。

我發現在 Windows 窗體窗體上管理鍵盤輸入的最佳方法是在擊鍵之后和焦點控件接收事件之前處理它。 Microsoft 維護一個名為.KeyPreview的內置Form級屬性,以促進這一精確的事情:

public frmForm()
{
    // ...
    frmForm.KeyPreview = true;
    // ...
}

然后表單的 _KeyDown、_KeyPress 和/或 _KeyUp 事件可以被編組以在焦點表單控件看到它們之前訪問輸入事件,並且您可以應用處理程序邏輯來捕獲那里的事件或允許它傳遞到焦點表單控件.

雖然在結構上不如XAML 的事件路由體系結構優雅,但它使 Winforms 中窗體級函數的管理簡單得多。 有關注意事項,請參閱有關 KeyPreview 的 MSDN 說明

if (Form.ModifierKeys == Keys.Shift)

如果上面的代碼在窗體的 keydown 事件中並且沒有其他控件捕獲按鍵按下的 keydown 事件,則確實適用於文本框。

也可能希望通過以下方式停止進一步的密鑰處理:

e.Handled = true;

在窗體中:

if( Form.ModifierKeys == Keys.Shift )

這聽起來像是 Stack Overflow 問題的重復Detect Shift key is pressed without using events in Windows Forms? .

如果您需要監聽任何通用類中的鍵,當“窗體”窗口被按下時,這就是您的代碼。 它不監聽全局窗口鍵事件,因此當窗口未激活時它不能用於查看鍵。

表單.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 類,可以通過 Handler.Instance 在任何其他對象中訪問...簡單吧。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM