iPhone to Windows Phone 7 – Application LifeCycle and Tombstoning

i2WLogo2

[ Link to First Tutorial ]

tombstone_istock The iPhone and Windows Phone 7 share the same dilemma: how do we maintain an extremely responsive experience for the user, provide some sort of multi-tasking and preserve battery life, all at the same time? The solution offered by Windows Phone 7 is to tombstone applications when they are inactive.  This tutorial will explore tombstoning, multi-tasking and the entire application life cycle in detail.

The guiding model for the entire lifetime of an application, from launch to termination, is called the Windows Phone Execution Model.  It is designed to provide a responsive system, at all times. The strategy to accomplish this is to avoid having two or more applications continuing to run in the background, competing for resources, slowing the foreground application and draining the battery.

To accomplish this, Windows Phone 7, like the iPhone, allows only one application to be running in the foreground, and no 3rd party applications are allowed to run in the background; even when they appear to do so.    When an application is moved to the background, but not closed, it is tombstoned.  Tombstoning means that the application is inert, but, the operating system maintains state information for the application. If the user returns to the tombstoned application, the state information is passed to the application before it is restarted, allowing the programmer to restore the application to the state it was in when it was tombstoned.  This can give the very effective illusion that the application was there all along.

Navigation

The navigation between applications is consistent throughout all user-interactions with the phone.  You navigate into an application by launching it, you navigate out of the application with the hardware back button.  When the user backs out of an application, that application is closed and terminated.

When you are in the middle of using an application and you launch a new application, however, the old application is not closed; it is deactivated.  That means it has a few seconds to put its affairs into order before it is terminated. But terminated it is.  It is just as terminated as a closed application.  In short, it “is not only merely dead, it is really most  sincerely dead.”

The key difference is that a closed application should relaunch like a new instance while a deactivated application should re-launch as if it had not been terminated.

The illusion of resuming “where you were” is accomplished by the clever cooperation of three phone-supported features:

  • Events are fired on the termination and resumption of an application
  • A Developer can use tombstone events to preserve the state of the application and of the current page.
  • The developer can use this state to restore the application when it starts again

Your application will be tombstoned immediately when any of the following happen

  • The user navigates to a new application by pressing, for example, the start key
  • Your application uses a launcher or chooser (covered in a subsequent tutorial in detail) to start a process

On the other hand, if you launch one of a limited number of native services on behalf of your application (e.g., to retrieve an image) your application will not necessarily be tombstoned, unless the newly foreground application needs additional resources.  The services you can launch without necessarily being tombstoned include:

  • · PhotoChooserTask
  • · CameraCaptureTask
  • · MediaPlayerLauncher
  • · EmailAddressChooserTask
  • · PhoneNumberChooserTask
  • · Multiplayer Game Invite [games]
  • · Gamer You Card [games]

The Lifecycle

Each application moves through the stages of the Application lifecycle.  The application moves from one state to another in response to user actions. Each transition triggers events to which you can (and should) respond.

The stages/events are

  • Launching /Activating
  • Running
  • Deactivating /Closing

All of these events are in the Microsoft.Phone.Shell.PhoneApplicationService class.

Launching

launch_rocket_iphoto When the user taps the tile for the application on the start menu the application is started and the Launching event is raised.

This always appears as a new instance.  Thus, in handling the launching event you do not use transient state to restore an existing session, though you are free to check isolated storage for new-instance related information (Isolated storage is persistent storage on the phone, and will be covered in detail in an upcoming tutorial).

To be clear, Launching creates a new instance of your application.

A “resumed” instance is caused by the Activating event, and the two events are mutually exclusive.

Running

RunningOnce either the launching or the activating event is handled, the application is running.

At this point you may want to start saving settings and other persistent data to reduce the amount that needs to be saved when the application moves from Running to either Closing or Deactivating.

At Deactivation (see below) you have only 10 seconds to store all your data, so if you have quite a bit of data it can make a tremendous difference if you have incrementally stored data while the application was running, rather than trying to store it all in the deactivation event handler.

You can enter the running state from either the Launching or Activating state, and you can exit the running state to either the closing or deactivating state.

guillotine Closing or Deactivating

The user can end the running state in one of two ways.  If the user presses the Back button, backing up past the first page of the application, then they have indicated they are done with the application and the Closing event fires and the application is terminated.  To return to the application, the user will restart, firing the Lunching event.  Since this is a termination and not a suspension, the developer will want to store persistent data to isolated storage, but will not store transient state data, as the next launch will appear to be a new instance, not a resumption of this instance.

