Part 2: Manage life cycle and state (Windows Runtime apps using C++)
[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]
In this part of the C++ tutorial series, you update your "Hello, world" app to respond to life-cycle events, and save user and session data.
You can launch several Store apps and switch between them without worrying about slowing down the system or running down the battery. That's because the system automatically suspends—and sometimes closes—apps that are running in the background. A well-designed app can be suspended, closed, and restarted by the system and still seem as though it was running the entire time.
Before you start...
- This is the second tutorial in a series. Before you start this tutorial, read Hello World in C++.
- The complete code for this tutorial is in the Hello World (C++) sample on Code Gallery.
About the app's life cycle
Before you go back to the code, let's talk a bit about the app's life cycle. At any given point, an app is either not running, running, or suspended. The transition from not running to running is called activation; it marks the beginning of the app's life cycle.
When an app is running, it can be suspended when the user switches away from it or the operating system enters a low power state. While your app is suspended, it continues to reside in memory so that the user can quickly switch back to it. No extra code is required to handle the transition between suspended and resuming.
But Windows can also close a suspended app at any time to free up memory or to save power. When your app is closed, it's unloaded from memory and goes into a not running state. When the user starts it again, the system must activate it, and that might mean that the app data and user data must be reloaded.
When the user closes an app by pressing Alt+F4 or by using the close gesture, the app is suspended for 10 seconds and then is closed.
Windows notifies your app before it suspends it, but not before it closes it. You handle the OnSuspended event to save state and release resources and file handles. When the app is activated, you handle the OnLaunched event to restore state and make it seem to the user that the app never stopped running.
Step 1: Using SuspensionManager
In Hello World in C++, the default file MainPage.xaml is used. It's based on the Blank Page template, which has no support for navigation or the app life-cycle. Now, we're going to replace MainPage with a Basic Page. When you add a Basic Page to a project for the first time, Visual Studio also adds several files to the project in the \Common\ folder. One of these files contains the NavigationHelper class, which handles the details of forward and backward navigation through your app's pages. The other class that's added is SuspensionManager, which helps serialize the page-state data in your app's local storage when the app is suspended, and restore it when the app is activated. In this tutorial, we'll focus on Suspension Manager.
Perform the following steps in both the Windows Phone 8.1 project and the Windows 8.1 project:
To add a basic page to the project
Right-click MainPage.xaml, click Remove, and then choose the option to permanently delete the file. (This also deletes the two code-behind pages.)
On the menu bar, click Project > Add New Item, and then in the left pane of the dialog box, select XAML, and in the center pane, select Basic Page. Name this page MainPage and then click Add. A BasicPage has a lot more markup than a BlankPage. Its grid panel has two rows. In the Windows project, the top row contains a navigation button and a TextBlock that shows the app's title—by default, "My Application"—which is defined in the
<x:String x:Key="AppName">My Application</x:String>
element in thePage.Resources
node in MainPage.xaml. In the Windows Phone 8.1 project, the top row contains two text blocks, one for the page title and one for the app title. There is no back button because phone apps use the hardware back button.Let's change the app title to "Hello World." Although we could change it in the Resources element in MainPage.xaml, a better place to define the resource is in App.xaml in the shared project, because both the Windows 8.1 project and the Windows Phone 8.1 project can use it. First, delete the resource in MainPage.xaml, then in App.xaml in the HelloWorld.Shared project, just before the closing tag, insert this
<Application.Resources>
node:<Application.Resources> <x:String x:Key="AppName">Hello World</x:String> </Application.Resources>
The
pageTitle
TextBlock binds to the static resourceAppName
, like this:<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}"
. It now finds that resource in the App class because we deleted the local override in the page class.Next, we paste in the XAML that we added in the previous tutorial, except that here it's modified a bit—the text block that held the app title is eliminated because the title is now displayed in the title grid. And in the StackPanel we added a Grid.Row attribute to correctly place the StackPanel on the new page.
Windows 8.1: We put our XAML in the bottom row of the root grid so that it shows up beneath the grid that contains the navigation button and title.
</Grid> <!-- PASTE HERE --> </Grid> </Page>
<StackPanel Margin="24,20,20,20"> <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" Text="What's your name?"/> <TextBox x:Name="nameInput" Width="300" HorizontalAlignment="Left"/> <Button Content="Say "Hello""/> <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" x:Name="greetingOutput"/> </StackPanel>
Windows Phone 8.1: A basic page is somewhat different in the Phone project. Paste the following XAML in the
contentRoot
grid element. Because the phone screen is typically narrower than a tablet or PC screen, we stack the controls vertically. (Vertical is the default orientation.)<Grid Grid.Row="1" x:Name="ContentRoot"> <!-- PASTE HERE --> </Grid>
<StackPanel Margin="24,20,20,20"> <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" Text="What's your name?"/> <TextBox x:Name="nameInput" Width="300" HorizontalAlignment="Left"/> <Button Content="Say "Hello""/> <TextBlock Style="{ThemeResource BaseTextBlockStyle}" FontSize="16" x:Name="greetingOutput"/> </StackPanel>
In the TitlePanel, point the app title TextBlock to the global AppName that we added to App.xaml as a static resource—just like in the Windows project—and delete the second TextBlock because we don't need a page title. The title panel now looks like this:
<!-- TitlePanel --> <StackPanel Grid.Row="0" Margin="24,17,0,28"> <TextBlock Text="{StaticResource AppName}" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/> </StackPanel>
Let's not forget to add the event handler! In the first tutorial, we used the Properties window (F4) to add an event handler. Here's another way: in the Button element just after the content attribute, start typing
Click
; a list should appear and "Click" should be pre-selected, depending on how much you have typed.Press Tab to accept the suggestion. A box that says "New event handler" should appear. Press Enter to have Visual Studio create the function prototype in the .h file and an implementation stub in the .cpp file. With this approach, you have to accept the default name, which will be something like Button_Click. (To change the name, you can press Ctrl+Shift+H to replace it project-wide.) Press F12 to Go To Definition and add this code to the implementation:
greetingOutput->Text = "Hello, " + nameInput->Text + "!";
Next, lets' register the main app Frame so that SuspensionManager can save and restore the navigation state for each page that the Frame holds. You register the Frame immediately after it's created in the OnLaunched method in App.xaml.cpp.
To save the Frame state
In Solution Explorer, in the shared project at the bottom, open App.xaml.cpp.
Add the following #include directive.
#include "Common\SuspensionManager.h"
In the OnLaunched method, call the SuspensionManager::RegisterFrame static method to register the root Frame. Place this statement directly after the
rootFrame = ref new Frame();
statement.HelloWorld::Common::SuspensionManager::RegisterFrame(rootFrame, "appFrame");
Repeat steps 1 through 3 for the Windows Phone 8.1 project.
Note Use the context chooser drop-down at the top of the code editor to match the project you are currently working in. By selecting the correct context, you ensure that the code editor will gray-out code that isn't compiled for the project. When you select the Windows project in the context chooser...
...this is how App.xaml.h appears:
That's all we have to do to save the frame state. The SuspensionManager class does the rest. You can view that class definition more closely in the Common folder in your project.
Step 2: Save the page state
Generally, there are two kinds of data to manage in an app: app data and session data. App data persists across sessions and must always be accessible to the user. In your app, the Text of the nameInput
TextBox is app data. Always save important app data incrementally whenever the app is running. Because your app has only five or fewer seconds to run code in the suspending event handler, you have to ensure that important app data is completely saved to persistent storage before the app is suspended. Don't hold megabytes worth of app data and then try to save it all at once when the app is suspended.
The Windows.Storage.ApplicationData class helps manage app data. This class has a RoamingSettings property that returns an ApplicationDataContainer that can store app data that persists across sessions. Let's store the user's name in the roaming ApplicationDataContainer as the user types it in.
Note The RoamingSettings app data container makes it easy to store data in a way that's accessible to the user across multiple machines. Basically, the data is uploaded to the cloud in the background. You can also use the LocalSettings app data container, but only when you want to store machine-specific info.
Save persistent app data as often as it makes sense in your app. Here, you handle the TextBox::TextChanged event on the nameInput
TextBox and save the user's name as the user enters it.
To save app data
In the
nameInput
XAML tag, start typing "TextChanged," then press Tab. When the event-handler box appears, press Enter. Press F12 to go to the event handler that was created—nameInput_TextChanged
and then add this code to save thenameInput
text inroamingSettings
:auto roamingSettings = Windows::Storage::ApplicationData::Current->RoamingSettings; roamingSettings->Values->Insert("userName", nameInput->Text); if (roamingSettings->Values->HasKey("userName")) { nameInput->Text = roamingSettings->Values->Lookup("userName")->ToString(); }
Press F5 to build and run the app. Your name is saved as you enter it in the text box. Later, this tutorial shows how to verify that.
Repeat steps 1-2 for the Windows Phone 8.1 project.
Session data is temporary data that's relevant to the user’s current session in your app. A session ends when the user closes the app, or logs off the device or reboots it. You restore it only if the operating system suspends and closes the app. (Remember, just suspending an app doesn't remove it from memory, and so no state is lost in that case.) You use the SuspensionManager
class to save session state in the Application::Suspending event handler in App.xaml.cpp. Save your state here because the operating system might decide to close the app later and there is no notification for that event. Our session data consists of:
The navigation state of the app Frame, so that the app can be restored to the same page is was on.
The state of each page. In our case, we have one page that has one state variable: the
greetingOutput
text.
To save the session state
In Solution Explorer in the shared project, open App.xaml.cpp.
Add this using directive at the top of the file:
using namespace concurrency;
In the
OnSuspending
event handler method, add these lines:auto deferral = e -> SuspendingOperation->GetDeferral(); create_task(HelloWorld::Common::SuspensionManager::SaveAsync()) .then([deferral]() {deferral->Complete(); });
Calling
SaveAsync
saves the navigation state of the Frame and then gives your Page the opportunity to save its content in itsSaveState
method. We'll talk more about thecreate_async
andthen
methods in a later tutorial.To save the page state, in Solution Explorer, open MainPage.xaml.cpp and then add this code to the
SaveState
method:// Insert adds a new key or replaces value if key already exists. e->PageState->Insert("greetingOutputText", greetingOutput->Text);
The
SuspensionManager
class serializes and saves thepageState
dictionary to an XML file. Data saved inpageState
is saved only for the current session.Repeat step 4 for the Windows Phone 8.1 project. (Steps 1-3 apply to both projects.)
Choose Build > Build solution to make sure the app builds with no errors. We'll test the functionality after we add support for app state.
Step 3: Restoring the app's state in the OnLaunched event handler
Earlier, you saw that the App.xaml.cpp file contains code that handles your app's activation. There are many different ways to activate an app. Here we look at the Launch activation and the OnLaunched method.
An app is launched whenever it wasn't running and the user starts it. When an app is launched in this way, Windows displays the app's splash screen.
In App.xaml.cpp, the code that handles activation defines an override of the OnLaunched method. This code is executed only if the activation is a Launch activation. (You can override other methods to handle other kinds of activation, but we don't do it here. For more info, see Application lifecycle.)
Basically, the code performs these steps:
Declares a Frame and tries to assign the current window contents to it.
If the window already contains a Frame, the app is already initialized in memory and so Frame initialization is skipped.
If the app isn't initialized, the code creates a Frame to host the app pages. You register the Frame with the
SuspensionManager
. This is the code you added in Step 1.If the window already contains a Frame, the app is already initialized in memory and so Frame initialization is skipped.
Next, the code checks the previous execution state to see how the app was last closed. When the previous execution state is Terminated, it means that the last time the app ran, Windows successfully suspended it and then closed it. In that case, you have to restore the app's state.
Next, the code checks whether the Frame contains any content. If the app is running, or the navigation state was restored, the Frame already has content. Otherwise, the Frame navigates to the first page in the app. In this case, it navigates to MainPage.
Finally, the code activates the window.
Now that you know what happens when the app is started, let's look at how to restore the app state.
To restore the app's state
If the app was closed, call the
SuspensionManager::RestoreAsync
method.Calling
RestoreAsync
restores the navigation state of the Frame and then calls LoadState on each Page in the Frame.if (e->PreviousExecutionState == ApplicationExecutionState::Terminated) { HelloWorld::Common::SuspensionManager::RestoreAsync(); }
In MainPage.xaml.cpp in the Windows 8.1 project, add code to the
LoadState
method to restore the page state.First, check to see whether the
pageState
dictionary exists and has a key namedgreetingOutputText
. If the key exists, use it to restore thegreetingOutput
text.Next, load the user name. Because you want the user-name data to persist over multiple sessions, you store it in the RoamingSettings app data container. Let's add some code to see whether the user name exists and, if it does, to display it.
Here's the complete code for the
LoadState
method.void MainPage::LoadState(Object^ sender, Common::LoadStateEventArgs^ e) { (void) sender; // Unused parameter // Restore values stored in session state. if( e->PageState != nullptr && e->PageState->HasKey("greetingOutputText")) { greetingOutput->Text = e->PageState->Lookup("greetingOutputText")->ToString(); } // Restore values stored in app data. auto roamingSettings = Windows::Storage::ApplicationData::Current->RoamingSettings; if (roamingSettings->Values->HasKey("userName")) { nameInput->Text = roamingSettings->Values->Lookup("userName")->ToString(); } }
Repeat steps 1 and 2 for the Windows Phone 8.1 project.
Now you can build and run the app, and examine how the session state is saved and restored. To test a project, right-click the node in Solution Explorer and then click Set as Startup Project. The project node is now bold in Solution Explorer. So far, you've tested your app in Visual Studio by running it in debug mode, and stopped it by selecting Debug > Stop Debugging. But doing this causes the app to perform a normal shutdown, and the Suspending event doesn't occur. Fortunately, you can use other functionality in Visual Studio to simulate suspending, closing, and restoring an app.
To simulate suspending, closing, and restoring an app
Set the desired project as the startup project, then press F5 to run the app in debug mode.
Enter your name in the input box and click "Say "Hello"". The greeting is displayed.
In Visual Studio, open the Lifecycle Events drop-down menu on the Debug Location toolbar.
By default, the Debug Location toolbar appears when the debugger is running. If it's not there, click View > Toolbars > Debug Location to show it.
Select Suspend and shutdown.
Visual Studio simulates suspending and closing your app, and so the Suspending event occurs and your state management code is executed.
Press F5 to run the app again. The app is restored to its previous state.
Type a different name in the text box, and choose "Say "Hello"".
In Visual Studio, close the app by selecting Debug > Stop Debugging.
Press F5 to run the app again.
Notice that the name that you last typed is restored because it was saved as you typed it in. The greeting is not restored because the Suspending event didn't occur, and so the session state wasn't saved.
Set the other project as the startup project and try the same thing.
Summary
Congratulations, you're done with the second tutorial! It taught how to save app data and session state in a Store app.
See the code
Did you get stuck, or do you want to check your work? If so, see the Hello World (C++) sample on Code Gallery.
Next steps
The next part of this tutorial series teaches how to use the design tools in Visual Studio to create a more complex UI. See Navigation, layout, and views (C++).