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

3 Comments February 7, 2011

Tombstoning is one of the greatest challenges in writing applications for Windows phones, which is why I decided to devote a series of blog posts to it. In Part 1 of this series, we built a photo-extras application that allows the user to perform simple image-editing chores on photos. In Part 2, we added tombstoning support so the application wouldn’t lose its state when tombstoned. And in Part 3, we fixed a bug in our tombstoning logic and refined it so that it wouldn’t untombstone anything if the app hadn’t been tombstoned in the first place. In this, the fourth and final article in the series, we’ll finally produce a finished application. But not before we squash one last bug – one that arises from an issue that’s unique to photo-extras applications.

First things first…we need to see the bug in action. To do that, you’ll need to deploy the application to your phone, because you can’t do what I’m about to do in the emulator. (Technically, that’s not quite true. The fundamental problem is that there is no Pictures library in the emulator, at least not one that you can see. There are undocumented ways to hack the emulator and unlock features that aren’t there by default, but I’m not going to get into that.)

Download the app to your phone. It’ll start up when you do. At this point, if you’re using the Zune client to connect your phone to your PC, close the Zune client. If you’re using WPConnect instead, you’re fine. WPConnect doesn’t prevent a phone tethered to your PC from accessing the media library, while the Zune client does. For more information, refer to Silverlight for Windows Phone Programming Tip #4.

Now, do the following:

  1. Press the Start button to go to the phone’s main screen.
  2. Tap the Pictures tile to open the pictures library.
  3. Select a picture. It doesn’t matter which one.
  4. Tap the ellipsis in the lower-right corner of the screen.
  5. In the ensuing menu, select “extras.”
  6. Select RealWorldTombstoningDemo to launch the sample app with the picture you just selected.

So far, so good. Now click the Open button in the application bar and select a different photo. Once again, it doesn’t matter which one as long as it isn’t the same one you selected a moment ago. You should now see the second of the two photos you selected. What do you see instead?

If you step through the OnNavigatedTo method in the debugger when the app is reactivated following the photo selection, you’ll see what’s happening. When RealWorldTombstoningDemo was launched, it was launched with a query string identifying a picture because it was launched from the “extras” menu.

When you clicked the Open button to select another photo, the app was deactivated. When you selected the photo, the app was reactivated. And here’s the key: because the app was originally launched with a query-string token, it’s reactivated with the query-string token, too. Rather than go through the code path that checks for a tombstoned bitmap, OnNavigatedTo followed the code path that loads a photo using a token. It’s as if the app was started from the “extras” menu a second time. But it wasn’t, so we need to ignore the token the second time and use the newly selected photo instead.

Now you see what I meant at the end of Part 3 when I said there’s still a major bug needing our attention. The good news is that it’s not difficult to devise a fix once you understand what’s happening. In a nutshell, we need to rewire OnNavigatedTo to do the following:

  1. If a photo is already present when OnNavigatedTo is called (which will be the case if a photo was loaded in OnPhotoSelected or the app was deactivated but not tombstoned), do nothing.
  2. If there’s no photo present (in other words, if _bitmap == null), check to see if there’s a photo awaiting untombstoning. If there is, untombstone it.
  3. If there’s no photo present and nothing to untombstone, check to see if there’s a query-string containing a picture token. If there is, load the photo from the token.

Here’s the finished OnNavigatedTo method. It’s not very different than before; it has just been restructured a bit. Basically we now give an existing bitmap precedence over one loaded from the “extras” menu. And we retain the key if _bitmap == null test that prevents us from untombstoning a bitmap if a bitmap is already present.

 

protected override void OnNavigatedTo(NavigationEventArgs e)

