When you start learning a new platform, the documentation can only take you so far. The Windows Phone 7 docs are great, but when you start developing your first application, you will inevitably run into issues that the documentation doesn’t cover. We are creating the Fuel Tracker guidance application to help people through this process. The idea is to address some of the most common issues that come up for all applications.
Although the guidance is not yet complete, we wanted to share some of our findings. Last week, Cheryl posted a cheat sheet of design guidelines and certification requirements that you should keep in mind during development. This post goes into more detail in the areas of navigation structure, tombstoning, and data loading, providing some tips to help you avoid common problems.
Navigation and the Back Button
Try to keep your navigation model as simple as possible. Wheel-and-spoke models work better than “anything goes”. When we created the Fuel Tracker application, we started with a model where every page provided buttons to navigate to every other page. However, we quickly realized that this causes a few problems:
- When you navigate to a page by clicking a navigation button (as opposed to the back button), a new page instance is created, even if you have previously visited that page. This is not an issue for read-only pages that convey information, but for data-entry pages, users expect any partially-entered data to still be there when they return.
- When you navigate to a page and then click the back button, users expect to go to the previous page. However, one of the application certification requirements is that clicking the back button from the first page must exit the application. This is a little awkward if the user can navigate directly to the first page from any other page.
To address these issues, Fuel Tracker now uses a navigation model where only the first page contains buttons allowing navigation to other pages, and other pages allow navigation only back to the first page.
In this model, the first page is reused, while the other pages are created new each time they are visited. The other pages are data-entry pages, but because they are not reused, they are treated like modal dialogs: users can either tap a save button to save any changes and navigate back to the first page, or they can tap the back button to discard any changes and navigate back.
Application Deactivation and Reactivation, aka "Tombstoning"
Applications can be interrupted at any time. For example, an application is deactivated or "tombstoned" when the user answers an incoming phone call or navigates away using the Start or Search buttons. The application is reactivated when the user ends the phone call or navigates back to it using the Back button.
During deactivation, the application is unloaded from memory, but the phone retains a record of it, including data that you can store in a temporary state dictionary. By default, a reactivated application will show its previously-active page, but you must restore any other state yourself, including reloading any application data that you normally load at startup.
In most cases, you should handle tombstoning in your application. You might think you can just keep things simple at first and accept the default behavior. However, this is reasonable only for pages or applications that have very limited interactivity. For any page where a user can change its state, it is a good idea to store the state during deactivation and retrieve it during reactivation. This applies to state changes as complex as data entry updates or as simple as the user changing which pivot item is currently displayed in a Pivot control.
The deciding factor should be whether anything unexpected occurs when you reactivate. Users expect applications to pick up where they left off, so any deviance from this will look like a bug.
When you implement tombstoning support, be sure to test it thoroughly. You can test it in the emulator by navigating to each page, then clicking the Start button followed by the Back button. Be sure to do this for every page of your application and for each major state that the page can be in (for example, showing a dialog box).
If you test your tombstoning support while the Visual Studio debugger is attached, you will notice that the debugger will sometimes detach itself when you deactivate your application. In this case, the application will appear to hang on the "resuming" screen when you reactivate. You can fix this situation simply by going to Visual Studio and pressing F5 again. The application will then reload and reactivate.
Retrieving and Saving Application Data
Most applications need to store some data on the phone between runs, saving it before shutdown and retrieving it again at startup. Your first instinct might be to retrieve your application data in handlers for the PhoneApplicationService.Launching and Activated events, which are analogous to the startup events found on other platforms. However, these events occur before the application is fully loaded, so if you handle them and perform any time-consuming operations, you will delay startup, potentially creating an impression of poor performance.
If your data-retrieval operations take too long, you will probably want to perform them asynchronously on a background thread. However, even in this case, you should not launch the background thread in the Launching and Activated event handlers. The background thread will not block the startup operations from completing, but it will consume CPU cycles during startup.
A better approach is to perform or start any data-retrieval operations in your pages using Page.OnNavigatedTo method overrides. A page’s OnNavigatedTo method is called whenever the application navigates to the page. This includes the initial navigation to the start page when the application is launched, and the navigation to the previously-active page when the application is reactivated. This means that any page can be the first one to access the data after the application is loaded. If multiple pages need to access the same data, you can provide it through a shared service class that loads the data on the first access only, and provides a cached copy on each subsequent access.
For More Information
This post summarizes some of the information that will be covered in more detail in the Fuel Tracker guidance documentation scheduled for release in the near future. You can see an early release of the application source code at the Fuel Tracker CodePlex page. If you have any thoughts on this guidance or on other things you’d like to see, your feedback is welcome. You can leave a comment here or on the discussions and issue tracker pages at CodePlex.