CHAPTER 5

image

App Life Cycle and Contracts

In this, the final chapter in this book, I show you how to take control of the app life cycle by responding to key Windows events. I show you how to fix the code that Visual Studio adds to projects, how to properly deal with your app being suspended and resumed, and how to implement contracts that tie your app into the wider user experience that Windows 8 offers. Along the way, I’ll demonstrate the use of the geolocation feature and show you how to set up and manage a recurring asynchronous task. Table 5-1 provides the summary for this chapter.

Table 5-1. Chapter Summary

Problem Solution Listing
Ensure that your app receives the life-cycle events. Handle the Suspending and Resuming events defined in the Application class. 1–2
Ensure the clean termination of a background task when the app is suspended. Request a deferral and use the five-second grace period that this provides to prepare for suspension. 3-6
Implement a contract. Add the feature implementation in your code and override the Application method so you receive the life-cycle event when the contract is invoked. 7–9

Dealing with the App Life Cycle

In Chapter 1, I showed you the skeletal code that Visual Studio placed into the App.xaml.cs file to give me a jump start with my example project. This code handles the Windows app life-cycle events, ensuring that I can respond appropriately to the signals that the operating system is sending me. There are three key stages in the life of a Windows app.

The first stage, activation, occurs when your app is started. The Windows runtime will load and process your content and signal when everything is ready. It is during activation that I generate the dynamic content for my example app, for example.

Users don’t typically close Windows apps; they just move to another app and leave the Windows to sort things out. This is why there are no close buttons or menu bars on a Windows app UI. An app that is no longer required is moved into the second stage and is suspended. While suspended, no execution of the app code takes place, and there is no interaction with the user.

If the user switches back to a suspended app, then the third stage occurs: the app is restored. The app is displayed to the user, and execution of the app resumes. Suspended apps are not always restored; if the device is low on memory, for example, Windows may simply terminate a suspended app.

Correcting the Visual Studio Event Code

The first change I need to make is to remove the view model from the MainPage.xaml.cs code-behind file. I have been moving the view model gradually to demonstrate the central role that it plays in a Windows app, and in this chapter, there are features I am going to show you that require the view model to be created as part of the life cycle. You can see the simplified MainPage.xaml.cs file in Listing 5-1.

Listing 5-1. Removing the View Model Instantiation from the MainPage.xaml.cs File

using GrocerApp.Data;
using System;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
 
namespace GrocerApp.Pages {
 
    public sealed partial class MainPage : Page {
        private ViewModel viewModel;
 
        public MainPage() {
            this.InitializeComponent();
        }
 
        private void UpdateBadge() {
            // . . .statements omitted for brevity. . .
        }
 
        private void UpdateTile() {
            // . . .statements omitted for brevity. . .
        }
 
        protected override void OnNavigatedTo(NavigationEventArgs e) {
            viewModel = (ViewModel)e.Parameter;
            this.DataContext = viewModel;
 
            MainFrame.Navigate(typeof(ListPage), viewModel);
 
            viewModel.GroceryList.CollectionChanged += (sender, args) => {
                UpdateTile();
                UpdateBadge();
            };
 
            UpdateTile();
            UpdateBadge();
        }
 
        private void NavBarButtonPress(object sender, RoutedEventArgs e) {
            Boolean isListView = (Button)sender == ListViewButton;
            MainFrame.Navigate(isListView ? typeof(ListPage)
                : typeof(DetailPage), viewModel);
 
        }
    }
}

I can now turn to the App.xaml.cs file and build on the template code to respond to changes in the app life cycle. Unfortunately, the code for handling the life-cycle events that Visual Studio adds to a project doesn’t work. It deals with activation and suspension quite happily, but it prevents the app from being notified when it is being restored. Fortunately, the solution to this is pretty simple, and you can see the changes required to App.xaml.cs in Listing 5-2.

Listing 5-2. Handling the Life-Cycle Notification Events

