27 December 2015

Gotcha–navigating from SplitView requires back button to be pressed twice in UWP apps

Yesterday, while being in the process of converting my one of my apps to UWP, I was moving a lot of the functionality of the app menu to a SplitView. Microsoft have really gone out on a limb as far as samples are concerned, so I was following the SystemBack sample, which has really little code in it. How hard can it be, right?
All  went rosey, until I discovered that altough the back button showed up nicely on my secondary page, the actual navigation back only happenend after I had pressed the back button twice. Some debugging learned me the global event
SystemNavigationManager.GetForCurrentView().BackRequested
does not even get called the first time. I spent a not so enjoyable hour deconstructing my app, then finally building a bare bones repro – and there it happened too. One of those moment where you seriously start to doubt your own mental health. The code is not very complicated. I have a MainPage.xaml with a button on a SplitView, and when you click that, it navigates to the next page
private void Navigation_Click(object sender, RoutedEventArgs e)
{
  Frame rootFrame = Window.Current.Content as Frame;
  rootFrame.Navigate(typeof(Page2));
}
And on that Page2 a rather piece of standard code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  var rootFrame = Window.Current.Content as Frame;
  if (rootFrame != null)
  {
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
      rootFrame.CanGoBack
        ? AppViewBackButtonVisibility.Visible
   : AppViewBackButtonVisibility.Collapsed;
  }
}
And in the app.xaml.cs, also rather standard (and ugly, now that I look at it), nicked from the sample
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
  Frame rootFrame = Window.Current.Content as Frame;
  if (rootFrame == null)
    return;

  if (rootFrame.CanGoBack && e.Handled == false)
  {
    e.Handled = true;
    rootFrame.GoBack();
  }
}
And yet, this method gets only called on the second back button press. The solution? Read back a little.

“I have a MainPage.xaml with a button on a SplitView, and when you click that, it navigates to the next page”

It's one of those mental leaps you sometimes have to make as a developer. It turns out that the exact method Navigation_Click works flawlessly when the button calling the method is not on a SplitView. Somehow, some way, having a SplitView open messes up the navigation back stack.
The solution, once you know that, is very simple of course:
private void Navigation_Click(object sender, RoutedEventArgs e)
{
  rootSplitView.IsPaneOpen = false;
  Frame rootFrame = Window.Current.Content as Frame;
  rootFrame.Navigate(typeof(Page2));
}
Go back to the click event handler, and add a line that closes the SplitView before initiating the navigation. It’s the little things like this that makes developer life so interesting. ;)

Code used in this post can be found here.

Special thanks to Scott Lovegrove and the other people from the Open Live Writer team for making this post possible – it’s the first one using Open Live Writer that now supports blogger – right after Google shut down the outdated web api endpoint that Live Writer still used. Thank you all folks!

7 comments:

Unknown said...

Have you created a bug about this?

Joost van Schaik said...

Hi Yury, I have not (yet). I don't know if this is intended behavior or not. Will find out, though

Unknown said...

Thank you very much :) I spent more than two weeks with trying to solve this problem... I'll share this solution cause It's awesome help for other developers...

Joost van Schaik said...

@Ján Janušek Please do so. The whole point of this blog is to write samples helping people avoid the traps I fell into. And thanks for your kudos.

Matthias said...

Thanks for this post. I also needed an hour to find this cause. But I have a follow up problem: there is a "LightDismissLayer" (luckily found with the Visual Tree Browser) which stays on the SplitView content when I navigate back. The SplitView.Pane is gone and also my own darkening layer. But this SplitView own layer keeps there.

So my problem is, I cannot click on anything in the SplitView content. When I open the SplitView pane and close it again, it's all fine :/

Do you have any suggestions? Thanks

Matthias said...

I spend another hour now. I noticed, when I set TargetVersion to 10586 (I had 14393 before) this problem is gone...

Joost van Schaik said...

@Matthias I love problems that get solved before I have the time to answer. Good job and thanks for sharing