KeyCode and Shift Arguments

The KeyDown and KeyUp events include KeyCode and Shift arguments to identify which keys the user pressed. Let's explore those arguments in more detail.

KeyCode and Shift Arguments

In yesterday's article, I wrote about how to add an event handler to the KeyDown event of a text box control.  As a reminder, our goal was to override the default behavior of the up and down arrow keys on a continuous Access form.  

By default, the up and down arrow keys move to the previous or next form control.  On a continuous form laid out in a grid format, the more intuitive behavior would be for the up and down arrow keys to move between records.

Using the up arrow moves left to the previous control instead of up to the previous record.

The Starting Point

At the end of yesterday's article, we had gotten to the point where Access had auto-generated the following code:

Private Sub tbDiscount_KeyDown(KeyCode As Integer, Shift As Integer)

End Sub

The tbDiscount_KeyDown routine is the event handler for the tbDiscount text box's Key Down event.  Whenever the user presses any key on the keyboard while the tbDiscount text box has the focus, this routine will execute.

KeyDown event arguments

Let's start by discussing the event arguments, KeyCode and Shift.

KeyCode

The KeyCode argument corresponds to the key on the keyboard that the user pressed.  The argument is an integer, but to make your code more readable you'll want to use the vbKeyCode constants, such as vbKeyA, vbKeyReturn, or vbKeyNumpad3.

Perhaps the most important thing to note about the KeyCode argument is that it is passed by reference.  This means that we can assign a different value to the KeyCode variable within the routine and it will be as if we pressed that other key instead.  

For example, if we really wanted to mess with someone, we could do something like this:

Private Sub tbKeySwitcher_KeyDown(KeyCode As Integer, Shift As Integer)
    Select Case KeyCode
    Case vbKeyW: KeyCode = vbKeyQ
    Case vbKeyQ: KeyCode = vbKeyW
    End Select
End Sub
What a great way to mess with people.

While this can be a fun prank, it's not particularly useful.  The main reason you would assign a value to the KeyCode argument is if you wanted to block the default behavior of the key.  To do this, you would set KeyCode to zero.  I often use the following technique to disable the default behavior of a key press, add special handling for certain keys, then restore the default behavior for all other keys.  

Private Sub tbSample_KeyDown(KeyCode As Integer, Shift As Integer)
    Dim SaveKeyCode As Integer
    SaveKeyCode = KeyCode
    KeyCode = 0
    Select Case SaveKeyCode
    Case vbKeyUp
        'Do something
    Case vbKeyDown
        'Do something else
    Case vbKeyLeft
        'Do another thing
    Case vbKeyRight
        'Do yet some other thing
    Case Else
        'Restore default behavior to all other keys
        KeyCode = SaveKeyCode
    End Select
End Sub

Shift

The Shift argument contains information about the so-called modifier keys, namely [Shift], [Ctrl], and [Alt].  The argument is a bit mask.  These are the values that comprise the mask:

  • 1: [Shift] acShiftMask
  • 2: [Ctrl] acCtrlMask
  • 4: [Alt] acAltMask

Since the bit mask represents three boolean values, there are eight possible values for the Shift argument:

  • 0: {no modifier keys in use}
  • 1: [Shift]
  • 2: [Ctrl]
  • 3: [Shift] + [Ctrl]
  • 4: [Alt]
  • 5: [Shift] + [Alt]
  • 6: [Ctrl] + [Alt]
  • 7:  [Shift] + [Ctrl] + [Alt]

The best way to check if any particular modifier key is in use is by performing a bitwise AND operation.  For example:

Sub TestShiftBitMask()
    Dim Shift As Integer
    For Shift = 0 To 7
        Debug.Print vbNewLine; "Shift code:"; Shift;
        If Shift And acShiftMask Then Debug.Print " [Shift] ";
        If Shift And acCtrlMask Then Debug.Print " [Ctrl]  ";
        If Shift And acAltMask Then Debug.Print " [Alt]   ";
    Next Shift
End Sub
Bit masks are a thing of binary beauty.

Generated Events

Each pressed key generates a separate event.  For example, if I were to press [Ctrl] + [Alt] + [Shift] + [P], that would generate four separate events:

Private Sub tbMyText_KeyDown(KeyCode As Integer, Shift As Integer)
    Debug.Print "KeyCode: "; KeyCode, "Shift: "; Shift
End Sub
The four events generated by a [Ctrl] + [Alt] + [Shift] + [P].

Image by PaulNI from Pixabay

Updated [2021-05-01]: Add paragraph about setting KeyCode argument to zero along with accompanying sample routine, tbSample_KeyDown.