Exercise 1: Introducing the Windows Phone Application Life Cycle

This exercise uses a provided starter solution for Microsoft Visual Studio 2010 Express for Windows Phone or Microsoft Visual Studio 2010. This solution includes a simple Windows Phone 7 application that creates and displays travel reports. We will perform the following tasks to illustrate how tombstoning works:

  • Connect the application to existing data and add calls to log
  • Handle application and page navigation events

We will use the Visual Phone Developer 2010 Express development environment, and will deploy to the Windows Phone Emulator for debugging. The solution we will work with is based upon the Silverlight for Windows Phone Application template. During development, we will add a Silverlight for Windows Phone project-specific item, the Windows Phone Portrait Page.

Note:
 The steps in this hands-on lab illustrate procedures using Microsoft Visual Studio 2010 with theWindows Phone Developer Tools, but they are equally applicable to Microsoft Visual Studio 2010 Express for Windows Phone. Instructions that refer generically to Visual Studio apply to both products.

Task 1 – Connecting the Application to Existing Data

This task uses a provided starter solution for Microsoft Visual Studio 2010 Express for Windows Phone or Microsoft Visual Studio 2010. The solution includes the sample Windows Phone 7 application with 3 pages and some preparations for later tasks in this Lab.

  1. Open Microsoft Visual Studio 2010 Express for Windows Phone or Microsoft Visual Studio 2010.
  2. Open the starter project located in the Source\Ex1-Tombstoning\Begin folder, select ApplicationLifecycle.sln.
  3. Examine the opened project:
    • The project is a standard Windows Phone Application, with a customized MainPage.xaml, two pages that work as a wizard for editing trips, one page to display a saved trip, additional resources in App.xaml, and a changed SplashScreenImage.jpg.
  4. A few external tools are used:
    • A Logger class that assists in keeping track of the order in which functions are called.
    • A NonLinearNavigationService class that assists in implementing a wizard behavior when navigating to different pages.
    • A Utils class that wraps the application’s serialization operations.
  5. Currently, the application does not persist its data. This means that if we run the application, modify its data and then restart the application, the modified data would not appear. The TripViewModel class holds the application data. This class is also responsible for saving the data to the application's isolated storage.
  6. Open TripListViewModel.cs and locate the following comment:

    C#

    //TODO: Add a function to load trips from file

  7. Replace it with the following code:

    C#

    // Load trips from file and add them to the ObservableCollection public void LoadTrips() { Trips.Clear(); List<TravelReportInfo> trips = Utils.LoadTravelReports("TravelReportInfo.dat"); foreach (TravelReportInfo trip in trips) { if (!trip.IsBeingEdited) { Trips.Add(trip); } else { CurrentTrip = trip; } } }

  8. Find the SaveCurrentTrip function and locate the following comment:

    C#

    //TODO: Call the util class to save all the trips

  9. Replace it with the following code:

    C#

    // Get trips from the ObservableCollection and save them to file List<TravelReportInfo> trips = new List<TravelReportInfo>(Trips); // Save the current trip if it is still being edited if (CurrentTrip.IsBeingEdited) { trips.Add(CurrentTrip); } Utils.SaveTravelReports(trips, "TravelReportInfo.dat", addToList);

  10. Open App.xaml.cs, find the InitViewModel function and locate the following comment:

    C#

    //TODO: Load data into tripListViewModel

  11. Replace it with the following code:

    C#

    tripListViewModel.LoadTrips();

  12. As mentioned earlier, updating the data in the application works in a wizard like flow – there are two pages, one following the other, that are responsible for updating the data. When the user finishes updating the second edit page, the wizard operation is completed and the application should navigate back to the main page where all the data is presented. The naïve way of implementing this behavior would be to navigate to the main page from the second edit page. Unfortunately this approach will create an unwanted behavior – if the user taps the back button after returning to the main page, the application will navigate back to the second edit page of the wizard. This is not what the user would expect of a wizard process. A better behavior would be to leave the application if the user taps the back button while the main page is displayed. To achieve this, we use the external NonLinearNavigationService asset class. This class follows the navigation process and when it discovers a "navigation loop" it simply causes the application to navigate backward, rather than forward, to the navigated page. To support this process, a few calls to the NonLinearNavigationService need to be made.
  13. Open App.xaml.cs and find the App class constructor and locate the following comment:

    C#

    //TODO: Initialize the NonLinearNavigationService

  14. Replace it with the following code:

    C#

    // init the non linear nav service, // make sure you call it last in the app CTOR NonLinearNavigationService.Instance.Initialize(RootFrame);

  15. Open EditTripFirstPage.xaml.cs and find the OnNavigatedTo function and locate the following comment:

    C#

    //TODO: Handle recursive navigation

  16. Replace it with the following code:

    C#

    // If we are in recursive back - DO NOT DO ANY WORK ON PAGE // Developers - make sure you have no specific logic that you need // to take care of here if (NonLinearNavigationService.Instance.IsRecursiveBackNavigation == true) { return; }

  17. Open EditTripSecondPage.xaml.cs and find the OnNavigatedTo function and locate the following comment:

    C#

    //TODO: Handle recursive navigation

  18. Replace it with the following code:

    C#

    // If we are in recursive back - DO NOT DO ANY WORK ON PAGE // Developers - make sure you have no specific logic that you need // to take care of here if (NonLinearNavigationService.Instance.IsRecursiveBackNavigation == true) { return; }

  19. This lab uses a Logger class to help analyze the different states the application enters. A call to the Logger class is required in key functions:
  20. Open App.xaml.cs, find the Application_Launching function and locate the following comment:

    C#

    //TODO: Create log data

  21. Replace it with the following code:

    C#

    WindowsPhoneRecipes.Logger.Instance.AddLine();

  22. Repeat the previous 2 steps of creating log lines for the following functions under App.xaml.cs:
    • Application_Deactivated
    • Application_Closing
    • App class constructor
  23. Still in App.xaml.cs locate the Application_Activated function and then locate the following comment:

    C#

    //TODO: Create log data

  24. Replace it with the following code:

    C#

    if (e.IsApplicationInstancePreserved) { WindowsPhoneRecipes.Logger.Instance.AddLine("FAS"); } else { WindowsPhoneRecipes.Logger.Instance.AddLine("Tombstoning"); }

    This will write a log message according to whether the application instance was preserved (“FAS”) or tombstoning occurred (“Tombstoning”).

    Note:
    Normally, you will do something more meaningful when the application image is preserved, such as avoiding a lengthy reload operation from isolated storage. However, this is mostly meaningless in such a simple sample application and so we only use the IsApplicationInstancePreserved flag to write a specific log message.

  25. Open MainPage.xaml.cs, find the class constructor and locate the following comment:

    C#

    //TODO: Create log data

  26. Replace it with the following code:

    C#

    WindowsPhoneRecipes.Logger.Instance.AddLine();

  27. Repeat the previous 2 steps of creating log lines for the following function under MainPage.xaml.cs: OnNavigatedTo.
  28. Locate function ShowHideLogButton_Click and replace it with the following code snippet, which displays the log data:

    C#

    private void ShowHideLogButton_Click(object sender, EventArgs e) { // toggles the log area visibilty if (svLog.Visibility == Visibility.Collapsed) { // get the updates log lines from the logger txbLog.Text = WindowsPhoneRecipes.Logger.Instance.Log.ToString(); svLog.Visibility = Visibility.Visible; ContentPanel.Visibility = Visibility.Collapsed; } else { svLog.Visibility = Visibility.Collapsed; ContentPanel.Visibility = Visibility.Visible; } }

  29. Open EditTripFirstPage.xaml.cs, find the class constructor, and locate the following comment:

    C#

    //TODO: Create log data

  30. Replace it with the following code:

    C#

    WindowsPhoneRecipes.Logger.Instance.AddLine();

  31. Repeat the previous 2 steps of creating log lines for the following functions under EditTripFirstPage.xaml.cs:
    • OnNavigatedTo
    • OnNavigatedFrom
  32. Locate the function ShowHideLogButton_Click and replace it with the following code snippet, which displays the log data:

    C#

    private void ShowHideLogButton_Click(object sender, EventArgs e) { // toggles the log area visibilty if (svLog.Visibility == Visibility.Collapsed) { // get the updates log lines from the logger txbLog.Text = WindowsPhoneRecipes.Logger.Instance.Log.ToString(); svLog.Visibility = Visibility.Visible; ContentPanel.Visibility = Visibility.Collapsed; } else { svLog.Visibility = Visibility.Collapsed; ContentPanel.Visibility = Visibility.Visible; } }

  33. Open EditTripSecondPage.xaml.cs, find the class constructor and locate the following comment:

    C#

    //TODO: Create log data

  34. Replace it with the following code:

    C#

    WindowsPhoneRecipes.Logger.Instance.AddLine();

  35. Repeat the previous 2 steps of creating log lines for the following functions under EditTripSecondPage.xaml.cs:
    • OnNavigatedTo
    • OnNavigatedFrom
  36. Locate the function ShowHideLogButton_Click and replace it with the following code snippet, which displays the log data:

    C#

    private void ShowHideLogButton_Click(object sender, EventArgs e) { // toggles the log area visibilty if (svLog.Visibility == Visibility.Collapsed) { // get the updates log lines from the logger txbLog.Text = WindowsPhoneRecipes.Logger.Instance.Log.ToString(); svLog.Visibility = Visibility.Visible; ContentPanel.Visibility = Visibility.Collapsed; } else { svLog.Visibility = Visibility.Collapsed; ContentPanel.Visibility = Visibility.Visible; } }

  37. Open ShowTripPage.xaml.cs, find the class constructor and locate the following comment:

    C#

    //TODO: Create log data

  38. Replace it with the following code:

    C#

    WindowsPhoneRecipes.Logger.Instance.AddLine();

  39. Repeat the previous 2 steps of creating log lines for the following function under ShowTripPage.xaml.cs:
    • OnNavigatedTo
  40. Locate function ShowHideLogButton_Click and replace it with the following code snippet, which displays the log data:

    C#

    private void ShowHideLogButton_Click(object sender, EventArgs e) { // toggles the log area visibilty if (svLog.Visibility == Visibility.Collapsed) { // get the updates log lines from the logger txbLog.Text = WindowsPhoneRecipes.Logger.Instance.Log.ToString(); svLog.Visibility = Visibility.Visible; ContentPanel.Visibility = Visibility.Collapsed; } else { svLog.Visibility = Visibility.Collapsed; ContentPanel.Visibility = Visibility.Visible; } }

  41. Compile the application and fix errors, if any.
  42. Run the application.
  43. Observe the main page. Notice that 3 trips are included already; during startup the LoadTravelReports in the Utils class is called. If it finds a trips database file in the application's isolated storage, it loads the file. Otherwise, it copies a default database file that was packaged in the project (find it under Resources folder).

    Figure 1

    Running the Travel Report application – trip list page

  44. Tap on any of the trips navigate to the ShowTripPage, where the trip data is displayed.

    Figure 2

    Displaying trip data in view mode

  45. Tap the 'Back' button () and now add a new trip by clicking the '+' button (). The EditTripFirstPage should be displayed. Enter data (try the different input fields and see different keyboard layouts).

    Figure 3

    The first page for creating a new trip

  46. Tap Next, enter data, and tap Save.

    Figure 4

    The second page for creating a new trip

  47. The new trip should now appear in the list of trips. Tap the 'Show/Hide Log' button () to show all the log lines added so far.

    Figure 5

    The log view

    Note:
    The 'Show/Hide Log' button can be used in any of the pages in the application.

  48. Clicking the 'Show/Hide Log' button () again hides the log view and shows the last page again.
  49. This step concludes Task 1.

Task 2 – Application Execution Model: Handling Application and Page Navigation Events

In addition to responsiveness, the execution model provides users with a consistent navigation experience between applications. On Windows Phone, users navigate forward by launching applications from the installed applications list or from a tile on the Start screen. Users can also navigate backwards through the pages of a running application, or through the stack of previously running applications by using the hardware Back button.

Windows Phone enables a seamless navigation experience for the user by limiting the phone to running one application in the foreground at any given time. To do this, Windows Phone activates and deactivates applications dynamically, exposing events for developers to respond to when the application state changes. By implementing handlers for these events, developers can save and restore application states as their application transitions between the different states. Under Windows Phone Mango, this process is enhanced by the fact that the operating system retains an application’s in-memory image as long as possible, which may negate the need to manually restore the application’s state when returning to it. This behavior creates an experience in which it seems to the user as if the application continued to run from its last position. This exercise explains, in detail, the life cycle of a Windows Phone application and describes the strategies that developers can use to take advantage of the execution model events.

The procedure in which the operating system terminates an application’s process when the user navigates away from the application is called tombstone or tombstoning. The operating system maintains information about the application’s last state, that is, its last viewed page and the navigation journal. If the user navigates back to the application, the operating system restarts the application process and passes the state data back to the application. The enhancement to this model under Windows Phone Mango is known as fast application switching, or FAS. Be aware that even with FAS we are still required to properly handle tombstoning, as it is entirely possible for an application’s image to be discarded if the phone requires additional memory.

Note:
Important: Please note that your application “terminates” upon tombstoning. Your application is not suspended or “put on hold”; the process that runs your application terminates and is removed from memory and with it, all its data. On Windows Phone Mango your application’s in-memory image is preserved, but only until such a time where the phone requires the memory that it occupies.

Developers use tombstone events in order to preserve the Application state and Page state. These can be restored by the developer later to display the correct last state of the application.

  • Application state is not associated with a specific page. Application state is managed in the events exposed by the PhoneApplicationService class.
  • Page state is a visual state of an application page. This includes such things as the scroll position of a ScrollViewer control and the contents of TextBox controls. Page state management should be handled in the OnNavigatedTo and OnNavigatedFrom event handlers.

When does an application get tombstoned?

Generally speaking, an application gets tombstoned when a user forward navigates away from it. While there are a few exceptions to this general rule, applications in the background may be tombstoned at any time if the system requires additional resources to carry out the current foreground activity.

An application will not be automatically tombstoned when it launches an experience that feels, to the user, like an extension of the original application. These experiences are native flows that help the user complete some task like choosing a photo. The reason behind not tombstoning in these cases is to ensure a smooth flow between the application and the experience it has called [for example, no delay between choosing a photo and returning back to the application that uses it].

Below is the list of native experiences that, when invoked, do not trigger an automatic tombstone in the calling application:

  • PhotoChooserTask
  • CameraCaptureTask
  • MediaPlayerLauncher
  • EmailAddressChooserTask
  • PhoneNumberChooserTask
  • Multiplayer Game Invite [games]
  • Gamer You Card [games]
Note:
Please not that the calling application will not be tombstoned under normal circumstances, but it will be tombstoned if the system requires more resources than are available to bring up the launched experience.

For all other cases, an application in the background will be immediately tombstoned unless the device runs Windows Phone Mango and FAS is available. Those scenarios are detailed below:

  • User forward navigates away from an application [for example, taps the Start key]
  • Application invokes certain Launchers/Choosers not listed above
  • System requires more resources to carry out a foreground activity

Walkthrough of the Life Cycle of an Application

This section walks through the entire life cycle of a Windows Phone application, describing the user actions that cause the state of an application to change, and highlighting the events that developers should handle in their code to create a consistent experience for users. All of the life cycle-related events mentioned in this section (Launching, Running, Closing, Deactivating, and Activating) are members of the PhoneApplicationService class in the Microsoft.Phone.Shell namespace.

Launching

A Windows Phone application is launched when the user taps the entry for the application in the phone’s installed applications list or taps the tile for the application on the Start screen. Whenever the user launches an application using one of these methods, a new instance of the application is created. When the application is started, the Launching event is raised. In the handler for this event, an application should read any necessary data from isolated storage to present the user with a new application session. Applications should not attempt to restore transient state from a previous application instance. When a user launches an application, it should always appear to be a new instance.

Note:
Note that the Launching and Activating events are mutually exclusive.

Running

After the Launching event is handled, an application starts running. While an application is in this state, it manages its own state as the user navigates through the application’s pages. The only execution model-related task that an application may perform during this state is to incrementally save settings and other persistent data to reduce the amount of data that needs to be saved when the application’s state changes. This is optional, and for applications with a small amount of persistent data –like this simple lab exercise–may be unnecessary.

Closing

The application state that follows the running state depends on the actions that the user takes. One possibility is that the user presses the hardware Back button to navigate backwards through the pages of the application, past the application’s first page. When this occurs, the Closing event is raised and the application is terminated. Within the handler for this event, an application should save persistent data to isolated storage. It is not necessary to save transient state data, data related only to the current instance of the application, because the only way for the user to return to the application after it is terminated is to launch it again, opening its home page. As mentioned in the previous section, applications should appear to be fresh instances when the user launches them.

Deactivating

If an application is running and is subsequently replaced in the foreground by another application or experience – for example, the lock screen or a launching chooser, the first application will be deactivated.

There are several ways in which this state can be invoked. An application will be deactivated if the user taps the Start button or if the device timeout causes the lock screen to be engaged – in this case the application will be tombstoned, though a small distinction must be made for Windows Phone Mango, where the application becomes dormant and will only be tombstoned if its in-memory image cannot be sustained. An application will also be deactivated when the user invokes a Launcher or a Chooser, helper applications that allow the user to perform common tasks such as taking a picture or sending an email. In any of these cases, the running application will be deactivated and the Deactivated event is raised. Unlike an application that is terminated, a deactivated application might become tombstoned. The application’s process is either suspended or terminated (as described above in the “Walkthrough of the Life Cycle of an Application” section). The operating system keeps a record of the application and stores a set of state data on behalf of the application (and on Windows Phone Mango, the application’s entire memory image). It is possible that the user will return to the deactivated application, at which point it will be reactivated and opened to the last page viewed by the user.

In the Deactivated event handler, an application should store information about its current state in the dictionary that is exposed through the State property of the PhoneApplicationService class. The data that is stored in this dictionary is transient state data, or data that will help the application to reestablish its state at the time it was deactivated. Remember that with Windows Phone Mango this data may be redundant, assuming the application’s image was preserved until the user returned to the application. Because there is no guarantee that a deactivated application will ever be reactivated, an application should always save any persistent data to isolated storage during this event.

All of the actions taken in the deactivated event handler must be completed within ten seconds, or the operating system will terminate the application without tombstoning it. For this reason, applications with large quantities of persistent data may want to save incrementally as the application is running.

Activating

After an application has been deactivated, it is possible that it will never be reactivated. The user could launch the application again from Start, invoking a new instance of the application. Alternatively, the user could launch several other applications, knocking the deactivated application off of the back of the application stack where it cannot be reached with the Back button.

It is also possible that the user will return to the application. This could happen if the user taps Back until the application is reached. Or, if a Launcher or Chooser was the cause of the deactivation, the user could complete the task or cancel out of it. When the user returns to a deactivated application, it is reactivated and the Activated event is raised. In this event, your application should retrieve persistent data from isolated storage. The application should also read values from the State dictionary in the PhoneApplicationService class to reestablish the state of the application that the user experienced before the application was deactivated, unless the entirety of the application’s state was preserved using fast application switching.

The following diagram illustrates the application life cycle for Windows Phone applications:

Figure 6

Windows Phone Application Life Cycle (source: https://msdn.microsoft.com/en-us/library/ff817008(VS.92).aspx)

Windows Phone applications are deactivated and might get tombstoned when the user navigates away from them. This topic highlights some best practices for handling execution model events.

Note:
Note: The Windows Phone project templates provided with Windows Phone Developer Tools include stubbed out handlers for these events in the project’s App.xaml.cs file.

The first problem to be addressed is handling tombstoning during the creation of a new trip.

Note:
 When developing for Windows Phone Mango, applications will use fast application switching by default. If you wish to force them to tombstone in order to debug that scenario then you should access the project’s properties (by right clicking the project and selecting Properties), go to the Debug tab and check “Tombstone upon deactivation while debugging”.
  1. Make sure that the project is set to tombstone when deactivated. Follow the instructions in the note above.
  2. Run the application again and add a new trip by tapping the '+' button (). The EditTripFirstPage should be displayed. Enter the trip destination.
  3. Now tap the hardware 'Start' button (). The application is now deactivated – it is possible to observe this in the Output window. If the Output window is not visible, open it by choosing View | Output from the Visual Studio menu bar or by pressing CTRL + W,O:

    Figure 7

    Deactivating event in Output window

  4. Tap the 'Back' button () to return to the application. If the application was tombstoned, the "Resuming" screen and a loading animation are displayed. Otherwise, the application should instantly resume.

    Figure 8

    Resuming screen when returning from tombstoning

  5. Notice that the destination field for which a value was provided before tombstoning is now empty. Tap the 'Show/Hide Log' button () to show the log.

    Figure 9

    Log when returning from tombstoning

  6. The log output is divided into three sections. The header shows information about the creation of the current log (after tombstoning) and about the log that was created for the previous session (before tombstoning). The middle part shows the log data for the previous session and the bottom part shows the log data for the current session. Notice the sequence in which the application parts were initialized when the application was returning from tombstone:
    • The application constructor is called
    • The application 'Activated' event is called (and not the 'Launching' event)
    • The application is navigated to the last page before tombstoning and the page's constructor is called.
  7. Stop the debugging process.
  8. Let us see how this is not an issue when fast application switching preserves the application’s image. Make sure that the project is set to use fast application switching (Right click the project, Properties -> Debug and then clear “Tombstone upon deactivation while debugging”). Repeat steps 2-7 and see that the edited description is not lost.
  9. Restore forced tombstoning (follow the instructions from step 1).
  10. To solve the above scenario the application needs to save the edited trip to state before being tombstoned and recover the trip when it's back from tombstoning. In some cases, persisting data at the application level is not applicable; instead developers need to persist data at the page level. This could be due to UI considerations, if not all the input fields are data-bound to objects, or if the developer decided not to synchronize temporary user input to data objects, or for similar reasons.

    Note:
    Note: The above cases are not applicable to the current application since all the fields are data-bound to the data object. The above list is mentioned here only as possible examples.

  11. In some cases, developers want to provide the user with a precise UI at the application deactivation moment. To accomplish this, the element should be focused when the application returns. Use OnNavigatedTo and OnNavigatedFrom events to save desired data in Page State.
  12. Open EditTripFirstPage.xaml.cs and add the following field to the class:

    C#

    private bool _saveToState = true;

  13. Find the CancelWizard function and locate the following comment:

    C#

    //TODO: no need to save to state - cancel

  14. Replace it with the following code:

    C#

    // no need to save to state - // the wizard is being cancelled _saveToState = false;

  15. Find the OnNavigatingFrom function and locate the following comment:

    C#

    //TODO: no need to save to state - cancel

  16. Replace it with the following code:

    C#

    // no need to save to state - // the wizard is being cancelled _saveToState = false;

  17. Still in the OnNavigatingFrom function, locate the following comment:

    C#

    //TODO: no need to save to state - normal

  18. Replace it with the following code:

    C#

    // no need to save to state - // the wizard is progressing to the next or previous page normally _saveToState = false;

  19. Find the OnNavigatedFrom function and locate the following comment:

    C#

    //TODO: save current trip and save to state

  20. Replace it with the following code:

    C#

    if (_viewModel != null) { if (_saveToState) { State["EditedTrip"] = _viewModel.CurrentTrip; } _viewModel.SaveCurrentTrip(false); }

  21. Another requirement would be to prompt the user to continue editing the trip or return to the list of all trips. To support a Yes/No MessageBox, this lab uses a custom control already located in the project, under the controls folder.
  22. Find the OnNavigatedTo function and locate the following comment:

    C#

    //TODO: load the edited trip from state

  23. Replace it with the following code:

    C#

    // The page has been recovered from tombstoning, load the edited trip from state if (State.ContainsKey("EditedTrip")) { _viewModel.CurrentTrip = State["EditedTrip"] as TravelReportInfo; NotificationBox.Show( "Unsaved Trip Report", "Do you want to complete the trip report?", new NotificationBoxCommand("Yes", () => { }), new NotificationBoxCommand("No", () => { CancelWizard(); })); }

  24. The code snippet above allows the user to decide whether or not to continue editing a saved trip in edit mode in case the application is back from the tombstone state.
  25. The second wizard page needs the same handling for tombstoning. Open EditTripSecondPage.xaml.cs and add the following field to the class:

    C#

    private bool _saveToState = true;

  26. Find the CancelWizard function and locate the following comment:

    C#

    //TODO: no need to save to state - cancel

  27. Replace it with the following code:

    C#

    // no need to save to state - // the wizard is being cancelled _saveToState = false;

  28. Find the OnNavigatingFrom function and locate the following comment:

    C#

    //TODO: no need to save to state – normal transition

  29. Replace it with the following snippet:

    C#

    // no need to save to state - // the user is either finishing the wizard or going back _saveToState = false;

  30. Find the OnNavigatedFrom function and locate the following comment:

    C#

    //TODO: save current trip and save to state

  31. Replace it with the following snippet:

    C#

    if (_viewModel != null) { if (_saveToState) { State["EditedTrip"] = _viewModel.CurrentTrip; } _viewModel.SaveCurrentTrip(false); }

  32. To support prompting the user to continue editing the trip or return to the list of all trips, find the OnNavigatedTo function and locate the following comment:

    C#

    //TODO: load the edited trip from state

  33. Replace it with the following snippet:

    C#

    // The page has been recovered from tombstoning, load the edited trip from state if (State.ContainsKey("EditedTrip")) { _viewModel.CurrentTrip = State["EditedTrip"] as TravelReportInfo; // if the user chooses no we navigate back to the main page NotificationBox.Show( "Unsaved Trip Report", "Do you want to complete the trip report?", new NotificationBoxCommand("Yes", () => { }), new NotificationBoxCommand("No", () => { CancelWizard(); })); }

  34. Compile and run the application.
  35. Add a new trip by tapping the '+' button (). The EditTripFirstPage should be displayed. Enter the trip destination.

    Figure 10

    Creating a new trip – before tombstoning

  36. Now tap the hardware 'Start' button (). The application is now deactivated.
  37. Tap the 'Back' button (). Tapping 'Back' returns to the tombstoned application.
  38. This time around the customized message box should appear, asking the user to decide whether or not to continue editing the trip. Click Yes.

    Figure 11

    Dialog prompting the user to continue editing the trip

  39. Notice that now the destination field, for which a value was provided before tombstoning, has the original value.
  40. Click Cancel to leave edit mode and return to the list of trips.
  41. Another way to return to a tombstoned application, rather than tapping the Back button, is by tapping the application icon from the application list.
  42. Add a new trip by tapping the '+' button (). The EditTripFirstPage should be displayed. Enter the trip destination.
  43. Tap the hardware 'Start' button (). The application is now deactivated.
  44. Click the small arrow () in the upper right corner to see the list of installed applications.
  45. Choose the ApplicationLifecycle app; the application should reload. Click the 'Show/Hide Log' button () to show the log. Notice that the application's Launching event is now called, and nothing seems to be loading from state, including no old log data.

    Figure 12

    Log when returning from tombstoning

  46. Also notice that the application navigates to the main page, and not returning to the last edited trip. This behavior needs to be fixed.
  47. Open MainPage.xaml.cs and find the OnNavigatedTo function and locate the following comment:

    C#

    //TODO: Prompt user to load last edited trip

  48. Replace it with the following snippet:

    C#

    // if the current trip is in edit mode, we need to ask if the user wants to go back and edit it if (_viewModel.CurrentTrip != null && _viewModel.CurrentTrip.IsBeingEdited) { // if the user chooses yes, we navigate to the first edit trip page NotificationBox.Show( "Unsaved Trip Report", "Do you want to complete the trip report?", new NotificationBoxCommand("Yes", () => { NavigationService.Navigate(new Uri("/Pages/EditTripFirstPage.xaml", UriKind.Relative)); }), new NotificationBoxCommand("No", () => { })); }

  49. Compile and run the application.
  50. Add a new trip by tapping the '+' button (). The EditTripFirstPage should be displayed. Enter the trip destination.
  51. Now tap the hardware 'Start' button (). The application is now deactivated.
  52. Click the small arrow () in the upper right corner to see the list of installed applications.
  53. Choose the ApplicationLifecycle app. The application should reload, navigate to the main page, and prompt the user about returning to the last edited trip. Click yes to continue editing the trip.

    Figure 13

    Dialog prompting the user to continue editing the trip

  54. The application should now navigate to the first page of the wizard, and the pre-inserted trip data should appear.
  55. Stop the debugger.
  56. This step concludes the exercise and the lab