Part 18: Navigating Between Pages
When someone clicks the microphone icon in our app bar, we want to take them to a new page where we can allow them to record a custom sound. To do this, we need to create a second page in our app, and then navigate from the MainPage.xaml to that new page. Navigation in Windows Phone Apps is similar to navigating from one web page to another. In this lesson, we'll learn about the Navigation API and, even though our needs are simple in this particular app, we'll find out the Navigation API capabilities with regards to how the Windows Phone 8 operating system re-hydrates apps shut down due to memory constraints.
Our game plan in this lesson ...
- Revisit the Databound Project template to observe how it navigated from the main page to the details page.
- Discuss the classes required for navigation in the Windows Phone 8 API.
- Implement the code necessary to navigate from our main page to a new page that we'll use to record a custom sound.
1. Revisiting the Databound Project template to learn about navigation
If you'll recall a few lessons ago when we looked at the Databound Project Template, we saw how tapping one of the list items ...
... navigates to another page containing more details about the given item:
In addition to the MainPage.xaml, the project template also has a second page called DetailsPage.xaml:
In the MainPage.xaml.cs file, in the MainLongListSelector_SelectionChanged event handler method, we see the code required to enable this. Focus on lines 47-50:
The NavigationService class is used to simply navigate from one XAML page to another. However, it has a larger role in more complex scenarios. Since it is solely responsible for navigation between XAML pages, it also allows you to inspect the navigation history (called the "back stack"), remove entries from the back stack, and then observe the effect that these changes have on the navigation through the app.
To learn about some of the advanced functionality of the NavigationService() class, check out this article on MSDN:
However in our case, we merely need to navigate from the MainPage.xaml to a new page. We don't need to worry about alternate back page scenarios, passing values between the pages, or the like. Therefore, this other article on MSDN describes the process we'll use in general terms, in case you want to do a little additional research:
Most of the simple navigation scenario is accomplished using the the Navigate() method in the code example.
Notice the Uri that is used as an input parameter to the Navigate() method. The Uri object represents a Uniform Resource Indicator, similar to a URL but has more utility and as more far reaching. The Uri has two basic parts—a string that represents a location, and a UriKind that should be used to interpret the location string.
Let's start by parsing through the string ... you'll notice that it is part string literal, part dynamic value that retrieves its value from the current selected item in the LongListSelector. They are appended together with the plus + operator:
"/DetailsPage.xaml?selectedItem=" + MainLongListSelector.SelectedItem as ItemViewModel).ID
The first part of the literal string should be obvious—it is the XAML page we want to navigate to. Everything after that—i.e., after the question mark ? character—is a query string. You've undoubtedly seen a query string before, even if you didn't know what it was called. A query string is a means of sending additional information along with the intended page to be loaded. I said you've undoubtedly seen this before because it is one of two or three primary ways of sending additional data between stateless web pages and is used ubiquitously on the World Wide Web.
If you look at a search for the name "Clint Rutkas" on Bing.com, the navigation bar in your web browser will look like this:
It's simply a clever way of passing information from one web page that's intended to be interpreted and processed by another web page.
In the code example we're examining, when someone taps an item in the LongListSelector we want to send the ID for the item that was selected from the MainPage.xaml to the DetailsPage.xaml ... the ? separates the page name from the query string portion of the URL. The query string is in the form of a name / value pair.
... the selectedItem is the name of the pair, and everything after the equals character is the value of the pair.
Two name / value pairs are separated by an & ampersand character. We'll see this used later in this series to send geo-positional latitude and longitude from one page to another ... it will look like this:
This is how you are able to pass multiple values in one string. Clever.
The second constructor argument for the new Uri takes an enumeration of type UriKind. There are three possible values:
In regards to the differences between the first two, check out this page:
... which says the following:
"Absolute URIs are characterized by a complete reference to the resource (example: http://www.contoso.com/index.html), while a relative Uri depends on a previously defined base URI (example: /index.html)."
In our case, a relative Uri would be "relative to the project's structure". When we prefix the UriString with a forward-slash character / we're specifying the root of the project's deployment package. That should correspond with what we see in the Solution Explorer ... the DetailsPage.xaml is in the root of the project folder. When the project is deployed, those two .xaml files will both be in the root of the package like we saw when we opened up the PetSounds.xap file as a zipped file.
But what about the UriKind.RelativeOrAbsolute? That's a bit trickier. The best I can figure out is that we're simply asking the runtime to figure it out for itself. It will attempt to clean up the UriString we provide and figure out where resources are. I think we could switch that to UriKind.Relative and it would work, too.
Once the NavigationService loads the new page, the DetailsPage.xaml, an event called OnNavigatedTo() fires. Notice its code:
In line 31, the NavigationContext.QueryString.TryGetValue("selectedItem") will retrieve the value of the name / value pair in the query string as an out parameter if the parameter name exists. Next, that selectedItem value is used to load the correct item from the data model, and set it as the DataContext for the DetailsPage.xaml so that the various TextBlock controls can bind to it (line 35).
Now that we have seen a full-fledged navigation example at work, we're better prepared to tackle this in our own project.
2. Create the RecordAudio.xaml Page
Let's begin by creating that new page we want to navigate to:
- Make sure you're in the C# file templates section
- Select "Windows Phone Portrait Page"
- Rename to: RecordAudio.xaml
- Click the Add button
Back in the MainPage.xaml page, we'll revisit the event handler method stub we created several lessons ago called RecordAudioClick():
3. Implement the code to navigate to the new page
I'll replace the exception (intended to be a reminder) with the following line of code:
Hopefully, this version will be dramatically simplified when compared to the hairy example we saw earlier. We create a new Uri object with a simple address to our new RecordAudio.xaml page, and use the UriKind.RelativeOrAbsolute enum value.
Now, let's test that line of code by running the app and clicking the microphone icon in the app bar:
... if all goes well, we should see a default page template like so:
Great! We'll need to revisit this notion later in this series of lessons with a slightly more complex example in which we pass data between the pages.
To recap, the big take away from this lesson is how to navigate between XAML pages using the NavigationService. We learned what a Uri object is, how to specify a page location and even pass parameters between pages, what the UriKind enumeration options mean, and so on.