On the other hand, if the user replaces your application in the foreground with another application, you will receive the Deactivating event.  Make no mistake, your application is about to terminate, just as certainly as it did with closing, but here you will store not only the persistent data but also the transient state data, because if you return, you will want to appear to be in the state you were before being deactivated (tombstoned).

You store the transient data in a dictionary (the State property of the PhoneApplicationService class). You cannot know if the application will be reactivated or not, so you’ll always store your transient data in the State dictionary. If you are reanimated then this will provide the information you need to restore the application to the previous state. If you are never re-activated then the transient data will be discarded.

Most important, from the time you receive the deactivation event you have ten seconds to complete all of the actions necessary to be tombstoned, or the application will not be tombstoned, it will be terminated.  Earlier, we mentioned the opportunity to incrementally store data; if storing your data might push you past the ten second limit, then incremental storage becomes a virtual necessity.

Activating

Dawn After an application has been deactivated, it is possible that it will never be reactivated. On the other hand, it is also possible that the user will return to the application and the user’s experience ought to be that the application has been waiting, in steady state; not that it is being relaunched.

You prepared for this on deactivation by saving persistent data to isolated storage, and transient data to the State dictionary.  Now, upon activation, you will want to retrieve both the persistent and the transient data, and put the application back into the state it was in at the time it was deactivated.

To summarize, and to drive the point home one last time: if the user closes the application (by hitting the Back key past the opening screen) and then re-starts the application, you’ll receive the Launching application and will present a new instance.

If the user, on the other hand, deactivates the application (e.g., by launching another application) and then returns to your application, you’ll receive the Activating event and you’ll handle that by restoring the state of the application and appearing to have been alive the entire time.

Note, however, that tombstoned applications (those that are deactivated) are actually terminated and it really is a new instance when activated; it is the developer’s job to make it appear as though the original application was suspended in the interim.

Building A Demonstration Application

To see these stages at work, let’s build a two-page application for recording a trip report.

This application is a slightly modified version of the application used in the Application LifeCycle Hands On Lab available here.

To begin, let’s create the form for the first page.  This is very similar to the work done in previous tutorials, so I will just show the Xaml for the page to get us started,

<phone:PhoneApplicationPage
    x:Class="WindowsPhoneApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignWidth="480"
    d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait"
    Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <Grid
        x:Name="LayoutRoot"
        Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition
                Height="*" />
        </Grid.RowDefinitions>

        <StackPanel
            x:Name="TitlePanel"
            Grid.Row="0"
            Margin="12,17,0,28">
            <TextBlock
                x:Name="ApplicationTitle"
                Text="TRAVEL REPORT"
                Style="{StaticResource PhoneTextNormalStyle}" />
            <TextBlock
                x:Name="PageTitle"
                Text="My Trip"
                Margin="9,-7,0,0"
                Style="{StaticResource PhoneTextTitle1Style}" />
        </StackPanel>

        <Grid
            x:Name="ContentPanel"
            Grid.Row="1"
            Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition
                    Height="80" />
                <RowDefinition
                    Height="80" />
                <RowDefinition
                    Height="80" />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel
                Grid.Row="0"
                Orientation="Horizontal">
                <TextBlock
                    Text="Trip to:"
                    Style="{StaticResource PhoneTextLargeStyle}"
                    VerticalAlignment="Center" />
                <TextBox
                    x:Name="txtDestination"
                    Text="{Binding Destination, Mode=TwoWay}"
                    Width="362"
                    InputScope="AddressCity" />
            </StackPanel>
            <StackPanel
                Grid.Row="1"
                Orientation="Horizontal">
                <TextBlock
                    Text="First Day:"
                    Style="{StaticResource PhoneTextLargeStyle}"
                    VerticalAlignment="Center" />
                <TextBox
                    x:Name="txtFromDate"
                    Text="{Binding FirstDay, Mode=TwoWay}"
                    Width="330" />
            </StackPanel>
            <StackPanel
                Grid.Row="2"
                Orientation="Horizontal">
                <TextBlock
                    Text="Last Day:"
                    Style="{StaticResource PhoneTextLargeStyle}"
                    VerticalAlignment="Center" />
                <TextBox
                    x:Name="txtToDate"
                    Text="{Binding LastDay, Mode=TwoWay}"
                    Margin="2,0,0,0"
                    Width="330" />
            </StackPanel>
            <StackPanel
                Grid.Row="3">
                <TextBlock
                    Text="Justification:"
                    Style="{StaticResource PhoneTextLargeStyle}" />
                <TextBox
                    x:Name="txtJustification"
                    Text="{Binding Justification, Mode=TwoWay}"
                    AcceptsReturn="True"
                    Height="160"
                    VerticalScrollBarVisibility="Auto" />
            </StackPanel>
            <StackPanel
                Grid.Row="4"
                Orientation="Horizontal"
                HorizontalAlignment="Center"
                VerticalAlignment="Bottom"
                Height="100">
                <Button
                    Content="Next"
                    x:Name="btnNext"
                    Width="230" />
                <Button
                    Content="Cancel"
                    x:Name="btnCancel"
                    Width="230" />
            </StackPanel>
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

