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.