using GrocerApp.Data;
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
 
namespace GrocerApp {
 
    sealed partial class App : Application {
        private ViewModel viewModel;
 
        public App() {
            this.InitializeComponent();
 
            viewModel = new ViewModel();
 
            viewModel.StoreList.Add("Whole Foods");
            viewModel.StoreList.Add("Kroger");
            viewModel.StoreList.Add("Costco");
            viewModel.StoreList.Add("Walmart");
 
            viewModel.GroceryList.Add(new GroceryItem {
                Name = "Apples",
                Quantity = 4, Store = "Whole Foods"
            });
            viewModel.GroceryList.Add(new GroceryItem {
                Name = "Hotdogs",
                Quantity = 12, Store = "Costco"
            });
            viewModel.GroceryList.Add(new GroceryItem {
                Name = "Soda",
                Quantity = 2, Store = "Costco"
            });
            viewModel.GroceryList.Add(new GroceryItem {
                Name = "Eggs",
                Quantity = 12, Store = "Kroger"
            });
 
            this.Suspending += OnSuspending;
            this.Resuming += OnResuming;
        }
 
        protected override void OnLaunched(LaunchActivatedEventArgs args) {
            Frame rootFrame = Window.Current.Content as Frame;
 
            if (rootFrame == null) {
                rootFrame = new Frame();
                if (args.PreviousExecutionState == ApplicationExecutionState.
                 Terminated)

                {
                    //TODO: Load state from previously suspended application
                }
                Window.Current.Content = rootFrame;
            }
 
            if (rootFrame.Content == null) {
 
                if (!rootFrame.Navigate(typeof(Pages.MainPage), viewModel)) {
                    throw new Exception("Failed to create initial page");
                }
            }
            Window.Current.Activate();
        }
 
        private void OnResuming(object sender, object e) {
            viewModel.GroceryList[1].Name = "Resume";
        }
 
        private void OnSuspending(object sender, SuspendingEventArgs e) {
            var deferral = e.SuspendingOperation.GetDeferral();
            viewModel.GroceryList[0].Name = "Suspend";
            deferral.Complete();
        }
    }
}

I have highlighted the most important change, which is to register for the Suspending and Resuming events, and added statements to the handler methods for those event. Visual Studio includes a handler for the Suspending event when it created the class, but I had to add the Resuming handler to get the event notification. I removed the code in the OnLaunched method, which tried (and failed) to work out when the app was being resumed.

My app doesn’t currently perform any tasks that are affected by the app being suspended and resumed at the moment, but I want to show you how to test for the events. To that end, I respond to the Suspending and Resuming events by changing the Name property of the first two items in the GroceryList collection in the view model to signal when these events have been received.

Simulating the Life-Cycle Events

The easiest way to simulate the life-cycle events is to use Visual Studio. When you start an app with the debugger, Visual Studio displays a menu to the toolbar that allows you to send the life-cycle events to the app, which I have highlighted in Figure 5-1.

9781430250340_Fig05-01.jpg

Figure 5-1.  The Visual Studio button to send life-cycle events to an app

If you select the menu items to suspend and then resume the app, you will see the changes in the view model data shown in Figure 5-2. (You won’t see the changes until you resume the app; once the app has been suspended, no UI updates are processed.)

9781430250340_Fig05-02.jpg

Figure 5-2.  Indicating that the life-cycle events have been receiveds

Testing the Life-Cycle Events

The problem with simulating the life-cycle events is that you don’t get quite the same result as when they arise naturally. To do this, you need to start the app without the debugger, which is why indicating that the events have been received via the view model is so useful. Creating the circumstances in which the events are sent requires some specific actions, which I describe step-by-step in the following sections.

Activate the App

To trigger the activated event, start the app by selecting Start Without Debugging from the Visual Studio Debug menu. You can also start the app from the Start screen, either in the simulator or on your local machine. The important thing is not to start the app with the debugger.

Suspend the App