Be sure to take a long look at this Xaml and make sure you are comfortable with what it is doing. Then paste it into your new application and ensure it looks like you expected (and that it builds).  Before adding any logic to this, let’s put in the second page, to which you’ll navigate from this page. Select Portrait page, and name it, creatively, SecondPage.  It should open in your designer; if not, double click on it.

Here is the Xaml for the second page:

<phone:PhoneApplicationPage
    x:Class="WindowsPhoneApplication1.SecondPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait"
    Orientation="Portrait"
    mc:Ignorable="d"
    d:DesignHeight="768"
    d:DesignWidth="480"
    shell:SystemTray.IsVisible="True">

    <Grid
        x:Name="LayoutRoot"
        Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition
                Height="*" />
        </Grid.RowDefinitions>

        <StackPanel
            x:Name="TitlePanel"
            Grid.Row="0"
            Margin="12,17,0,28">
            <TextBlock
                x:Name="ApplicationTitle"
                Text="TRAVEL REPORT"
                Style="{StaticResource PhoneTextNormalStyle}" />
            <TextBlock
                x:Name="PageTitle"
                Text="My Trip"
                Margin="9,-7,0,0"
                Style="{StaticResource PhoneTextTitle1Style}" />
        </StackPanel>

        <Grid
            x:Name="ContentPanel"
            Grid.Row="1"
            Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition
                    Height="*" />
                <RowDefinition
                    Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel
                VerticalAlignment="Top">
                <TextBlock
                    Text="Report Summary:"
                    Style="{StaticResource PhoneTextLargeStyle}" />
                <TextBox
                    x:Name="txtSummary"
                    Text="{Binding Summary, Mode=TwoWay}"
                    AcceptsReturn="True"
                    Height="460"
                    VerticalScrollBarVisibility="Auto" />
            </StackPanel>
            <StackPanel
                Grid.Row="1"
                Orientation="Horizontal"
                Margin="10"
                HorizontalAlignment="Center"
                Height="100">
                <Button
                    Content="Save"
                    x:Name="btnSave"
                    Width="230"
                    />
                <Button
                    Content="Cancel"
                    x:Name="btnCancel"
                    Width="230"
                     />
            </StackPanel>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

Navigation

No doubt you noticed the Next Page button on MainPage. Let’s go back to MainPage.xaml and add the logic for navigating to the second page.  One way to do this is to double click on MainPage.xaml and then right click within the page and choose View Code.

Add event handlers for the two buttons,

public MainPage()
{
    InitializeComponent();
    btnNext.Click += BtnNextClick;
    btnCancel.Click += BtnCancelClick;
}
Within the Next Button’s event handler, use the NavigationService’s static Navigate button to navigate to the second page,
void BtnNextClick(object sender, RoutedEventArgs e)
{
    NavigationService.Navigate(new Uri(
          "/SecondPage.xaml", UriKind.Relative));
}

We’re going to hold off on the Cancel button for now, but we do have a problem. The default behavior for the user clicking the back button from the first page is to back out of the application and cause it to be terminated.  If the user clicks Back from the first page of the application, and the operating system’s record keeping shows that it is the first page the user navigated to, then the user will not be asked whether the data entered so far should be saved. We can fix that by overriding the OnBackKeyPress event (an event of the page).

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
    base.OnBackKeyPress(e);

    MessageBoxResult result =
        MessageBox.Show(
                        "Click OK to save your work, or cancel to exit without saving"
                        "Save your work?",
                        MessageBoxButton.OKCancel );

    if (result == MessageBoxResult.OK)
    {
        MessageBox.Show( "Work Saved" );
    }
    else
    {
        MessageBox.Show( "Work discarded" );

    }
}

Run the applciation and try hitting the back button to ensure this works.

Implementing Saving the Work