{

    Debug.WriteLine("OnNavigatedTo");

 

    if (_bitmap == null) // Skip this if a bitmap is already loaded

    {

        // Restore the state of the Save button

        if (State.ContainsKey(_save))

            (ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled =

                (bool)State[_save];

 

        // Restore the WriteableBitmap

        if (State.ContainsKey(_width) && State.ContainsKey(_height))

        {

            int width = (int)State[_width];

            int height = (int)State[_height];

 

            // Create a new WriteableBitmap

            _bitmap = new WriteableBitmap(width, height);

 

            // Get the bitmap bits from isolated storage

            using (IsolatedStorageFile store =

                IsolatedStorageFile.GetUserStoreForApplication())

            {

                if (store.FileExists(_file))

                {

                    using (IsolatedStorageFileStream stream =

                        new IsolatedStorageFileStream(_file, FileMode.Open, store))

                    {

                        using (BinaryReader reader = new BinaryReader(stream))

                        {

                            int count = _bitmap.Pixels.Length * sizeof(int);

                            byte[] pixels = new byte[count];

                            reader.Read(pixels, 0, count);

                            Buffer.BlockCopy(pixels, 0, _bitmap.Pixels, 0, count);

                        }

                    }

 

                    // Clean up by deleting the file

                    store.DeleteFile(_file);

                }

            }

 

            // Show the bitmap to the user

            Photo.Source = _bitmap;

        }

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

        {

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

            MediaLibrary library = new MediaLibrary();

            Picture picture =

                library.GetPictureFromToken(NavigationContext.QueryString["token"]);

 

            BitmapImage bitmap = new BitmapImage();

            bitmap.SetSource(picture.GetImage());

            _bitmap = new WriteableBitmap(bitmap);

            Photo.Source = _bitmap;

 

            // Disable the application bar's Save button

            (ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled = false;

        }

    }

}

 

Now, believe it or not, we have a finished app. You can download it and see for yourself. Try to trick it: load photos, click buttons, play taps on the Back button, launch from the “extras” menu, and anything else you can think of. Unless I’ve missed something, this app is ready to ship.

I did slip one additional feature into the final version. Before, if you edited the photo (clicked it to convert colors to grays) and clicked the Back button, you exited the app with unsaved data. Because the app is closed (not tombstoned) when you exit with the Back button, the changes are lost forever. That’s not a big deal for a demonstration app, but it could be a very big deal in one with richer editing features. So I added an OnBackKeyPress method that prompts the user for confirmation before exiting with unsaved changes:

screen

Check the source code to see how it works.


3 Comments

  • Gravatar Image
    Twitter Trackbacks for Jeff Prosise's Blog : Real-World Tombstoning in Silverlight for Windows Phone, Part 4 [wintellect.com] on Topsy.com February 7, 2011 10:16 AM

    PingBack from http://topsy.com/www.wintellect.com/CS/blogs/jprosise/archive/2011/02/07/real-world-tombstoning-in-silverlight-for-windows-phone-part-4.aspx?utm_source=pingback&utm_campaign=L2

  • Gravatar Image
    Jeremy Likness' Blog February 14, 2011 10:49 PM

    Yesterday I wrote about an ultra light Windows Phone 7 MVVM Framework and mentioned that I would deal

  • Gravatar Image
    Istagfiruu February 24, 2013 11:18 AM

    Duane,How would I approach this? as imeilpd by you it depends In your example . It doesn't appear there would be much heat sinking requirement.The LEDs appear to be pretty small (don't know value of resistors or voltage used).so, thin (6-8 mil) traces on both sides of the components would be fine ( assuming modest ambient temp range for final product)..Even if there was a short run (single trace, 50 mils long) into a ground plane it would likely be fine.On my CAD system I generally don't allow the pouring feature to automatically make thermal connections.. I like to control this on a component basis ( unless old large though hole technology is being used) so I route a simple connection to each pad involved, then pour with the option of disable thermal connections when pad connected - enabled. Another option: specify large thermal connection lengths with very narrow thermal connections. ( 50 mil thermal gap, 5 mil trace for thermal connection) when making a ground pour of the area.However, If that were not the case .(higher power or temperature range)Fatter traces both sides of the resistors and LEDs may be called for to keep it balanced.(as you noted, the need for a thermal balance on small parts)Under no conditions would I pour the ground plane without any thermal connection considerations (top example) on one side of the components.In some RF circuits there may be a compelling reasons to blow off thermal and manufacturing issues (extreme sensitivity to extra inductance, etc..) .. but I would try to avoid this situation at all costs.Saw some recent references to some new ICs that operate at over 700 gHz!At these speeds everything changes

Have a Comment?

Archives

Tags