Real-World Tombstoning in Silverlight for Windows Phone, Part 3

2 Comments February 6, 2011

In Part 2 of this series, we made progress toward including tombstoning support in a photo-extras application by adding OnNavigatedFrom and OnNavigatedTo methods that use a combination of page state and isolated storage to save the state of our app before it’s deactivated and restore that state following reactivation. But we also determined that work remains to be done. Specifically, our tombstoning code prevented the user from opening another picture.

Before we implement a solution, let’s take a moment to understand what’s happening. Start a debug run of the app in the emulator. Select one picture, and then a second. Now check out the Debug.WriteLine output in Visual Studio’s output window. It should look something like this, minus the comments I added to correlate actions to output:

Launching
  …
OnNavigatedTo

// Click the Open button
OnNavigatedFrom
Deactivated

// Select the first photo
Activated
PhotoChooserTask.Completed
OnNavigatedTo

// Click the Open button
OnNavigatedFrom
Deactivated

// Select the second photo
Activated
PhotoChooserTask.Completed
OnNavigatedTo

And therein lies the explanation for why the second photo never appears. Observe that the PhotoChooserTask.Completed event fires before OnNavigatedTo is called. When you selected the second photo, the Completed event handler loaded it and copied the WriteableBitmap reference to _bitmap. A few milliseconds later, OnNavigatedTo untombstoned the WriteableBitmap created from the first photo and copied it to _bitmap. So the second photo was loaded – briefly – and immediately replaced by the first photo.

Now that we understand the problem, it shouldn’t be hard to come up with a solution. But there are right ways and wrong ways to go about it. For example, you could do something like this in the PhotoChooserTask.Completed handler, reasoning that if you remove the values from page state that trigger the untombstoning of the old bitmap, OnNavigatedTo won’t replace the new bitmap with the old:

 

State.Remove(_width);

State.Remove(_height);

 

But all you’d accomplish is this:

image

Notice the error message: “You can only use State between OnNavigatedTo and OnNavigatedFrom.” Couldn’t be plainer than that. (Thanks goodness it wasn’t another COMException!) In fact, the documentation is clear on this point, too: you can’t access page state before OnNavigatedTo is called, and you can’t access it after OnNavigatedFrom returns.

A better approach – and one that works – is to make a slight modification to the OnNavigatedTo method. Currently, that method is structured like this:

 

protected override void OnNavigatedTo(NavigationEventArgs e)

{

    Debug.WriteLine("OnNavigatedTo");

 

    if (NavigationContext.QueryString.ContainsKey("token"))

    {

        // If the app was invoked through the Extras menu, grab the photo and display it

    }

    else

    {

        // Restore the state of the Save button

        // Restore the WriteableBitmap

    }

}

 

Turn the else into an else-if to prevent OnNavigated from untombstoning the old bitmap if _bitmap isn’t null (which will be the case if a WriteableBitmap was created and assigned to _bitmap in OnPhotoSelected):

 

protected override void OnNavigatedTo(NavigationEventArgs e)

{

    Debug.WriteLine("OnNavigatedTo");

 

    if (NavigationContext.QueryString.ContainsKey("token"))

    {

        // If the app was invoked through the Extras menu, grab the photo and display it

    }

    else if (_bitmap == null) // Don't untombstone if a new photo was loaded

    {

        // Restore the state of the Save button

        // Restore the WriteableBitmap

    }

}

 

This approach pays double benefits. First, it prevents an old, tombstoned photo from replacing a newly selected one. Second, if the app was deactivated but not tombstoned – which is precisely what happens when the app launches a PhotoChooserTask – it prevents the app from wasting CPU cycles by untombstoning a bitmap that doesn’t need untombstoning.

Now we’re getting somewhere. You can see for yourself that it works by making the highlighted modification to OnNavigatedTo, or by downloading the completed application for Part 3. Then repeat the test you performed at the end of Part 2:

  1. Start the app in the emulator.
  2. Click the Open button and select a picture.
  3. Click the Open button and select another picture.

You should find that the app works as expected. Moreover, you can click the Start button followed by the Back button and prove that tombstoning still works. It seems as if we’ve slayed the tombstoning beast, and you might be tempted to declare the app finished and ship it.

But don’t be too hasty. There’s still a BIG tombstoning bug waiting to be discovered in this app – one that’s unique to photo-extras applications, but one that needs to be fixed nonetheless. In the fourth and final installment in this series on real-world tombstoning, I’ll explain.


2 Comments

Have a Comment?

Archives

Tags