The easiest way to suspend the app is to switch to the desktop by pressing Win+D. Open the Task Manager, right-click the item for your app, and select Go to Details from the pop-up menu. The Task Manager will switch to the Details tab and select the WWAHost.exe process, which is responsible for running the app. After a few seconds, the value shown in the Status column will change from Running to Suspended, which tells you that Windows has suspended the app. The app will have been sent the Suspending event (but you won’t see the evidence of this until the app is resumed).

Resuming the App

Switching back to the app will resume it. You will see that the view model items show that the events have been received. The state of a resumed app is exactly as it was at the moment it was suspended. Your layout, data, event handlers, and everything else will be just as it was.

Your app could have been suspended for a long time, especially if the device was put into a low-power state (such as sleeping). Network connections will have been closed by any servers you were talking to (so you should close them explicitly when you get the Suspending event) and will have to be reopened when your app is resumed. You will also have to refresh data that may have become stale; this includes location data, since the device may have been moved during the period your app was suspended.

image Tip   Windows allows users to terminate apps by pressing Alt+F4. There is no helpful warning event that gives you the opportunity to tidy up your data and operations. Instead, Windows just terminates your app’s process.

Adding a Background Activity

Now that I have confirmed that my app can get and respond to the Resuming and Suspending events, I can add some functionality that requires a recurring background task. For this example, I am going to use the geolocation service to report on the current device location. To start, I have created a new class file called Location.cs in the Data project folder. The contents of this file are shown in Listing 5-3.

Listing 5-3. The Location.cs File

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Windows.Data.Json;
using Windows.Devices.Geolocation;
 
namespace GrocerApp.Data {
    class Location {
 
        public static async Task<string> TrackLocation() {
            Geolocator geoloc = new Geolocator();
            Geoposition position = await geoloc.GetGeopositionAsync();
 
            HttpClient httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(" http://nominatim.openstreetmap.org ");
            HttpResponseMessage httpResult = await httpClient.GetAsync(
                String.Format("reverse?format=json&lat={0}&lon={1}",
                position.Coordinate.Latitude, position.Coordinate.
                Longitude));

 
            JsonObject jsonObject = JsonObject
                .Parse(await httpResult.Content.ReadAsStringAsync());
 
            return jsonObject.GetNamedObject("address")
                .GetNamedString("road") + DateTime.Now.ToString
                ("' ('HH:mm:ss')'");

        }
    }
}

This class uses the Windows 8 geolocation feature to get the location of the device. This feature is exposed through the Geolocator class in the Windows.Devices.Geolocation namespace, and the GetGeopositionAsync method gets a single snapshot of the location (as opposed to providing location updates via events, which is the other approach supported by the Geolocator class).

image Caution   The new C# await keyword signals that I have entered realms of parallel/asynchronous programming. This is an advanced example that uses the Task Parallel Library (TPL) to create and manage background tasks. I won’t go into the details of TPL and .NET parallel programming in this short book. If you want more information, then I suggest my Pro .NET 4 Parallel Programming in C# book, which provides full details. The await keyword is a new addition to C# 4.5, which means “wait for this asynchronous task to complete.”

Once I get the position of the device, I make an HTTP request to a reverse geocoding service, which allows me to translate the latitude and longitude information from the geolocation service into a street address. The geocoding service returns a JSON string, which I parse into a C# object so that I read the street information. The result from the TrackLocation method is a string listing the name of the street the device is on and a timestamp indicating the time of the location update.

image Tip   I have used the OpenStreetMap geocoding service because it doesn’t require a unique account token. This means you can run the example without having to create a Google Maps or Bing Maps developer account.

Extending the View Model

I am going to extend the view model so that it keeps track of the location data generated by the TrackLocation method. This will allow me to use data binding to display the data to the user. Listing 5-4 shows the additions I have made to the ViewModel class.

Listing 5-4. Updating the View Model to Capture the Location Data

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
 
