KeyDown event is not firing, KeyPreview set to tru

2020-06-25 01:54发布

I'm building a small Forms application, i've just started it. But i have this problem: if i put a Control to the form, the KeyDown event is not firing. I'm aware of the KeyPreview property, and set it to true. But that didn't helped... :( I also tried to set the focus to the main form, no success either.

Any thoughts?

Edit:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        KeyDown += new KeyEventHandler(Form1_KeyDown);
        this.KeyPreview = true;
    }

    void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.KeyCode)
        {
            case Keys.Left: MessageBox.Show("Left");
                break;
            case Keys.Right: MessageBox.Show("Right");
                break;
        }
    }
}

4条回答
Luminary・发光体
2楼-- · 2020-06-25 02:01

The reason of the behaviour you observe is that special keys like TAB, UP/DOWN/LEFT/RIGHT ARROW, PAGE UP/DOWN, HOME, END, etc. are often considered "Input Keys" by common controls.

For example, ARROW keys are considered "Input Keys" by the TabControl as these keys allows you to change the selected TabPage. A similar behaviour is present with a multiline TextBox where the ARROWS keys allows you to move the text cursor.

I assume that the Rumba Mainframe control you have does the same thing for the same reasons. You can try overriding it and changing the implementation of the IsInputKey method or handling the PreviewKeyDown event and setting the IsInputKey property to true.

Please see the documentation of the Control.IsInputKey Method and Control.PreviewKeyDown Event for further details

查看更多
聊天终结者
3楼-- · 2020-06-25 02:04

If you were on WPF, you could easily catch the required events, because WPF uses routed event system to dispatch events. In winforms, I recomment one of these two ways:

1. Using Application.AddMessageFilter Method:

Define a Message Filter class:

public class KeyMessageFilter : IMessageFilter
{
    private enum KeyMessages
    {
        WM_KEYFIRST = 0x100,
        WM_KEYDOWN = 0x100,
        WM_KEYUP = 0x101,
        WM_CHAR = 0x102,
        WM_SYSKEYDOWN = 0x0104,
        WM_SYSKEYUP = 0x0105,
        WM_SYSCHAR = 0x0106,
    }

    [DllImport("user32.dll")]
    private static extern IntPtr GetParent(IntPtr hwnd);

    // We check the events agains this control to only handle
    // key event that happend inside this control.
    Control _control;

    public KeyMessageFilter()
    { }

    public KeyMessageFilter(Control c)
    {
        _control = c;
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == (int)KeyMessages.WM_KEYDOWN)
        {
            if (_control != null)
            {
                IntPtr hwnd = m.HWnd;
                IntPtr handle = _control.Handle;
                while (hwnd != IntPtr.Zero && handle != hwnd)
                {
                    hwnd = GetParent(hwnd);
                }
                if (hwnd == IntPtr.Zero) // Didn't found the window. We are not interested in the event.
                    return false;
            }
            Keys key = (Keys)m.WParam;
            switch (key)
            {
                case Keys.Left:
                    MessageBox.Show("Left");
                    return true;
                case Keys.Right:
                    MessageBox.Show("Right");
                    return true;
            }
        }
        return false;
    }
}

So you have a class that every message in Windows Forms passes through it. You can do whatever you want with the event. If PreFilterMessage method returns true, it means that the event should not be dispatched to it's respcetive control.

(Note that the values in the Keys enumeration is almost idential to virtual key codes)

Before this works, you have to add it to the application's message filters:

public partial class Form1 : Form
{
    // We need an instance of the filter class
    KeyMessageFilter filter;

    public Form1()
    {
        InitializeComponent();

        filter = new KeyMessageFilter(panel1);
        // add the filter
        Application.AddMessageFilter(filter);
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);

        // remove the filter
        Application.RemoveMessageFilter(filter);
    }
}

The filter is only active in the lifetime of the Form1.

Notice: This will catch events in any form! If you want it to work for only one form, pass the form to the filter class, and compare its Handle property with m.HWnd in PreFilterMessage

2. Using Windows Hooks:

This is a more advanced and complicated (and low level) approach. And it requires more code. I've wrote a HookManager class that makes the process very simple. I'm gonna publish the class to github and write an article about it.

查看更多
啃猪蹄的小仙女
4楼-- · 2020-06-25 02:05

The arrow keys are one kind of special key that are automatically handled by Controls. So if you want to make them raise the KeyDown event you could:

1) Override the isInputKey method in every control of your form

OR

2) Handle the PreviewKeyDown event and set the IsInputKey property to true

More info can be found here.

I know WonderCsabo solved his problem already but someone else put a bounty on it because is having the same problem and no answer was selected. WonderCsabo please post your solution as answer as well.

查看更多
兄弟一词,经得起流年.
5楼-- · 2020-06-25 02:20

I already commented my solution, but I also post it as an answer, so it can be easily found.

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    switch (keyData)
    {
        case Keys.Left:
            // left arrow key pressed
            return true;
        case Keys.Right:
            // right arrow key pressed
            return true;
        case Keys.Up:
            // up arrow key pressed
            return true;
        case Keys.Down:
            // down arrow key pressed
            return true;
    }

    return base.ProcessCmdKey(ref msg, keyData);
}
查看更多
登录 后发表回答