Welcome to MSDN Blogs Sign in | Join | Help

jfo's coding

adventures in windows forms and wpf
A new way of saying IsInputKey: PreviewKeyDown

A year ago, I diagramed the maze that is Windows Forms keyboard handling 

There are some new helper methods in 2.0 that I havent gotten a chance to mention, which I thought might be interesting.  This first is PreviewKeyDown.

If you've struggled before with having to override a class in order to set IsInputKey=true so you can get arrow keys for your control, you can now also use the PreviewKeyDown event. 

The Button Sample

Lets take the example where you're building a button that shows a menu when the down or up arrow is pressed.  Typically the button will not get these keys as they're "navigational", and the button doesn't get called back on KeyDown.  By handling the PreviewKeyDown event and setting IsInputKey = true, the button will now recieve the Down and Up arrows.  Note that these two keys will no longer move focus to the next control as they're no longer "navigational."

namespace WindowsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
            // this form just has one button on it.
            button1.PreviewKeyDown +=new PreviewKeyDownEventHandler(button1_PreviewKeyDown);
            button1.KeyDown += new KeyEventHandler(button1_KeyDown);
            button1.ContextMenuStrip = new ContextMenuStrip();
            button1.ContextMenuStrip.Items.Add("One");
            button1.ContextMenuStrip.Items.Add("Two");
            button1.ContextMenuStrip.Items.Add("Three");
        }
        // Because the arrow keys are normally not input keys to
        // a button, (they normally change selection to the adjacent control)
        // KeyDown is not typically fired for a down arrow.  In
        // 1.1 of the framework you could override the Button and use IsInputKey
        // in 2.0 you can additionally respond to the PreviewKeyDown event.
        void button1_KeyDown(object sender, KeyEventArgs e) {
            switch (e.KeyCode) {
                case Keys.Down:
                case Keys.Up:
                    if (button1.ContextMenuStrip != null) {
                        button1.ContextMenuStrip.Show(button1, new Point(0, button1.Height), ToolStripDropDownDirection.BelowRight);
                    }
                    break;
            }
        }
       
        // PreviewKeyDown is where you preview the key to see if you want it
        // do not put any logic here other than to say you want the key
        // instead use the KeyDown event after setting IsInputKey to true
        private void button1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
            switch (e.KeyCode) {
                case Keys.Down:
                case Keys.Up:
                    e.IsInputKey = true;
                    break;
            }
        }
    }
}

Hmm, but why doesn't this work for panel?

There's really two pieces to the puzzle: whether a control can accept certain keys, and whether or not a control can accept focus.  A Panel, because it derives from ScrollableControl is not focusable by default. This behavior is controlled by the flag ControlStyles.Selectable - and you need to additionally call SetStyle(ControlStyles.Selectable, true).  Since this API is protected, you will need to inherit. Finally, to say that the Panel participates in the tab order, you need to set TabStop = true. 

public class MyPanelThatAcceptsArrowKeys : Panel {
            public MyPanelThatAcceptsArrowKeys() {
                // specifies whether or not it can get focus
                SetStyle(ControlStyles.Selectable, true);
                // specifies whether it should participate in tab order
                this.TabStop = true;

                ChangeBackColor();
            }
            protected override bool IsInputKey(Keys keyData) {
                switch (keyData) {
                    case Keys.Up:
                    case Keys.Down:
                        return true;
                }
                return base.IsInputKey(keyData);
            }
            protected override void OnKeyDown(KeyEventArgs e) {
                switch (e.KeyCode) {
                    case Keys.Up:
                    case Keys.Down:
                        ChangeBackColor();
                        break;
                }
                base.OnKeyDown(e);
            }
            private void ChangeBackColor() {
                Random rand = new Random();
                this.BackColor = Color.FromArgb(rand.Next(0, 255), rand.Next(0, 255), rand.Next(0, 255));
            }
     }

 

Posted: Thursday, January 26, 2006 10:48 PM by jfoscoding
Filed under:

Comments

Sheva said:

Nice, then how to capture the event involving both keyboard event and mouse button event, say how to capture ctrl + left button key press?

Sheva
# January 27, 2006 12:56 AM

jfoscoding said:

That particular one you would check Control.ModifierKeys & Keys.Control == Keys.Control in your mouse down event. The mouse event args tells you what button was pressed via the MouseButton property.

In VisualBasic you can also use the My.Computer.Keyboard.ControlKeyDown
http://msdn2.microsoft.com/en-us/library/ms172976.aspx

If you want a different key than Control, Alt, Shift, then you'll need to use GetKeyState directly:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/getkeystate.asp


# January 27, 2006 11:19 AM

Sheva said:

Thanks a lot, Jessica:)

Sheva
# January 27, 2006 11:55 AM
New Comments to this post are disabled
Page view tracker