To facilitate saving the work, we’ll want a couple helper methods.  Create a folder named Utilities and in that folder, add a class TravelReport.cs.  A TravelReport has a first and last day, a destination, a justification and a summary.  It will implement the INotifyPropertyChanged interface as discussed in previous tutorials, and it will also have a public method, Clear, to reset all the report values,

// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------

using System;
using System.ComponentModel;

namespace WindowsPhoneApplication1.Utilities
{
  public class TravelReportInfo : INotifyPropertyChanged
  {
    private string _destination = string.Empty;
    public string Destination
    {
      get { return _destination; }
      set
      {
        _destination = value;
        NotifyPropertyChanged("Destination");
      }
    }

    private DateTime _firstDay = DateTime.Now;
    public DateTime FirstDay
    {
      get { return _firstDay; }
      set
      {
        _firstDay = value;
        NotifyPropertyChanged("FirstDay");
      }
    }

    private DateTime _lastDay = DateTime.Now;
    public DateTime LastDay
    {
      get { return _lastDay; }
      set
      {
        _lastDay = value;
        NotifyPropertyChanged("LastDay");
      }
    }

    private string _justification = string.Empty;
    public string Justification
    {
      get { return _justification; }
      set
      {
        _justification = value;
        NotifyPropertyChanged("Justification");
      }
    }

    private string _summary = string.Empty;
    public string Summary
    {
      get { return _summary; }
      set
      {
        _summary = value;
        NotifyPropertyChanged("Summary");
      }
    }

    public void Clear()
    {
      Destination = string.Empty;
      Summary = string.Empty;
      Justification = string.Empty;
      FirstDay = DateTime.Now;
      LastDay = DateTime.Now;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
      if (null != PropertyChanged)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

Isolated Storage for Persistent Data

The persistent storage is accomplished with Isolated Storage; an approach that lets the application write to a known, safe location on the phone.  To do this, we’ll need to serialize the object, which is done for us using the XmlSerializer object, which exists in the namespace System.Xml.Serialization, which in turn requires that we add a Reference to the dll System.XML.Serialization.

Here’s the code for the SaveTravelReport method,

public static void SaveTravelReport(
    TravelReportInfo travelReportInfo,
    string fileName,
    bool isExiting)
{
    using (
        IsolatedStorageFile isf =
            IsolatedStorageFile.GetUserStoreForApplication())
    {
        using (
            IsolatedStorageFileStream fs = isf.CreateFile(fileName))
        {
            XmlSerializer ser =
                new XmlSerializer(typeof(TravelReportInfo));
            ser.Serialize(fs, travelReportInfo);
        }
    }

    if (!isExiting)
        MessageBox.Show(
                        "Travel report saved successfully",
                        "Save Complete",
                        MessageBoxButton.OK);
}

The method is passed an instance of TravelReportInfo, the file to save it to and a flag indicating whether this is being written as an interim save or because the user is exiting the application.

While you do not have to worry about memory management when creating managed objects, the IsolatedStorageFile is not managed, and so must be disposed of as quickly as possible once you are done with it.  The using syntax causes the destructor for the IsolatedStorageFile and IsolatedStorageFileStream to be destroyed as soon as they fall out of scope.

The logic within the using statements can be read as follows:

  • Get an IsolatedStorageFile by calling the static method GetUserStoreForApplication
  • With that file reference, create a file stream, using the filename given as a parameter
  • Serialize the Travel Report to the file using that file stream
  • If you’re not exiting, pop up a message box reassuring the user that you have saved their data

Handling The Execution Events

So far you are, in theory, persisting data and moving from page to page, but there isn’t really any way to tell yet.  Fortunately, you can respond to the lifecycle events and see if the persistence is actually working.  For example, in App.xaml.cs you’ll find an event named Application_Launching in response to which you can retrieve the persistent data,

private void Application_Launching(object sender, LaunchingEventArgs e)
{

    TravelReportInfo travelReportInfo;

    using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (isf.FileExists("TravelReportInfo.dat"))
        {
            using (IsolatedStorageFileStream fs = isf.OpenFile("TravelReportInfo.dat", System.IO.FileMode.Open))
            {
                XmlSerializer ser = new XmlSerializer(typeof(TravelReportInfo));
                object obj = ser.Deserialize(fs);

                if (obj != null )
                {
                    travelReportInfo = obj as TravelReportInfo;
                }
            }
        }
        if (travelReportInfo == null)
        {
            travelReportInfo = new TravelReportInfo();
        }
    }

    RootFrame.DataContext = travelReportInfo;
}

On line 4 we declare an instance of TravelReportInfo. It is important to know that this reference is now null, and will remain null until assigned to an instance.

Lines 6-11 check whether there is an isolated Storage file for TravelReportInfo. If so, lines 12 and 13 attempt to deserialize it. If the object retrieved is not null then line 17 uses the as operator to attempt an upcast to TravelReportInfo.  If that cast fails, null is assigned to travelReportInfo.

You can see that if the file doesn’t exist, or the object was not deserialized successfully, or the object deserialized is not of the right type, travelReportInfo will still be null; in which case we assign a new (and empty) travel report to it.  In either case, in the penultimate line of the method we set that travelReport as the dataContext for RootFrame; that is, for the entire page.

Clearing The Report

The utility to clear the report is now pretty straight forward,

