1

I have need for regulating program flow depending on Arrow pressing when Button is Active Control. Like this:

Private Sub btn_OK_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles btn_OK.KeyDown

    If e.KeyCode = Keys.Up Then
        If mode = mymodes.first Then
            firstcontrol.Focus()
        Else
            secondcontrol.Focus()
        End If
    End If
End Sub

As I can see KeyDown event is not fired at all with pressing any arrow key. Program instead do some his internal functionality and moves a focus by using form's tab order (as it seem's). KeyPreview is set to true on actual form.

Is here any way to get wanted functionality with arrow keys and without subclassing a button and using ProcessCmdKey?

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
user1697111
  • 199
  • 4
  • 13

4 Answers4

5

Ok I did some research and effectively, the key_down event for the Arrow keys is captured before being processed...

Here is the source where it starts : http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,7765d3efe64d5539

How it works

When a key is pressed, here is the list of what happens inside the control :

The function PreProcessControlMessageInternal is called.

This function will raise the event PreviewKeyDown on the control. Then it will call PreProcessMessage.

The function PreProcessMessage is called.

This function actually checks if anybody wants to use the key that has been pressed. In our case, (WM_KEYDOWN) :

  • The Control first calls ProcessCmdKey : if anyone want to decide this is a command key, return True and use that key. nobody else will see that key has been down

  • Then the Control calls IsInputKey() : If anyone decides this is an input key (TextBoxes for example), return True and process your key.

  • Then it calls ProcessDialogKey() : [Litterally from ReferenceSource]

is called to check for dialog keys such as TAB, arrow keys, and mnemonics

What to do

In your case, you have three possibilities, the last being the best (and easiest) :

Process the message when ProcessDialogKey() receives it :

Protected Overrides Function ProcessDialogKey(keyData As Keys) As Boolean
    If keyData = Keys.Up Or keyData = Keys.Down Or keyData = Keys.Left Or keyData = Keys.Right Then
        'Do whatever you want with the key
        Return True 'So the processing will stop
    End If
    Return MyBase.ProcessDialogKey(keyData)
End Function

Prevent the handling of this key so you can handle it in Button_keyDown()

Protected Overrides Function ProcessDialogKey(keyData As Keys) As Boolean
    If keyData = Keys.Up Or keyData = Keys.Down Or keyData = Keys.Left Or keyData = Keys.Right Then
        Return False
    End If
    Return MyBase.ProcessDialogKey(keyData)
End Function


Private Sub btn_OK_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles btn_OK.KeyDown

    If e.KeyCode = Keys.Up Then
        If mode = mymodes.first Then
            firstcontrol.Focus()
        Else
            secondcontrol.Focus()
        End If
    End If
End Sub

The Best Way

So actually (and based on the comment of Zohar Peled), the best way is to handle the PreviewKeyDown event, so you don't have to override any other method :

Private Sub Form1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles MyBase.PreviewKeyDown
    'Do whatever here, all the keydown events will fall into this sub.
End Sub
Martin Verjans
  • 4,675
  • 1
  • 21
  • 48
  • Thanx Martin. I know how to solve this issue in subclass. I was just wondering if I can get that from highest level since all those events and keys are offered from IDE but obviously they are not functional. – user1697111 Mar 30 '16 at 10:00
  • @user1697111 Your form already is a derived class of system.winfows.forms.form, so you don't need any other derived classes. – Zohar Peled Mar 30 '16 at 10:16
  • Nice explanation. +1 for research. However you can achieve the same behavior by simply handle the `PreviewKeyDown` event instead of the `KeyDown` event so there is no need to override the ` ProcessDialogKey` method. – Zohar Peled Mar 30 '16 at 10:19
  • @ZoharPeled I edited my answer... You're right actually PreviewKeyDown is called before all this processing – Martin Verjans Mar 30 '16 at 10:51
  • OK, finally I used second solution for handling button's events. Thanks for wide explanation. – user1697111 Mar 31 '16 at 04:32
0

I noticed that KeyUp works with Arrow-Keys on a Button while KeyDown does not. Would that help with your issue?

Brandtware
  • 451
  • 4
  • 17
  • Would you belive or not, On my system KeyUp is fired when button gets a focus?? Put that under handler and see: Debug.Print("KeyUp " & Me.ActiveControl.Name) Debug.Print("KeyUp " & e.KeyCode.ToString). – user1697111 Mar 30 '16 at 08:58
  • Your "If e.KeyCode = Keys.Up" should prevent false positives when the button gets the focus otherwise. So... basically my answer is working, isn´t it? If you have multiple Buttons you could Point them to the same event-handler if neccessary... – Brandtware Mar 30 '16 at 09:20
  • No, KeyUp also don't work as expected when button is focused. – user1697111 Mar 30 '16 at 09:35
  • Interesting, in my test-app the keyup-Event fires only if no textboxes are present on the form, even if i tell their KeyUp-Events to set e.Handled and e.SuppressKeyPress to false o_O – Brandtware Mar 30 '16 at 09:50
0

This code will only work if the focus is on the button.

Place this code at form level, so all controls that have the focus will pass up that event...

Private Sub myForm_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown

  If e.KeyCode = Keys.Up Then
      If mode = mymodes.first Then
          firstcontrol.Focus()
      Else
          secondcontrol.Focus()
      End If
  End If
End Sub
Martin Verjans
  • 4,675
  • 1
  • 21
  • 48
  • Did you try it? because that my initial thought was something like this but it turns out that the arrow keys does not raise the key_down event at all. – Zohar Peled Mar 30 '16 at 08:56
  • That (of course) don't work no matter on Me.KeyPreview setting. Keep in mind, we talk about the Button control. – user1697111 Mar 30 '16 at 09:02
  • @ZoharPeled You're right I just tried it it arrow keys only raise KeyUp events... I'm checking inside referencesource... – Martin Verjans Mar 30 '16 at 09:21
0

Based on the answers to this question I came up with this (tested and working on my computer)

Private Sub Form1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles MyBase.PreviewKeyDown
    If Me.Button1.Focused Then
        If e.KeyCode = Keys.Up Then
            If mode = mymodes.first Then
                firstcontrol.Focus()
            Else
                secondcontrol.Focus()
            End If
        End If
    End If
End Sub

Turns out that arrow keys are not handled by key_down event by default, but they are handled by the PreviewKeyDown event.

Community
  • 1
  • 1
Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • That also won't work (event is not fired at all). Why dont'you try to setup a little form with few controls on it and try its work before posting an answer? Also 'this question' uses subclassing. – user1697111 Mar 30 '16 at 09:14
  • Note: `(tested and working on my computer)`. Also note I'm handling the `PreviewKeyDown` event and not `KeyDown` event. Also, `this question` have more then one answer. [this answer](http://stackoverflow.com/a/14500362/3094533) doesn't use any sub classes. – Zohar Peled Mar 30 '16 at 09:38