The right way to check for key state in UWP apps

WinUI

6 years ago

UWP apps should work great with any kind of input including the keyboard. That includes support for keyboard shortcuts and checking for the state the keys are in. It turns out however that there are some things we have to watch out for.

Problem

Let's suppose we want to write a KeyDown event handler and check if the Ctrl key is pressed. The simple approach would look like this:

if (Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) == 
    CoreVirtualKeyStates.Down)
{
    //do something
}

This looks perfectly valid, but there is a hidden caveat. Once we run the app and test it, we will find out that the code inside the if block is run only once for each two presses. Why is that?

Locked key state

If we use the debugger, we can see that when pressed, the Ctrl key state toggles between values Down and Down|Locked. This means the CoreVirtualKeyStates enum has a [Flags] attribute:

[ContractVersion(typeof(UniversalApiContract), 65536)]
[Flags]
[WebHostHidden]
public enum CoreVirtualKeyStates : uint
{
   None = 0,
   Down = 1,
   Locked = 2
}

The key can be Down and Locked at once! But what does Locked mean? The documentation says that Locked means:

The key is in a toggled or modified state (for example, Caps Lock) for the input event.

This definitely makes sense for Caps Lock and Num Lock keys, but the documentation further says:

All keys support the Locked state (not just the standard Caps Lock and Num Lock keys).

This is very interesting! It means that every single key on the keyboard acts as if it can be toggled, including the classic keys like simple letters! You can even see that even when the key is released, it stays in the Locked state until it is pressed again. I have also confirmed that the "locked" state is system-wide, although non-function keys like letters it is not reflected immediately after the app is launched and before that the GetKeyState method returns simply None.

The right way to check

To make sure the key is really pressed down, you can use the enum's HasFlag method:

if (Window.Current.CoreWindow.GetKeyState(VirtualKey.Control).
     HasFlag(CoreVirtualKeyStates.Down))
{
    //Ctrl is pressed
}

Alternatively, you can also utilize the bitwise AND operator:

if ((Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) &
    Windows.UI.Core.CoreVirtualKeyStates.Down)
    == CoreVirtualKeyStates.Down)
{ 
    //Ctrl is pressed
}

Both ways will yield the expected results. Also interestingly, to check if the key is not pressed we should use the following (note the ! operator before the condition):

if (!Window.Current.CoreWindow.GetKeyState(VirtualKey.Control).
     HasFlag(CoreVirtualKeyStates.Down))
{
    //Ctrl is not pressed
}

We cannot use == CoreVirtualKeyStates.None because as mentioned above, key could be released, but still in the Locked state.

Source code

Source code sample that shows how key states are toggled is available here on my GitHub.

Summary

UWP has small intricacies we should be aware of to reliably check for pressed down keys. The key (pun intended :-) ) takeaway is that keys can be in two states at once so we cannot rely on simple equality checking and should rather use the HasFlag method appropriately.