The SilverlightFlawedTombstoning project is a simple Silverlight program
with just one page. The program responds to taps on the screen by
changing the background of ContentGrid to a random color, and displaying the total
number of taps in its page title. Everything of interest happens in the
code-behind file:
Example 1. Silverlight
Project: SilverlightFlawedTombstoning File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage { Random rand = new Random(); int numTaps = 0;
public MainPage() { InitializeComponent(); UpdatePageTitle(numTaps); }
protected override void OnManipulationStarted(ManipulationStartedEventArgs args) { ContentPanel.Background = new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(256), (byte)rand.Next(256), (byte)rand.Next(256))); UpdatePageTitle(++numTaps);
args.Complete(); base.OnManipulationStarted(args); }
void UpdatePageTitle(int numTaps) { PageTitle.Text = String.Format("{0} taps total", numTaps); } }
|
The little UpdatePageTitle method is called from both the program’s
constructor (where it always results in displaying a value of 0) and
from the OnManipulationStarted
override.
Build and deploy the program
to the phone or phone emulator by pressing F5 (or selecting Start
Debugging from the Debug menu). Arrange Visual Studio so you can see the
Output window. When the program starts up, tap the screen several times
to change the color and bump up the tap count. Now press the phone’s
Start button. You can see from Visual Studio that two threads in the
program end and the program has terminated, but to the phone the program
has actually been deactivated and tombstoned.
Now press the Back button
to return to the program. You’ll see a blank screen with the word
“Resuming…” and the Output window in Visual Studio shows libraries being
loaded. That’s the program coming back to life.
However, when the program
comes back into view, you’ll see that the color and the number of taps
have been lost. All your hard work! Totally gone! This is not a good way
for a program to emerge from tombstoning. It is this state data that we want
to preserve when the program is flat-lined.(Now you may see why the
approach I described after the SilverlightInsertData program would not
always work. That scheme involved saving the instance of SecondPage when MainPagepage.
But if the user goes to the Start screen from SecondPage and then returned, that would be a new instance
of SecondPage
and not the one that FrontPage saved.)
navigated to that
An excellent opportunity to
save and reload state data for a page is through overrides of the OnNavigatedTo and OnNavigatedFrom methods defined by the PagePhoneApplicationPage derives. As you’ve seen, these methods are
called when a page is brought into view by being loaded by the frame,
and when the page is detached from the frame.
class from which
Using these
methods is particularly appropriate if your Silverlight application will
have multiple pages that the user can navigate among. You’ve already
discovered that a new instance of PhoneApplicationPage is created every time a user navigates to a page, so
you’ll probably want to save and reload page state data for normal
navigation anyway. By overriding OnNavigatedTo
and OnNavigatedFrom you’re effectively
solving two problems with one solution.
Although Windows
Phone 7 leaves much of the responsibility for restoring a tombstoned
application to the program itself, it will cause the correct page to be
loaded on activation, so it’s possible that a page-oriented Silverlight
program that saves and restores page state data using the State property of PhoneApplicationSerivceOnNavigatedTo and OnNavigatedFrom
will need no special processing for tombstoning. The phone operating
system preserves this State property during the time a program is
deactivated and tombstoned, but gets rid of it when the program closes
and is terminated for real.
class during
The code-behind file for SilverlightBetterTombstoning includes a using directive for Microsoft.Phone.Shell
and uses this State dictionary. Here’s
the complete class:
Example 2. Silverlight
Project: SilverlightBetterTombstoning File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage { Random rand = new Random(); int numTaps = 0; PhoneApplicationService appService = PhoneApplicationService.Current;
public MainPage() { InitializeComponent(); UpdatePageTitle(numTaps); }
protected override void OnManipulationStarted(ManipulationStartedEventArgs args) { ContentPanel.Background = new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(256), (byte)rand.Next(256), (byte)rand.Next(256))); UpdatePageTitle(++numTaps);
args.Complete(); base.OnManipulationStarted(args); }
void UpdatePageTitle(int numTaps) { PageTitle.Text = String.Format("{0} taps total", numTaps); }
protected override void OnNavigatedFrom(NavigationEventArgs args) { appService.State["numTaps"] = numTaps;
if (ContentPanel.Background is SolidColorBrush) { appService.State["backgroundColor"] = (ContentPanel.Background as SolidColorBrush).Color; }
base.OnNavigatedFrom(args); }
protected override void OnNavigatedTo(NavigationEventArgs args) { // Load numTaps if (appService.State.ContainsKey("numTaps")) { numTaps = (int)appService.State["numTaps"]; UpdatePageTitle(numTaps); }
// Load background color object obj;
if (appService.State.TryGetValue("backgroundColor", out obj)) ContentPanel.Background = new SolidColorBrush((Color)obj);
base.OnNavigatedTo(args); } }
|
Notice the appService
field set to PhoneApplicationService.Current. That’s just for convenience for accessing the State property. You can use the long PhoneApplicationService.Current.State instead
if you prefer.
Storing items in the State dictionary
is easier than getting them out. The syntax:
appService.State["numTaps"] = numTaps;
replaces an existing item if the
“numTaps” key exists, or adds a new item if the key does not exist.
Saving the background color is a little trickier: By default the Background property of ContentPanel is null,
so the code checks for a non-null
value before attempting to save the Color
property.
To get items out of the
dictionary, you can’t use similar syntax. You’ll raise an exception if
the key does not exist. (And these keys will not
exist when the application is launched.) The OnNavigatedTo method shows two different standard ways of accessing the
items: The first checks if the dictionary contains the key; the second
uses TryGetValue, which returns true if the key exists.
In a real program, you’ll
probably want to use string variables for the keys to avoid
accidently typing inconsistent values. (If your typing is impeccable,
don’t worry about the multiple identical strings taking up storage: Strings are
interned, and identical strings are consolidated into one.) You’ll
probably also want to write some standard routines that perform these
jobs.
Try running this program
like you ran the earlier one: Press F5 to deploy it to the phone or
phone emulator from Visual Studio. Tap the screen a few times. Press the
Start button as if you’re going to start a new program. Visual Studio
indicates that the process has terminated. Now press the Back button.
When the program resumes the settings have been saved and the corpse
looks as good as new!
As you experiment, you’ll
discover that the settings are saved when the application is tombstoned
(that is, when you navigate away from the application with the Start
button and then return) but not when a new instance starts up from the
Start list. This is correct behavior. The operating system discards the State
dictionary when the program terminates for real. The State dictionary is
only for transient data and not for data that affects other instances
of the same application.
If you want some similar
data shared among all instances of a program, you probably want to
implement what’s often called application
settings. You can do that as well.