Respond to multiple KeyDown events

喜欢而已 提交于 2019-12-01 22:31:33

Here's a simple example of what you can do in order to listen to several keys at the same time, using the keyup and keydown events instead.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace WinFormTest {
    public partial class Form1 : Form {
        private readonly IDictionary<Keys, bool> downState;

        public Form1() {
            InitializeComponent();
            downState = new Dictionary<Keys, bool>();
            downState.Add(Keys.W, false);
            downState.Add(Keys.D, false);

            KeyDown += remember;
            KeyUp += forget;
        }

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            Timer timer = new Timer() { Interval = 100 };
            timer.Tick += updateGUI;
            timer.Start();
        }

        private void remember(object sender, KeyEventArgs e) {
            downState[e.KeyCode] = true;
        }

        private void forget(object sender, KeyEventArgs e) {
            downState[e.KeyCode] = false;
        }

        private void updateGUI(object sender, EventArgs e) {
            label1.Text = downState[Keys.W] ? "Forward" : "-";
            label2.Text = downState[Keys.D] ? "Right" : "-";
        }
    }
}

You might want to investigate going lower-level and using windows hooks to detect keyboard events. This requires P/Invoking into native methods, but is pretty straight-forward. The hook you'd want is WH_LL_KEYBOARD. Details can be found at pinvoke.net.

You'd need a bit of boilerplate, but it's as close to the keyboard events as you can reasonably expect to get:

[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
    public uint vkCode;
    public uint scanCode;
    public uint flags;
    public uint time;
    public IntPtr dwExtraInfo;
}

public delegate IntPtr LowLevelKeyboardProc(int, IntPtr, KBDLLHOOKSTRUCT);

[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idhook, LowLevelKeyboardProc proc, IntPtr hMod, uint threadId);

[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);

public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
    using (var curProc = Process.GetCurrentProcess())
    using (var curMod = curProc.MainModule)
    {
        return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curMod.ModuleName), 0u);
    }
}

public IntPtr MyKeyboardHook(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT keyboardInfo)
{
    if (code < 0)
    {
        return CallNextHookEx(IntPtr.Zero, wParam, ref keyboardInfo);
    }

    // Do your thing with the keyboard info.

    return CallNextHookEx(IntPtr.Zero, code, wParam, ref keyboardInfo);
}

Make sure to unhook your handler when your app stops needing it. The KBDLLHOOKSTRUCT encapsulates all the info Windows will give you about a keyboard event; details of its members can be found at MSDN.

One detail of this kind of hook is that it gets executed on the thread that registered it, so make sure you take note of that, and don't set it on the UI thread if it's going to do anything long-running.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!