WinRT: Navigate to the previous page by pressing the back key

July 25, 2012, (updated on September 8, 2014), Software Development

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
}
Tweet about this on TwitterShare on FacebookEmail this to someoneShare on TumblrShare on LinkedIn

Tags: , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax