1

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.

Colour Palette

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:

With focus

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.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 1
    It doesn't work because PictureBox is not a control that can get the focus. The only controls you have that can get it are the buttons. You ought to fix that, it isn't difficult. Check [this post](http://stackoverflow.com/a/3562449/17034) for the approach. – Hans Passant Jun 13 '16 at 14:56
  • 1
    Your "OK" and "Cancel" buttons are backwards. They don't follow the standard Windows convention. (Yes, OS X does it differently. Yes, that may arguably be a better convention. But it is not the one that your users are accustomed to, so it is not the one you should be following.) – Cody Gray - on strike Jun 13 '16 at 15:02
  • @HansPassant Please see my updated question. Thanks. – Andrew Truckle Jun 13 '16 at 15:21
  • 1
    The designer hides the event in the Properties window. You can fix that easily as well with the [Browsable] attribute but it really isn't necessary, just subscribe it in the form constructor. – Hans Passant Jun 13 '16 at 15:28
  • I have done this and now it works. All I have to do is click the control first so that it has focus and then I can use the arrow keys. – Andrew Truckle Jun 13 '16 at 15:37
  • @HansPassant I have implemented it using your approach which differs somewhat from the accepted answer but the principles are the same. As long as your comment get retained with the link to your solution. Or, can that question be linked ot this one? – Andrew Truckle Jun 13 '16 at 15:46
  • @CodyGray Where is this Windows convention defined? – Andrew Truckle Jun 13 '16 at 16:04
  • 1
    [Command Buttons: Design Guidelines](https://msdn.microsoft.com/en-us/library/windows/desktop/bb246415(v=vs.85).aspx) *The button order is as follows, from left to right: Help (optional) / OK, or any command buttons that initiate action for the whole window / Cancel / Apply (optional). If a command button applies only to a particular control, group it with that control; do not place it in the button row.* – Reza Aghaei Jun 13 '16 at 21:08
  • 1
    [Dialog Boxes](https://msdn.microsoft.com/en-us/library/windows/desktop/dn742499(v=vs.85).aspx) *Present the commit buttons in the following order: OK/[Do it]/Yes, [Don't do it]/No, Cancel, Apply (if present), Help (if present)* – Reza Aghaei Jun 13 '16 at 21:29
  • Reza's given you a couple of good links. In particular, the second one, taken from the general list of [Guidelines for Windows desktop applications](https://msdn.microsoft.com/en-us/library/windows/desktop/dn688964.aspx). (The first is the same stuff, adapted for MMC. Perfectly good as a reference, though.) Besides, even if it wasn't explicitly documented, I did use the word "convention". The convention is quite well established by any dialog box generated by the system, or any message box that uses the standard MessageBox or TaskDialog API. Users expect this format because they're used to it. – Cody Gray - on strike Jun 14 '16 at 06:00

1 Answers1

2

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.

Community
  • 1
  • 1
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks. This does work, although then I lose the general control movement support. I changed it to Control+Arrow and that is OK. – Andrew Truckle Jun 13 '16 at 15:01
  • Thanks for your answer. There seems to be so many ways to do things in C#. At the moment I have done it using the inherited class as per Hans comments. I see that yours in somewhat simpler to implement. Is there a benefit of one approach over another? – Andrew Truckle Jun 13 '16 at 15:39
  • As mentioned in the answer, it's better to encapsulate such logic in the control if you want to create a reusable control. But if you don't want it to be reusable, you can simply apply both approaches which I shared. – Reza Aghaei Jun 13 '16 at 15:42
  • 1
    At least knowing the second approach as a *Hack* you can reuse that extension method to enable or disable some styles on controls. – Reza Aghaei Jun 13 '16 at 15:43
  • I see the benefits of it being a `Control` so that the event handlers were encapsulated within it. Maybe one day I will make that change. Thanks. – Andrew Truckle Jun 13 '16 at 15:45
  • Just FYI, you don't need to set KeyPreview to true if you're overriding ProcessCmdKey. That's the whole point of ProcessCmdKey. KeyPreview is an anachronism inherited from VB 6, it just causes the form's KeyDown event to be raised when a key down event occurs on a child control. Also, I'm curious why you chose not to follow [Hans's model](http://stackoverflow.com/a/3562449/366904) of subclassing the PictureBox control. Seems like that would be preferable to all of this use of Reflection. It would also promote encapsulation/separation of concerns (good OOP design). – Cody Gray - on strike Jun 14 '16 at 06:03
  • @CodyGray **•** About `ProcessCmdKey`; Thanks for the information and correction :) **•** About the Hans's model; In general I prefer it and voted for it. Also mentioned in the answer *But in general it's better to create a custom paint selectable control like [this](http://stackoverflow.com/a/3562449/17034) rather than putting such logic at form level. Your control should contain such logic.* – Reza Aghaei Jun 14 '16 at 12:53
  • @CodyGray But there is some useful information in the current answer too, The OP couldn't find a way to handle keyboard events of `PictureBox` and the answer shows a way to make `PictureBox` selectable and how to handle keyboard events of the control without inheritance. In some cases it's preferred by users to not introduce a new control for functionalities which are not such reusable. For such cases the answer shares an extension method which will be useful for such *hack*. As mentioned at the end of answer *At least knowing this approach...*. – Reza Aghaei Jun 14 '16 at 12:54
  • 1
    Fair enough; I had missed the italicized portion in your answer the first time I read it. I did not mean to imply that your answer was not useful, just to suggest that I would strongly prefer...well, doing exactly what you said is "in general...better". :-) I have indeed seen a number of people who are reluctant "not to introduce a new control," but I do not understand that. It seems to be either an irrational fear or something that comes from fundamentally misunderstanding the object model. You subclass to modify and extend behavior. Reusability is just an added bonus. – Cody Gray - on strike Jun 14 '16 at 13:17