SharpGIS

#GIS from a .NET developer's perspective

Shortcut Key Handling in Windows Store Apps

I wanted to create a simple ALT+S shortcut in my app to jump to a TextBox in my Windows Store App (no this is not the Win+Q search charm shortcut). However, this is not that obvious to get working app-wide, so I’ll share it here:

The obvious way to do this is assign a KeyDown event to your page using the typical “this.KeyDown += MyKeyDownHandler”, or override OnKeyDown. However this has one problem: If any control that has focus currently handles key down events (like TextBox), this event won’t be raised due to how event bubbling works. However there is another way to create an event handler that overrides the bubbling: UIElement.AddHandler. In the 3rd parameter of that, you can specify that you want to be notified even if the event has been handled. Here’s what that looks like for listening to the event app-wide:

Window.Current.Content.AddHandler(UIElement.KeyDownEvent, new KeyEventHandler(App_KeyDown), true);
//...
private void App_KeyDown(object sender, KeyRoutedEventArgs e)
{
     //Handle Key Down
}

If you attach to Window.Current.Content, be sure to detach again in OnNavigatingFrom, or you’ll risk having a memory leak, and also still get events firing in that page when it’s not loaded any longer. If you just want this within the page, use myPage.AddHandler of Window.Current.Content.AddHandler, but beware that if anything outside this page has focus the event won’t be raised. – at least in that case you don’t need to worry about unhooking again though.

Now second is to handle the key combination. You can check the menu/alt key status using the following line of code:

bool isMenuKeyDown = CoreWindow.GetForCurrentThread().GetAsyncKeyState(VirtualKey.Menu) == CoreVirtualKeyStates.Down;

So the obvious handler code would look like this:

private void App_KeyDown(object sender, KeyRoutedEventArgs e)
{
    if (e.Key == Windows.System.VirtualKey.S)
    {
        if(CoreWindow.GetForCurrentThread().GetAsyncKeyState(VirtualKey.Menu) == CoreVirtualKeyStates.Down)
        {
            //Handle key combination… 
} } }

Turns out the above code only works every other time. When reading the documentation on GetAsyncKeyState it states that “Determines whether a key is up or down at the time the function is called, and whether the key was pressed after a previous call to GetAsyncKeyState.

So basically this method changes its result based on whether it was called before, and not solely whether the key is down or not. This makes no sense to me why it was designed like this (but do feel free to explain in the comments if you know).

Anyway, if we just make sure this method is always called in the handler it now starts working predictably. So here’s my snippet that ckecks if ALT+S is pressed, sets focus on my textbox and selects all the text:

private void App_KeyDown(object sender, KeyRoutedEventArgs e)
{
    bool isMenuKey = CoreWindow.GetForCurrentThread().GetAsyncKeyState(Windows.System.VirtualKey.Menu) == CoreVirtualKeyStates.Down;
    if (isMenuKey && e.Key == VirtualKey.S)
    {
        queryTextBox.Focus(FocusState.Keyboard);
        queryTextBox.SelectAll();
    }
}

Comments (1) -

  • none

    12/10/2012 9:31:30 PM |

    Does this technique have benefits for your scenario over the CoreWindow or Dispatcher key events (which are global to the app)?

Pingbacks and trackbacks (2)+

Comments are closed