Windows Phone: properly managing BackStack entries with the NavigationHelper

TL;DR – if you’re manually manipulating the BackStack and using NavigationHelper, see the bottom of this post to avoid a bug. Also, download FlySmarter!

I fixed an interesting bug in FlySmarter after a user complained that the first-run tutorial kept showing up after it had been run once. Here’s some background:

On Windows Phone, the Frame class has a BackStack property, which is the history of the Frame. If the user presses the hardware Back button (or you call Frame.GoBack()), the Frame looks at the last entry in the BackStack to figure out what page to go to, and what arguments to pass it.

95% of the time, this what you want. However, sometimes you want to change what happens when the user presses Back. One example in FlySmarter is when you start at the main page (the FlightsList), then you tap to add a flight (AddFlight page), then you enter data and hit OK and the app finds the flight and goes to the flight information page for that flight. (FlightInfo page) Here, when the user presses Back I wanted to go back to the FlightsList page because the user is already done adding the flight. Of course, this is up to you as the developer to figure out what makes sense for your app.

Actually, to implement that I just call Frame.GoBack() to pop the stack after adding a flight to get back to FlightsList, then call Frame.Navigate() to go directly to the FlightsList page. Another, weirder, case, is with the first-run tutorial (see my universal app template for an example of a first-run tutorial). Here I want to start at the FlightsList page, then show some popups on the FlightInfo page, then show some more popups on the FlightsList page. There are several ways you could implement this – my way was to do something like this when we navigate to the FlightsList page:


// if we've navigated to this page with a first-run tutorial ID
if (Frame.CanGoBack &&
Frame.BackStack[Frame.BackStackDepth - 1].SourcePageType
== typeof(FlightInfo))
{
Frame.BackStack.RemoveAt(Frame.BackStackDepth - 1);
}

So, what’s the problem? When I started debugging through FlySmarter to try to find out why the first-run tutorial kept popping up, I noticed that when it happened, the PageState that was being passed to the FlightsList page was not what it should have been! After some debugging, I found the culprit in the NavigationHelper class. Here’s a snippet of code from the class:


public void OnNavigatedTo(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
this._pageKey = "Page-" + this.Frame.BackStackDepth;
// code omitted for brevity
this.LoadState(this, new LoadStateEventArgs(e.Parameter,
(Dictionary<String, Object>)frameState[this._pageKey]));
}

This code is using the current BackStackDepth as part of the key for storing this page’s data, which is a problem if we’re manually manipulating the BackStack and changing its depth!

So, to fix it, I added the following method to the NavigationHelper class:


/// <summary>
/// Clients must call this to remove something from the BackStack, since
/// this uses the BackStackDepth to generate page keys
/// </summary>
public void RemoveBackStackEntry(int indexToRemove)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
bool entryPresent = true;
int index = indexToRemove + 1;
while (entryPresent)
{
entryPresent = frameState.ContainsKey("Page-" + index);
if (entryPresent)
{
frameState["Page-" + (index - 1)] = frameState["Page-" + index];
frameState.Remove("Page-" + index);
}
index++;
}
Frame.BackStack.RemoveAt(indexToRemove);
this._pageKey = this._pageKey = "Page-" + this.Frame.BackStackDepth;
}


and then I call this method instead of BackStack.RemoveAt(). Problem solved!

--

See all my Windows Phone development posts. I also send out a monthly-or-so email with news for developers - check out the latest email and sign up here!

I'm planning on writing more posts about Windows Phone development - what would you like to hear about? Reply here, on twitter at @gregstoll, or by email at greg@gregstoll.com.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s