A previous function generated a keycode to “bind” a macro or Word command to a keyboard shortcut. When including a few error checks as part of that process, we need to validate whether a given key should be allowed for a shortcut when used without any modifier keys like Control or Option (Alt on Windows). The function doubles for validating keyboard shortcuts accessible when only a Shift modifier key is used since Shift has a special meaning for most keys on a keyboard.
Thanks for your interest
This content is part of a paid plan.
Key check for isolated key bindings (clumsy)
In retrospect, this is a clumsy way to implement the function, but I left it as an example of what happens when we don't think enough about our function. It also clearly illustrates why a Select Case statement was invented. See the improved version where we also add a function to validate any potential main key for a key binding assignment.
When automatically adding or updating keyboard shortcuts, we would probably not want someone to override the Return key by itself without a good reason. Similarly, the Shift key has a well-established action with most keys on a keyboard, so we also probably wouldn’t want someone to override something like Shift+A. Unfortunately, both are possible, and accidents happen especially if you have dozens of macros.
As one of several keybinding error checks implemented in a separate article, we’re testing whether a given key can be used as a shortcut assignment without a modifier key or when combined just with the Shift key. Unfortunately, the relevant special key values are scattered throughout the WdKey enumeration, and we even need to access another keycode constants table to cover the arrow keys. While the steps are straight forward, they stretch out enough to make the sequence of checks messy, so it is convenient to implement a separate function to tell us yay or nay to the question.
This article is public but hidden and part of a series of mostly member articles. It is an early version of the key check function below:
- Simpler approach to get a keycode for any key combination (lacks error checks)
- More concise but programmy version to get a keycode for any key combination (includes error checks)
- A function that validates whether isolated keys are allowed for a tentative keyboard shortcut
- Another macro assembles the pieces to automatically assign or reset keyboard shortcuts for macros or Word commands
The function below performs a niche but necessary task for validating potential keybinding assignments.
Isolated keybinding keys
We’re considering whether keyboard shortcuts automatically assigned to a single key such as F3 are valid for a keybinding assignment. If not, we need to notify the user or at least skip the erroneous shortcut assignment.
We can assign key bindings without modifier keys to many keys, but caution should be exercised since most would be problematic to override. Some useful special keys to consider include Page Up/Down, arrow keys, Insert, Enter (Return), Delete/Backspace, Home, End, and a few others.
Since the Shift key has a well-established action with most keys on a keyboard, we can also apply the function to validate any keyboard potential shortcuts that use the Shift key as a lone modifier key. For example, we might wish to tweak a macro that overrides Shift+Return. The keybinding restrictions aren’t exactly the same between the two cases, but this function provides an example to adapt for any special cases you may create.
We will refer constant names rather than specific numbers since the constant names are clearer, and they safeguard our function against some changes to the constants tables or differences between Word for Windows or Mac (modifier keys have different values, for example).
Function skeleton
The function skeleton is:
The function is similar enough between Word for Windows or Mac that we only need minimal changes for either operating system.
Reasons for creating functions
Some functions are created because they are useful across various macros where they simplify creating more complex macros. Others like this one just take a group of messy steps and encapsulate them to make another macro or function easier to read, but they may or may not find use elsewhere. Although, we occasionally create a tool only to later realize it is also useful elsewhere.
Parameters
The only parameter required is a key constant value identifying which key is being checked. We’ll just call the variable Key with a capital “K” to indicate the value does not change within the function. I would prefer to require the key constant to be a WdKey type from the WdKey enumeration table since restricting possible values to those in the table allows fewer chances for any problems later, but we also need to check the arrow keys which do not appear in that table.
Ughhh.
Thus, we’re stuck with using a Long number type (basically a big Integer in VBA) for the key value type. This is not a problem in this function since VBA automatically converts enumeration constants to Long values without any issues when testing against the respective key constants.
Remember an enumeration is basically a list of easy-to-read, pre-defined constant values, but they do possess more utility than just better names.
Return value
The function returns True (As Boolean) if the corresponding key can be used without other modifier keys and False if not.
Default return value
To simplify the check, it’s easiest if we assume a False (no match) result and just change it to True if we find a matching constant. We set the default return value via the function name.
Exit function immediately on a match
As soon as we find a match, the Key value cannot match another constant, so we immediately set the return value.
True indicates the function found a valid Key constant. Then we exit the function.
Check Insert key
Some relevant keys are “on their own” in the WdKey table, so we just manually check each one. Insert is one such key value with a constant name wdKeyInsert. Our condition is:
An equals = sign in VBA is unfortunately contextual. It acts like an assignment when used alone, but in a conditional statement, it becomes a True or False (Boolean) value.
Use this for any individual key, but it is convenient to save a few steps and combine some keys into one conditional statement.
Delete and Backspace keys
Windows systems use both Backspace and Delete keys, and the respective constants in the WdKey table are wdKeyBackspace and wdKeyDelete.
Delete on a Mac
The Delete key on a Mac acts like Backspace in Windows, and Fn+Delete acts like forward Delete. While the forward Delete key exists and works for standard actions at the system or Word level, we cannot bind a keyboard shortcut in Word to any key combination using either the Fn (Globe) or the Delete key. That’s a shame because it is convenient in Word for Windows to mimic Control+Backspace (delete previous word) and Control+Delete (delete next word) to work similarly for sentences or heading content.
For example, I assign Alt+Backspace and Alt+Delete to two macros that quickly delete to the beginning or end of a sentence. The similar key patterns make them easy to remember, and I use them often when writing. I can’t emulate the forward Delete key combination with something like Fn+Option+Delete on a Mac, so I need to use a key combination like Control+Option+Delete on a Mac. It’s not the end of the world, but the asymmetry in the keyboard shortcuts compared to the actions is disappointing and a little awkward.
The name confusion between the deletion keys is unfortunate, but it is a historical fluke where Macs only included a single “delete” key on their laptops keyboards which deleted backward in the document. Apple called the key “Delete” (I wish they had called it something like “Back” instead) whereas Windows systems included both keys and called them Backspace and Delete, respectively. Since the Backspace key action is clear by the name, Delete deleted forward in the document on Windows systems.
Catching the Delete key on a Mac
On a Mac, the Delete key corresponds to wdKeyBackspace in the WdKey enumeration since that is the corresponding action. While the forward Delete key works for standard tasks, the constant wdKeyDelete does not exist in Word for Mac, and it will cause an error if it is used. As a result, we cannot bind any keyboard shortcuts to it in Word for Mac VBA. Word won’t even recognize the forward Delete key as a valid main key shortcut in the customize keyboard options on Windows or Mac systems.
Arghhh.
I use both Backspace and Delete (forward Delete on a Mac) in Word for Windows keyboard shortcuts, so I need this function to differentiate between the two keys on a Mac and let me know the forward Delete key is invalid for any keybinding assignments. It’s inconvenient to create two whole functions to accommodate such a small difference, but fortunately, we’ve covered a solution before in a simple function that gave us the correct double quote characters in Word for Windows or Mac.
Windows
We need to check both Backspace or Delete separately since they are not consecutive constants in the WdKey table. Since either key is valid in Windows, we form a compound condition using the Or (Boolean) operator.
Remember VBA decides whether an equals = sign is an assignment (as in x = 3) or a condition (with a True or False result) based on the usage context.
The If statement is:
We have some repeated steps compared to the last key check, but this is addressed in the final comments at the bottom.
Mac
In Word for Mac, the “Delete” key has the constant value wdKeyBackspace since that is the corresponding action. Word for Mac only supports backward Delete (Backspace in Windows) in VBA, and when I say “only” it will actually cause an error as an unrecognized constant, so we only want to check wdKeyBackspace.
Detecting Mac or Windows
If you only use Windows or Mac exclusively, then just include the respective key validation steps, but if you sometimes bounce between them like I do, it’s nice to let the function switch between the different systems automatically.
Use a preprocessor directive
VBA includes an If-Else statement that is processed before the macro runs in Word. The fancy name is a preprocessor directive, but it just gives us a way to run different steps in Mac or Windows (additional options exist). The directive emulates a regular VBA If-Else statement just with a hashtag in front of the different parts of the command, so it looks like:
It adds a little complexity, but if you use both Windows and Mac, it’s nice when we don’t need two versions of the same function where most steps are exactly the same.
Checking a number within a numeric range
If we want to check whether a number is within a range of values, say SmallNumber (smallest) up to BigNumber (largest), we first check whether our Key argument value is greater than or equal to the SmallNumber.
In VBA, the symbol >= is a “relational operator” that compares whether the number on the left is greater than or equal to the number is on the right. The result is a True or False (Boolean) value.
The Key value must also be less than or equal to BigNumber.
Putting them together into a compound condition, both statements must be true for the Key value to lie between SmallNumber and BigNumber. This corresponds to an And operator.
Putting this into a conditional statement, we have:
Key constant ranges
We have a list of constants to check against, but some can be tested as contiguous ranges of constant values reducing the number of individual checks we need to perform. Each range or set of constants is mutually exclusive since key values (mostly) do not overlap within the same table.
Function keys
In the WdKey enumeration, function keys F1 through F16 are defined between wdKeyF1 and wdKeyF16 in a contiguous range with no other key constants in between them.
Why allow function keys above F12?
Most modern keyboards stop function keys at F12, so why allow any function keys above that?
Actually, this turns out to be nice when assigning nonsense key combinations to trigger a macro from other devices via a keyboard shortcut. For example, if you’re using an Elgato Stream Deck (not an affiliate), you can bind a Word macro to something like Shift+F15 using a button. Another option is macro keys on a fancy mouse or some keyboards. For example, Logitech often includes “G-keys” which can be programmed to run various key combinations.
Some higher function number keys are bound to things like media keys, so not all key combinations work, but it’s a nice trick when it you need it.
Some movement keys
Several other key values happen to lie in a contiguous range: Page Up, Page Down, End, and Home. The sequence starts with wdKeyPageUp and ends with wdKeyHome. Given this range, we check:
Arrow keys
Unfortunately, the arrow keys do not appear in the standard WdKey table. The reason for the omission is not clear since arrow keys are present on all keyboards. We need a different keycode table to access constants for vbKeyLeft, vbKeyUp, vbKeyRight, and vbKeyDown corresponding to the obvious respective arrows keys.
Fortunately, they form a contiguous sequence starting from vbKeyLeft increasing in value to vbKeyDown. Other than referring to constants from a different VBA table, the form of the conditional statement is the same.
Return result
Since we’ve already assigned a True or False value when the individual checks were made, there is nothing extra to do.
Valid isolated keys function
Our function to test whether a given Key is one of the allowed isolated key combinations is:
See why we separated this menial task off into another separate function from our GetKeyCode function? With all the constants scattered through two different tables, it would unnecessarily clutter the error checking steps.
If you only use Mac or Windows exclusively, just eliminate the unused steps and remove the preprocessor directive lines preceded by a hashtag “#”.
Of course, add any other key checks you want to allow, but use caution for common keys.
Repeated assignments
The repeated True assignments followed by exiting the function is annoying. We could flip the default value to True and set a False result only if we reach the end of the function. With this setup, we immediately exit the function when we find a match which simplifies the actions after the various tests.
However, defaulting to an accepted True result just bothers me. It is much more consistent with the concept of the function to default to an invalid key with a False result. With that in mind, using repetitive assignments is a slightly better, even if annoying, approach.
One could instead omit all the Exit Function commands since the function ends anyhow, but then all the remaining key checks are done which is inefficient.