Rico Suter's blog.
 


If you are developing Metro applications, it is evident to not only have a good tablet experience but also a a good desktop (mouse and keyboard) experience. This is why I implemented a simple solution to navigate to the previous page if the user presses the back key. A lot of standard applications in Windows 8 support this feature already and I hope a lot of new applications will support it as well.

The usage of my code is very simple: Just call the RegisterBackKey method if the page has been loaded - the rest will be handled by the method. If the user leaves the page - which calls the Unloaded event - all resources will be freed.

public sealed partial class MyPage : LayoutAwarePage
{
    public MyPage()
    {
        InitializeComponent();
        Loaded += delegate { PageUtilities.RegisterBackKey(this); };
    }
}

Thee next code block shows the implementation of the RegisterBackKey method which can be found in the MyToolkit (PageUtilities.cs). The method registers a callback to receive all key events. If the back key has been pressed and the current focus is not in a text area the application navigates to the previous page:

public static class PageUtilities 
{
    public static void RegisterBackKey(Page page)
    {
        var callback = new TypedEventHandler<CoreDispatcher, AcceleratorKeyEventArgs>(
            delegate(CoreDispatcher sender, AcceleratorKeyEventArgs args)
            {
                if (!args.Handled && args.VirtualKey == VirtualKey.Back && 
                    (args.EventType == CoreAcceleratorKeyEventType.KeyDown || 
                        args.EventType == CoreAcceleratorKeyEventType.SystemKeyDown))
                {
                    var element = FocusManager.GetFocusedElement();
                    if (element is FrameworkElement) // check if pressed while popup is open
                    {
                        if (element is Popup)
                            return;

                        var last = ((FrameworkElement)element).GetVisualAncestors().LastOrDefault();
                        if (last != null && last.Parent is Popup)
                            return;
                    }

                    if (element is TextBox || element is PasswordBox || element is WebView)
                        return; 

                    if (page.Frame.CanGoBack)
                    {
                        args.Handled = true;
                        page.Frame.GoBack();
                    }
                }
            });

        page.Dispatcher.AcceleratorKeyActivated += callback;

        SingleEvent.Register(page, 
            (p, h) => p.Unloaded += h, 
            (p, h) => p.Unloaded -= h, 
            (o, a) => { page.Dispatcher.AcceleratorKeyActivated -= callback; });
    }

    // ... see MyToolkit for complete class
}

The RegisterBackKey method needs the method SingleEvent.Register which is used to register a callback which is automatically deregistered after the first call. The method GetVisualAncestors can be found in the FrameworkElementExtensions class. These methods can also be found in the MyToolkit (SingleEvent.cs).

public class SingleEvent
{
    internal class SingleRoutedEventHandlerContainer
    {
        internal RoutedEventHandler Handler;
    }

    public static void Register<TObj>(TObj sender, Action<TObj, RoutedEventHandler> register,
        Action<TObj, RoutedEventHandler> unregister, Action<object, RoutedEventArgs> action)
    {
        var wrapper = new SingleRoutedEventHandlerContainer();
        wrapper.Handler = delegate(object s, RoutedEventArgs args)
        {
            unregister((TObj)s, wrapper.Handler);
            action(sender, args);
        };
        register(sender, wrapper.Handler);
    }

    // ... see MyToolkit for complete class
}


Discussion