Tuesday, April 29, 2014

You can only use State between OnNavigatedTo and OnNavigatedFrom in Windows Phone

If you've ever seen this error when debugging a Windows Phone app you have probably learned that you can't reference the State object* outside of OnNavigatedTo() or OnNavigatedFrom().  Easy fix!  Move those calls to the right methods.  If, for some reason, you can't act on that data at that moment, get the value that you need, stuff it in a field/property, then use it later.

The bigger frustration is when you do this but still get the error.  In other words, if you check and double-check, but you definitely only use State in the navigation methods.  If this happens to you, I might have the solution!

I recently came across this issue and was pretty frustrated at first, until I thought about what I was doing a bit more:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    var x = await Something();
    var y = State["key1"];
    // And so on...
}

In this example, I have some pretty basic stuff.  I make a call to do something asynchronously, I get the state value, I do something with that value, etc.  But then it hit me.  That darn awake!  If you remember how await works, you'll realize that this can't really work.  The "secret" of await is that the code executes in two passes.  Everything up to the await runs in the context of the actual function.  After the async line, the containing function (OnNavigatedTo) actually returns.  Once the awaited function returns an actual value, the original code runs on a callback with access to the return value.  A highly simplified view is here:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    Something( (x) =>
    {
        // This is actually a callback invoked when Something is done
        var y = State["key1"];
        // And so on...
    });
}

So the actual flow that is OnNavigatedTo calls Something, then returns.  When the callback is invoked, the runtime correctly complains that the usage of State isn't really happening in OnNavigatedTo.

So what's the solution?  Well, that depends on what you are doing.  In my case, I had a number of initializations to do, and I could just change the order to access State first.  Actually, that's the easiest!  Just access it immediately when the method starts and you'll probably be fine.  There's nothing wrong with an async section in there, as long as you only access State beforehand.  I hope this helps someone else!

* If you've never used the State object, it's pretty handy.  It's just a dictionary, but you can use it to store values in the current page that persist navigation/back, even if you leave the app and come back.  It gets cleared whenever the app is started.