Handling KeyDown event in a Form

2019-07-31 14:00发布

问题:

I have just found out that we can't use the KeyDown event directly with a PictureBox. So I have to change my strategy.

I decided to add the Keydown event to the actual form:

private void FullColourPaletteForm_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.KeyCode)
    {
        case Keys.Left:
            {
                MessageBox.Show("Left");
                e.Handled = true;
                return;
            }
    }

}

Doesn't get executed. I see no message box when I press the left allow. Instead (and rightly so) it just moves the cusor from control to control.

I was hoping to be able to mimic some kind of cursor support for the block of colour by intercepting the arrow keys inside the picture box.

I am not sure of the best way forward. I don't want to break the standard dialogue functionality of moving between controls, but I want to now include suipport for detectign keys so I can add my code to move my block of colour.

Can it be done? Not sure why my event is not getting triggered in the form anyway.

I saw this question. So I tried setting my form KeyPreview property. No joy. I also looked at ProcessCmdKey but it doesn't seem right for the issue in hand.

Update:

If I try to follow the idea in the comments and create a SelectablePictureBox control, it looks like this:

I have two issues. 1. I still can't seem to work out how to handle the keydown event on my pictureBox object itself. I am reluctant to manually add any handlers to the designer file incase my changes get lost.

Also, when doing general control nagivation on the form with cursor keys it does not seem to know about this control.

回答1:

If you want to handle arrow keys at form level, you can override the form's ProcessCmdKey function this way:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == Keys.Left)
    {
        MessageBox.Show("Left");
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

But in general it's better to create a custom paint selectable control like this rather than putting such logic at form level. Your control should contain such logic.


Note

OP: I have just found out that we can't use the KeyDown event directly with a PictureBox

As mentioned by Hans in comments, the PictureBox control is not selectable and can not be focused by default and you can not handle keyboard events for the control.

But you can force it to be selectable and support keyboard events this way:

using System;
using System.Reflection;
using System.Windows.Forms;
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        this.pictureBox1.SetStyle(ControlStyles.Selectable, true);
        this.pictureBox1.SetStyle(ControlStyles.UserMouse, true);
        this.pictureBox1.PreviewKeyDown +=
            new PreviewKeyDownEventHandler(pictureBox1_PreviewKeyDown);
    }
    void pictureBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyData == Keys.Left)
            MessageBox.Show("Left");
    }
}
public static class Extensions
{
    public static void SetStyle(this Control control, ControlStyles flags, bool value)
    {
        Type type = control.GetType();
        BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
        MethodInfo method = type.GetMethod("SetStyle", bindingFlags);
        if (method != null)
        {
            object[] param = { flags, value };
            method.Invoke(control, param);
        }
    }
}

At least knowing this approach as a hack you can reuse the extension method to enable or disable some styles on controls in future.