Saturday, January 20, 2007

Intercepting the Arrow Keys in a Windows Forms Application

Question
I have a Windows Form populated with a few controls. I want to let the user use the arrow keys to control some of the actions on the window. However, it seems like every time the user presses the arrow key, the controls (such as Button control) on the form receives the event and acts on it instead. How do I disable this behavior?

Answer
By default, special keys (such as Left, Right, Up, Down, etc) are filtered for focus management. That is to say, when you press the one of the arrow keys, Windows will automatically choose the next control on your form and set the focus on it. However, you can override this behavior by writing some code.

If your form has got no controls on it (or has controls that does not handle any keyboard inputs (such as the Panel control)), the easiest way would be to handle the KeyDown event of the form, like the following:

Private Sub Form1_KeyDown( _
   ByVal sender As Object, _
   ByVal e As System.Windows.Forms.KeyEventArgs) _
   Handles Me.KeyDown
        If e.KeyData = Keys.Up Or _
           e.KeyData = Keys.Down Or _
           e.KeyData = Keys.Left Or _
           e.KeyData = Keys.Right Then
            '---perform your own action---
            e.Handled = True
        End If
    End Sub

However, if the form contains controls that handles keyboard input (such as the TextBox and Button controls), then things get a little tricky.

Assuming you have some Button controls on your form and you want to disable the arrow keys having an effect on them. You need to do the following:

  1. Extend the Button control and override the IsInputKey function:

Public Class ExtendedButton
    Inherits Button
    Public Event ArrowClicked(ByVal key As Keys)

Protected Overrides Function IsInputKey( _
   ByVal key As Keys) As Boolean
        Select Case (key)
            Case Keys.Up, Keys.Down, _
               Keys.Left, Keys.Right
                RaiseEvent ArrowClicked(key)
                Return True
        End Select
        Return MyBase.IsInputKey(key)
    End Function
End Class

  1. Programmatically add the extended Button control to your form:

Private Sub Form1_Load( _
   ByVal sender As System.Object, _
   ByVal e As System.EventArgs) _
   Handles MyBase.Load

        Dim b1 As New ExtendedButton
        With b1
            .Text = "Button 1"
            .Location = New Point(10, 20)
            AddHandler .ArrowClicked, _
               AddressOf ArrowClicked
        End With
        Me.Controls.Add(b1)

        Dim b2 As New ExtendedButton
        With b2
            .Text = "Button 2"
            .Location = New Point(100, 20)
            AddHandler .ArrowClicked, _
               AddressOf ArrowClicked
            Me.Controls.Add(b2)
        End With
    End Sub

  1. Define the ArrowClicked event handler:

Public Sub ArrowClicked( _
   ByVal key As Windows.Forms.Keys)
        '---insert code here to do what
        ' you are supposed to do---
        Console.WriteLine("key pressed " & _
           key.ToString)
    End Sub

That’s it! When the user now presses one of the arrow keys, the ArrowClicked event will be fired. You can insert the code to do whatever you are supposed to do here, such as animating some actions on your form, etc.

1 comment:

Chris Wooldridge said...

You can additionally hook the PreviewKeyDown Event of the control and change the value of PreviewKeyDownEventArgs.IsInputKey to true.