【问题标题】:Hold Arrow Key Down Event with ProcessCmdKey使用 ProcessCmdKey 按住箭头键向下事件
【发布时间】:2014-08-10 12:58:03
【问题描述】:

只要按住箭头键,我就需要执行某种方法。现在,对于 Visual Studio 2012 中的常规箭头键按下,使用 OnKeyDown 不起作用,所以我需要使用 ProcessCmdKey,这就像一个魅力。有没有办法使用ProcessCmdKey 来检测箭头何时被释放/被按住?


我接受了 Markus 和 Hans 的建议,并将这两个想法结合起来。我使用了ProcessKeyPreview 方法,但我仍然遇到一些问题。当我按住任何箭头键时,此方法不会检测到 WM_KEYDOWN 已经发生......但是当我从键上松开手指时,它实际上确实注意到 WM_KEYUP 已经发生。

有趣的是,当我按住任何其他键(即字母“S”)时,它会正确识别何时按下和释放。我在下面发布了我的代码片段:

const int WM_KEYUP = 0x0101;
const int WM_KEYDOWN = 0x0100;

    protected override bool ProcessKeyPreview(ref Message m)
            {
                int msgVal = m.WParam.ToInt32();            
                if (m.Msg == WM_KEYDOWN)
                {
                    switch ((Keys)msgVal) {
                        case Keys.Down:
                            Console.WriteLine("down pressed"); //not detected
                            break;
                        case Keys.S:
                            Console.WriteLine("S pressed!"); //detected
                            break;
                    }
                }
                if (m.Msg == WM_KEYUP)
                {
                    switch ((Keys)msgVal)
                    {
                        case Keys.Down:
                            Console.WriteLine("down released"); //detected
                            break;
                        case Keys.S:
                            Console.WriteLine("s released!"); //detected
                            break;
                    }
                }
                return base.ProcessKeyPreview(ref m);
            }        

【问题讨论】:

    标签: c# events keyboard key


    【解决方案1】:

    您可以改为重载 ProcessKeyPreview,这样您就可以看到 WM_KEYDOWN 消息以及 WM_KEYUP 消息。请务必为表单打开 KeyPreview。

    WM_KEYDOWN:
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280(v=vs.85).aspx
    
    WM_KEYUP:
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx
    

    例子:

    public partial class TestForm : Form
    {
        public TestForm()
        {
            InitializeComponent();
            this.KeyPreview = true;
        }
    
        const int WM_KEYUP = 0x0101;
        const int WM_KEYDOWN = 0x0100;
    
        protected override bool ProcessKeyPreview(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_KEYDOWN:
                    Console.WriteLine(m.LParam);
                    break;
    
                case WM_KEYUP:
                    Console.WriteLine(m.LParam);
                    break;
            }
            return base.ProcessKeyPreview(ref m);
        }
    }
    

    【讨论】:

    • 他已经尝试过 OnKeyDown(),同样的原因也不起作用。
    • @HansPassant 我一定是误读了这个问题,因为我测试了我的示例,它向我显示了按键事件、重复和最终按键事件。
    • 在表单上放一个按钮来重现 OP 的问题。
    【解决方案2】:

    您无法使用 ProcessCmdKey() 看到 KeyUp 事件,它仅用于处理 KeyDown 事件。如果表单包含控件,您将需要在低得多的级别上解决这个问题。诀窍是在 Winforms 通过正常的键处理和 WndProc 链发送消息之前拦截消息。这需要实现 IMessageFilter 接口。像这样:

    public partial class Form1 : Form, IMessageFilter {  // NOTE: added IMessageFilter
        public Form1() {
            InitializeComponent();
            Application.AddMessageFilter(this);
        }
        protected override void OnFormClosed(FormClosedEventArgs e) {
            Application.RemoveMessageFilter(this);
            base.OnFormClosed(e);
        }
    
        bool IMessageFilter.PreFilterMessage(ref Message m) {
            // Trap WM_KEYUP/DOWN for Keys.Down key
            if ((m.Msg == 0x100 || m.Msg == 0x101) && (Keys)m.WParam.ToInt32() == Keys.Down) {
                bool repeat = (m.LParam.ToInt32() & (1 << 30)) != 0;
                bool down = m.Msg == 0x100;
                // But only for this form
                Form form = null;
                var ctl = Control.FromHandle(m.HWnd);
                if (ctl != null) form = ctl.FindForm();
                if (form == this) {
                    OnCursorDown(down, repeat & down);
                    return true;
                }
            }
            return false;
        }
        private void OnCursorDown(bool pressed, bool repeat) {
            // etc..
        }
    }
    

    【讨论】:

    • 不,不是导航键,它是 VB6 兼容属性。它也不会触发 KeyDown,与 ProcessCmdKey 不会触发的原因相同。
    • 好的,我测试了上面发布的代码,当我按下箭头时它会触发 WM_KEYDOWN,当我松开它时它会触发 WM_KEYUP。我有一个带有按钮和文本框的表单。焦点在按钮上。原谅我的无知......我错过了什么? (如果你能告诉我为什么它对我有用,我会撤回我的答案。)
    • 我喜欢你的回答...我只是不明白为什么我的例子对我有用,而你却说它不应该。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多