namespace GrocerApp.Data {
    public class ViewModel : INotifyPropertyChanged {
        private ObservableCollection<GroceryItem> groceryList;
        private List<string> storeList;
        private int selectedItemIndex;
        private string homeZipCode;
        private string location;
 
        public ViewModel() {
            groceryList = new ObservableCollection<GroceryItem>();
            storeList = new List<string>();
            selectedItemIndex = -1;
            homeZipCode = "NY 10118";
            location = "Unknown";
        }
 
        public string Location {
            get { return location; }
            set { location = value; NotifyPropertyChanged("Location"); }
        }
 
        // ...other properties removed for brevity...
 
 
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propName) {
            if (PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

Displaying the Location Data

I have updated the MainPage.xaml file to add controls that will display the location data to the user. The data binding ensures that the current information from the view model is used, which means that no changes are required to the code-behind file. Listing 5-5 shows the additional controls.

Listing 5-5. Adding Controls to the XAML File to Display the Location Data

<Page
    x:Class="GrocerApp.Pages.MainPage"
    xmlns=" http://schemas.microsoft.com/winfx/2006/xaml/presentation "
    xmlns:x=" http://schemas.microsoft.com/winfx/2006/xaml "
    xmlns:local="using:GrocerApp.Pages"
    xmlns:d=" http://schemas.microsoft.com/expression/blend/2008 "
    xmlns:mc=" http://schemas.openxmlformats.org/markup-compatibility/2006 "
    mc:Ignorable="d">
 
    <Page.TopAppBar>
        <AppBar>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Button x:Name="ListViewButton"
                    Style="{StaticResource AppBarButtonStyle}"
                    AutomationProperties.Name="List View"
                    Content="" Click="NavBarButtonPress"/>
                
                <Button x:Name="DetailViewButton"
                    Style="{StaticResource AppBarButtonStyle}"
                    AutomationProperties.Name="Detail View"
                    Content="" Click="NavBarButtonPress"/>
            </StackPanel>
        </AppBar>
    </Page.TopAppBar>
 
    <Grid Background="{StaticResource AppBackgroundColor}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >
            <TextBlock FontSize="30" Text="Your location is:" Margin="0,0,10,0" />
            <TextBlock FontSize="30" Text="{Binding Path=Location}" />
        </StackPanel>
        
        <Frame x:Name="MainFrame" Grid.Row="1" />
    </Grid>
</Page>
 

Declaring the App Capabilities

Apps must declare their need to access the location service in their manifest. Before running the updated app, open package.appxmanifest, switch to the Capabilities tab, ensure that the Location capability is checked (as shown in Figure 5-3), and save the file. Your app will also need the Internet (Client) capability, but this is declared by default when Visual Studio creates the project.

9781430250340_Fig05-03.jpg

Figure 5-3.  Enabling capabilities in the manifest

Controlling the Background Task

With all the plumbing in place, I can turn to the management of the background task and the integration with the life-cycle events, which are, after all, the point of this example. Listing 5-6 shows the changes I have made to the App.xaml.cs file.

image Caution   Once again, this is an advanced example. If you are not familiar with the .NET model for parallel programming, then skip to the next section where I demonstrate how to implement Windows contracts in your app.

Listing 5-6. Updating the App.xaml.cs File for the Background Task

using GrocerApp.Data;
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
 
namespace GrocerApp {
 
    sealed partial class App : Application {
        private ViewModel viewModel;
        private Task locationTask;
        private CancellationTokenSource locationTokenSource;
 
        public App() {
            this.InitializeComponent();
 
            viewModel = new ViewModel();
 
            // ...test data removed for brevity...
 
            this.Suspending += OnSuspending;
            this.Resuming += OnResuming;
        }
 
        protected override void OnLaunched(LaunchActivatedEventArgs args) {
            Frame rootFrame = Window.Current.Content as Frame;
 
            if (rootFrame == null) {
                rootFrame = new Frame();
                Window.Current.Content = rootFrame;
            }
 
            if (rootFrame.Content == null) {
 
                if (!rootFrame.Navigate(typeof(Pages.MainPage), viewModel)) {
                    throw new Exception("Failed to create initial page");
                }
            }
            Window.Current.Activate();
            StartLocationTracking(rootFrame);
        }
 
        private void OnResuming(object sender, object e) {
            viewModel.Location = "Unknown";
            StartLocationTracking(Window.Current.Content as Frame);
        }
 
        private void OnSuspending(object sender, SuspendingEventArgs e) {
            StopLocationTracking();
            SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral();
            locationTask.Wait();
            deferral.Complete();
        }
 
        private void StartLocationTracking(Frame frame) {
            locationTokenSource = new CancellationTokenSource();
            CancellationToken token = locationTokenSource.Token;
 
            locationTask = new Task(async () => {
                while (!token.IsCancellationRequested) {
                    await frame.Dispatcher.RunAsync(CoreDispatcherPriority.
                    Normal,

                        async () => {
                            viewModel.Location = await Location.
                            TrackLocation();

                        });
                    token.WaitHandle.WaitOne(5000);
                }
            });
            locationTask.Start();
        }
 
        private void StopLocationTracking() {
            locationTokenSource.Cancel();
        }
    }
}
 

The changes to this class represent two different activities. The first is to track the location of the user as a background task and is contained in the StartLocationTracking and StopLocationTracking methods. I want you to treat these methods as black boxes because I can’t explore the TPL concepts and features I rely on. The important information is that the StartLocationTracking method starts a background activity that reports on the location every five seconds, and the StopLocationTracking method cancels that task.

What I do want to talk about is how I integrate this background task with the life-cycle events. Responding when the app is started or in response to the Resuming event is easy; I simply call the StartLocationTracking method.

For the Suspending event, I want to make sure that my background task has completed before the app is suspended. If I don’t take this step, then I run the risk of either displaying stale data when the app is resumed or causing an error by trying to read from a network request that has been closed by the server during the period that my app was suspended.

To help work around this problem, the SuspendingEventArgs.SuspendingOperation.GetDeferral method tells the Windows runtime that I am not quite ready for my app to be suspended and that I need a little more time. This gives me a short window in which to wait for my task to complete. The GetDeferral method returns a SuspendingDeferral object, and I call its Complete method when I am ready for my app to be suspended.

Asking for a deferral grants an extra five seconds to prepare for suspension. This may not sound like a lot, but it is pretty generous given that Window may be under a lot of pressure to get your app out of the way to make system resources available.

Dispatching the UI Update

One other aspect of Listing 5-6 that is worth noting is this:

. . .
await frame.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    async () => {
        viewModel.Location = await Location.TrackLocation();
});
. . .

Windows will allow updates to UI controls to be made only from a designated thread; this is the thread that was used to instantiate my app. If I update my view model from my background task, the events that are emitted because of the update ultimately result in the wrong thread attempting to update the data binding and display the location to the user. This results in an exception that will be reported with this kind of detailed message:

The application called an interface that was marshalled for a different thread

I need to make sure that I use a Dispatcher to push my updates on the correct thread. However, there isn’t a Dispatcher available within the Application class that my App class is derived from. To solve this problem, I use the Dispatcher from the Frame control that is created in the OnLaunched method.

Testing the Background Task

All that remains is to test that the background task is meshing properly with the life-cycle events. The easiest way to do this is with the simulator, which supports simulated location data.

Start by defining a location in the simulator (one of the buttons on the right side of the simulator window opens the Set Location dialog box into which you can enter a location).

Once you have specified a location, start the app, remembering to do so without using the debugger. After a few seconds, you will see the location information displayed at the top of the app window, as shown in Figure 5-4.

image Tip   I have used the coordinates of the Empire State Building for this example. If you want to do the same, then use the Set Location dialog to specify a latitude value of 40.748 and a longitude of -73.98.

9781430250340_Fig05-04.jpg

Figure 5-4.  Displaying the location information to the user

Switch to the desktop and use the Task Manager to monitor the app until it is suspended. While the app is suspended, use the simulator’s Set Location dialog to change the location.

Resume the example app. The Resuming event will restart the background task, ensuring that fresh data is displayed.

image Tip   You may have to grant permission for the simulator and the app to access your location data. There is an automated process that checks the required settings and prompts you to make the required changes to your system configuration.

Implementing a Contract

Suspending and Resuming are not the only life-cycle events. There are others, and they are used by Windows as part of the system of contracts, which allow you app to get tighter integration with the rest of the operating system. In this section, I will demonstrate the search contract, which tells Windows that my app is willing and able to use the platform-wide search features.

Declaring Support for the Contract

The first step toward implementing a contract is to update the manifest. Open the Package.appxmanifest file and switch to the Declarations tab. If you open the Available Declarations menu, you will see the lists of contracts that you can declare support for. Select Search and click the Add button. The Search contract will appear on the Supported Declarations list, as shown in Figure 5-5.

9781430250340_Fig05-05.jpg

Figure 5-5.  Declaring support for the search contract

Ignore the Properties section for the contract; these allow you to delegate your obligations under the search contract to another app, which I won’t be doing.

Implementing the Search Feature

The purpose of the search contract is to connect the operating system search system with some kind of search capability within your app. For my example app, I am going to handle search requests by iterating through the items on the grocery list and selecting the first one that contains the string the user is looking for. This won’t be the most sophisticated search implementation, but it will let me focus on the contract without getting bogged down in creating lots of new code to handle searches. I have added a method to the ViewModel class called SearchAndSelect, as Listing 5-7 illustrates.

Listing 5-7. Adding Search Support to the View Model

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
 
namespace GrocerApp.Data {
    public class ViewModel : INotifyPropertyChanged {
        private ObservableCollection<GroceryItem> groceryList;
        private List<string> storeList;
        private int selectedItemIndex;
        private string homeZipCode;
        private string location;
 
        public ViewModel() {
            groceryList = new ObservableCollection<GroceryItem>();
            storeList = new List<string>();
            selectedItemIndex = -1;
            homeZipCode = "NY 10118";
            location = "Unknown";
        }
 
        public void SearchAndSelect(string searchTerm) {
            int selIndex = -1;
            for (int i = 0; i < GroceryList.Count; i++) {
                if (GroceryList[i].Name.ToLower().Contains(searchTerm.
                ToLower())) {

                    selIndex = i;
                    break;
                }
            }
            SelectedItemIndex = selIndex;
        }
        
        // ...properties removed for brevity...
 
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propName) {
            if (PropertyChanged != null) {
                 PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
}

This method will be passed the string that the user is searching for. It looks for items in the grocery list and sets the selection to the first matching item it finds or to -1 if there is no match. Since the SelectedItemIndex property is observable, this means that searching for an item will select it and display its details in the app layout.

I want the ListView control in the ListPage to select the matching item, so I have made a minor change to the ListPage.xaml.cs code-behind class, as shown in Listing 5-8.

Listing 5-8. Ensuring That the Selection Is Properly Displayed

. . .
protected override void OnNavigatedTo(NavigationEventArgs e) {
 
    viewModel = (ViewModel)e.Parameter;
 
    ItemDetailFrame.Navigate(typeof(NoItemSelected));
    viewModel.PropertyChanged += (sender, args) => {
        if (args.PropertyName == "SelectedItemIndex") {
            groceryList.SelectedIndex = viewModel.SelectedItemIndex;
            if (viewModel.SelectedItemIndex == -1) {
                ItemDetailFrame.Navigate(typeof(NoItemSelected));
                AppBarDoneButton.IsEnabled = false;
            } else {
                ItemDetailFrame.Navigate(typeof(ItemDetail), viewModel);
                AppBarDoneButton.IsEnabled = true;
            }
        }
    };
}
. . .

Responding to the Search Life-Cycle Event

The Application class makes it very easy to implement contracts by providing methods you can override for each of them. Listing 5-9 shows my implementation of the OnSearchActivated method, which is the one called when the user targets my app with a search.

Listing 5-9. Responding to Searches

using GrocerApp.Data;
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
 
namespace GrocerApp {
 
    sealed partial class App : Application {
        private ViewModel viewModel;
        private Task locationTask;
        private CancellationTokenSource locationTokenSource;
 
        public App() {
            this.InitializeComponent();
 
            viewModel = new ViewModel();
 
            // ...test data omitted for brevity...
            this.Suspending += OnSuspending;
            this.Resuming += OnResuming;
        }
 
        protected override void OnLaunched(LaunchActivatedEventArgs args) {
            Frame rootFrame = Window.Current.Content as Frame;
 
            if (rootFrame == null) {
                rootFrame = new Frame();
                Window.Current.Content = rootFrame;
            }
 
            if (rootFrame.Content == null) {
 
                if (!rootFrame.Navigate(typeof(Pages.MainPage), viewModel)) {
                    throw new Exception("Failed to create initial page");
                }
            }
            Window.Current.Activate();
            StartLocationTracking(rootFrame);
        }
 
        protected override void OnSearchActivated(SearchActivatedEventArgs args) {
            viewModel.SearchAndSelect(args.QueryText);
        }
 
        private void OnResuming(object sender, object e) {
            // ...statements omitted for brevity...
        }
 
        private void OnSuspending(object sender, SuspendingEventArgs e) {
            // ...statements omitted for brevity...
        }
 
        private void StartLocationTracking(Frame frame) {
            // ...statements omitted for brevity...
        }
 
        private void StopLocationTracking() {
            locationTokenSource.Cancel();
        }
    }
}

That is all that is required to satisfy the obligations of the search contract; by overriding the OnSearchActivated method, I have added the ability for Windows to search my app on behalf of the user.

Testing the Search Contract

To test the contract, start the example app. It doesn’t matter if you start it with or without the debugger. Bring up the charms bar and select the search icon. The example app will be selected as the target of the search automatically. To begin a search, just start typing.

When you press the search button to the right of the textbox, Windows will invoke the search contract and pass the query string to the example app. You want to search for something that will make a match, so type hot (so that your search will match against the hot dogs item in the grocery list) and click the button. You will see something similar to Figure 5-6.

9781430250340_Fig05-06.jpg

Figure 5-6.  Searching the app with a contract

I really like the contract approach. This is a very simple implementation of the search contract, but you can see how easy it is to integrate into Windows with just a few lines of code. You can go well beyond what I have done here and completely customize the way that your app deals with and responds to search.

Summary

In this chapter, I have shown you how to use the life-cycle events to respond to the way in which Windows manages apps. I described the key events and showed you how to respond to them to ensure that you app is receiving and processing them correctly.

Particular care must be taken to cleanly wrap up background tasks when an app is being suspended, and I showed you how to get control of this process by requesting a suspension deferral, allowing an extra few seconds to minimize the risk of potential errors or stale data when the app is resumed.

Finally, I showed you how life-cycle events allow you to fulfill the contracts that bind an app to the wider Windows platform and how easy it is to meet the obligations those contracts specify. I showed you the search contract, but there are several others, and I recommend you take the time to explore them fully. The more contracts you implement, the more integrated your app becomes with the rest of Windows and with other integrated apps.

In this book, I set out to show you the core system features that will jump start your app development. I have shown you how to use data bindings, how to use the major structural controls, how to deal with snapped and filled layouts, how to customize your app’s tile, and, in this chapter, how to take control of the app life cycle. With these skills as your foundation, you will be able to create rich and expressive apps and get a head start on Windows 8 development. I wish you every success in your app development projects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.34.205