How to achieve global keyboard shortcuts in UWP apps

We know there is a KeyDown event which will be fired while pressing the specified key when a UIElement is focused. However, in some case, you would like to fire the KeyDown event whatever UIElement is focused.

Let's recall how we register a KeyDown event handler to a UIElement:

  • Assume that a TextBox is named as InputBox.

  • In your code-behind: InputBox.KeyDown+=((sender,e)=>{ // Do something});

So, to achieve global keyboard shotcuts, instead of registering the event handler to a specified UIElement, just do this:

Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;

Wanna handle the logic in your ViewModel?

  • First, assume that you have a BasePage that contains some base logical code. Define a event handler:
public event EventHandler<KeyEventArgs> GlobalPageKeyDown;
  • Then, in this page's OnNavigatedTo & onNavigatedFrom override method, register and safely unregister the event handler(NOTE: if you don't unregister the event, it will cause memory leak and the whole page instance won't be recycled.)

OnNaivagedTo:

Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;


OnNaivagedFrom:

Window.Current.CoreWindow.KeyDown -= CoreWindow_KeyDown;

Then in CoreWindow_KeyDown, invoke the event handler we defined above:

GlobalPageKeyDown?.Invoke(sender, args);
  • Any page that derived from the BasePage can add this in your XAML code:
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"

xmlns:Core="using:Microsoft.Xaml.Interactions.Core"

xmlns:attach="using:JP.Utils.Framework"

<Interactivity:Interaction.Behaviors>
        <Core:EventTriggerBehavior EventName="GlobalPageKeyDown">
            <attach:InvokeCommandByKeyDown Command="{x:Bind LoginVM.LoginCommand}" PressedKey="Enter" />
        </Core:EventTriggerBehavior>
    </Interactivity:Interaction.Behaviors>

Note that it's using Behavior SDK, which has been updated to support UWP apps recently.

And InvokeCommandByKeyDown is a custom action we will define below.

  • Here is the code of InvokeCommandByKeyDown:
 public class InvokeCommandByKeyDown:DependencyObject,IAction
    {
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(InvokeCommandByKeyDown), new PropertyMetadata(null));

        public VirtualKey PressedKey
        {
            get { return (VirtualKey)GetValue(PressedKeyProperty); }
            set { SetValue(PressedKeyProperty, value); }
        }

        public static readonly DependencyProperty PressedKeyProperty =
            DependencyProperty.Register("PressedKey", typeof(VirtualKey), typeof(InvokeCommandByKeyDown), new PropertyMetadata(VirtualKey.None));

        public object Execute(object sender, object parameter)
        {
            KeyRoutedEventArgs keyPrarm = parameter as KeyRoutedEventArgs;
            if (keyPrarm != null)
            {
                if (keyPrarm.Key == PressedKey)
                {
                    Command.Execute(sender);
                    keyPrarm.Handled = true;
                }
            }
            else
            {
                KeyEventArgs param = parameter as KeyEventArgs;
                if (param != null && param.VirtualKey == PressedKey)
                {
                    Command.Execute(sender);
                    param.Handled = true;
                }
            }
            return null;
        }
    }