 public static void ClearTravelReport(TravelReportInfo travelReportInfo)
 {
     travelReportInfo.Clear();

     if (MessageBox.Show(
                          "Clear all saved reports also?",
                          "Clear Data",
                          MessageBoxButton.OKCancel)
          == MessageBoxResult.OK)
     {

         using (
             IsolatedStorageFile isf =
                 IsolatedStorageFile.GetUserStoreForApplication())
         {
             //Check if file exits
             if (isf.FileExists("TravelReportInfo.dat"))
                 isf.DeleteFile("TravelReportInfo.dat");
         }
     }
 }

To Save Or Not To Save

You will remember that when we were writing MainPage.xaml.cs we stubbed out the methods responding to the user’s decision to save or delete the data on backing out of the application. We can now return and fill those in. Here is the complete method,

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
    base.OnBackKeyPress(e);

    MessageBoxResult result =
        MessageBox.Show(
                        "Click OK to save your work, or cancel to exit without saving",
                        "Saving your work before existing",
                        MessageBoxButton.OKCancel );

    if (result == MessageBoxResult.OK)
    {
        // MessageBox.Show( "Work Saved" );
        Utilities.Utilities.SaveTravelReport(
             (App.Current.RootVisual as PhoneApplicationFrame).DataContext
                  as TravelReportInfo,
             "TravelReportInfo.dat", true);

    }
    else
    {
       // MessageBox.Show( "Work discarded" );
        Utilities.Utilities.ClearTravelReport(
            (App.Current.RootVisual as PhoneApplicationFrame).DataContext
              as TravelReportInfo);
   }
}

Run the Application. You should find that if you enter data, and then you back out of the application, and then re-start the application that it has persisted.  In this example, you may or may not want user-entered data to be persisted, remember that in that scenario the application should not appear to resume, it should appear to be a new instance.  We persist the user entered data in this demo to show how to do it, not to suggest that this kind of data should necessarily appear the next time the application starts (though, perhaps some of it should; sticky data can make filling out reports much easier!)

To differentiate what is stored in persistent storage (isolated storage) and what is kept in transient storage, let’s put the Summary from page two into transient storage.

Transient Data

To begin, let’s handle the Deactivated event,

private void Application_Deactivated(
     object sender, DeactivatedEventArgs e)
{
    PhoneApplicationService.Current.State.Add(
        "UnsavedTravelReportInfo",
         RootFrame.DataContext as TravelReportInfo);
}

We are now ready to handle restoring the transient data in the Activated event,

private void Application_Activated(
    object sender, ActivatedEventArgs e)
{
    TravelReportInfo travelReportInfo = null;

    if (PhoneApplicationService.Current.
        State.ContainsKey(
        "UnsavedTravelReportInfo"))
    {
        travelReportInfo =
            PhoneApplicationService.Current.
            State["UnsavedTravelReportInfo"]
               as TravelReportInfo;
        PhoneApplicationService.Current.State.
            Remove("UnsavedTravelReportInfo");
    }

    if ( travelReportInfo == null)
    {
        travelReportInfo = new TravelReportInfo();
    }
    RootFrame.DataContext = travelReportInfo;
}

In short, we check to see if there is transient data; if so we extract and delete it from the dictionary, and we set the travelReportInfo variable to that data. Otherwise, we set the travelReportInfo to a new instance, much as we did previously with the persistent data.

Previous Next

https://jesseliberty.com/2010/09/18/iphone-to-windows-phone-7-tutorial-objective-c-c-xaml/

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in iPhoneToWP7, Patterns & Skills, WindowsPhone and tagged , , . Bookmark the permalink.

11 Responses to iPhone to Windows Phone 7 – Application LifeCycle and Tombstoning

Comments are closed.