Mobile Corner
Page Navigation with Windows Phone and Windows 8
Nick Randolph continues to look at building applications across both Windows Phone and Windows 8, this time focusing on the navigation model.
- By Nick Randolph
- 04/24/2012
In my previous column, "Converting Windows Phone to Windows 8," I created a simple Windows Phone application that consumed an XML feed from Flickr and displayed a list of images. Then I showed you how to port that application, with a relatively high level of reuse, to Windows 8. This time, I'll look at extending the Windows Phone application to multiple pages, and focus on code compatibility and the navigation model. As a disclaimer, this is a discussion of code reuse rather than a demonstration of good UI design.
Multiple Pages in Windows Phone
The original Windows Phone application had a single page, so I kept the structure as simple as possible, creating the MainPageViewModel alongside the page by defining it as a page resource, and then wiring it up as the DataContext for the page. When you extend the application out to multiple pages, you need to think a bit more about how to reuse code between those pages.
For the purposes of this article, I'm going to add a second page, ImagePage.xaml, and its corresponding view model, ImagePageViewModel. When users tap on an image on the MainPage, the app takes them to the ImagePage to see a larger version of the image. If you haven’t come across the concept of a view model, you can think of it as the current state of the corresponding view (page). In this case, the MainPageViewModel has a property, FlickrImages, which returns a collection of the images currently displayed on the MainPage. The ImagePageViewModel contains a single property, Image, which returns the image displayed on the ImagePage.
Instead of duplicating the code that retrieves the XML feed from Flickr into both of the view models, I'm going to create another class called Repository, which both view models can access. The Repository will have a Load method that retrieves the list of photos from the Flickr XML feed, as shown in Listing 1. You’ll recognize most of this code from the original application (in the previous article). One addition I’ve made here is that if the Load method has already been called (Images !=null ), the existing Images value is returned, thus preventing multiple calls to the Flickr feed.
In order for both view models to access the same Repository (singleton), I'm going to create a class called ViewModelLocator. This class is responsible for creating instances of the view models as required and supplying them with the Repository singleton.
public class ViewModelLocator
{
public Repository Repository { get; private set; }
public ViewModelLocator()
{
Repository=new Repository();
}
public MainPageViewModel Main
{
get { return new MainPageViewModel(Repository); }
}
public ImagePageViewModel Image
{
get { return new ImagePageViewModel(Repository); }
}
}
Let’s start connecting these various classes up to the Windows Phone application, beginning with the ViewModelLocator, which you can create an instance of as an application resource in App.xaml.
<Application ... x:Class="FlickrImages.App">
<Application.Resources>
<local:ViewModelLocator x:Name="Locator" />
</Application.Resources>
...
</Application>
Next, you wire up the DataContext of both MainPage and ImagePage, respectively.
<phone:PhoneApplicationPage x:Class="FlickrImages.MainPage"
DataContext="{Binding Main,Source={StaticResource Locator}}" ... >
<phone:PhoneApplicationPage x:Class="FlickrImages.ImagePage"
DataContext="{Binding Image,Source={StaticResource Locator}}" ... >
Within the ImagePage, there's a TextBlock that's data bound to the Title property and an Image that's data bound to the ImageUrl property, of the specified Image, as illustrated in the following XAML snippet:
<StackPanel Grid.Row="0"
Margin="12,17,0,28">
<TextBlock Text="FLICKR"
Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock Text="{Binding Image.Title}"
Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}" />
</StackPanel>
<Image Grid.Row="1"
Margin="12,0,12,0"
Source="{Binding Image.ImageUrl}" />
The only other difference to MainPage is that an event handler has been wired up to the SelectionChanged event of the ListBox used to show the list of images.
private void ImageSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var lst = sender as ListBox;
if(lst==null) return;
var selection = lst.SelectedItem as FlickrImage;
if(selection==null)return;
lst.SelectedIndex = -1;
NavigationService.Navigate(
new Uri("/ImagePage.xaml?title=" + HttpUtility.UrlEncode(selection.Title),
UriKind.Relative));
}
The last line of the ImageSelectionChanged method initiates navigation to ImagePage and supplies the title of the selected FlickrImage as a URL-encoded parameter. Within the OnNavigatedTo method on the ImagePage, this parameter can be extracted and used to determine which image to display.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var title = NavigationContext.QueryString["title"];
(DataContext as ImagePageViewModel).Load(title);
}
The loading of the image has been abstracted out of the OnNavigatedTo method of the ImagePage, into the ImagePageViewModel, to make it more reusable, as shown in Listing 2.
And that completes the navigation within the Windows Phone application. When you run the application, you can tap on one of the images to open the ImagePage for a closer look at the image, as shown in Figure 1.
[Click on image for larger view.] |
Figure 1. Windows Phone Page Navigation |
Page Navigation in Windows 8
Switching to Windows 8, you’ll find that things are not too different. The first thing you’ll need to do is to make sure that your project references both view models (MainPageViewModel and ImagePageViewModel), Repository and ViewModelLocator. Remember, when you add these references, use the Add as Link option to ensure both your Windows Phone and Windows 8 applications reference the same files.
Next, you’ll need to create a second page. Again, call it ImagePage. As with Windows Phone, you'll need to create elements in order to display the title and the image of the selected Flickr image, as shown in Listing 3.
As with the Windows Phone application, the DataContext for the page is linked to the Image property of the Locator static resource. What is currently missing is the application level instance of the ViewModelLocator class. The following code, found in App.xaml, illustrates that the instance of this class is created in the same way as the Windows Phone application, and can then be referenced from any page within the application.
<Application
x:Class="MetroFlickrImages.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlickrImages">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<local:ViewModelLocator x:Key="Locator" />
</ResourceDictionary>
</Application.Resources>
</Application>
As with the Windows Phone application, you need to add an event handler to the SelectionChanged event on the ListBox on the first page of the application (BlankPage.xaml). This code looks almost identical to the event handler in the Windows Phone application, except for the last line that navigates to the ImagePage.
private void ImageSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var lst = sender as ListBox;
if (lst == null) return;
var selection = lst.SelectedItem as FlickrImage;
if (selection == null) return;
lst.SelectedIndex = -1;
Frame.Navigate(typeof(ImagePage), selection.Title);
}
The difference here is that instead of an Uri with embedded string parameter, the Navigate method on the Frame takes a Type, to work out which page to navigate to, and an object, as a parameter. In the OnNavigateTo method on the ImagePage, this parameter can be extracted to work out which image to display.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var title = e.Parameter as string;
(DataContext as ImagePageViewModel).Load(title);
}
private void BackTapped(object sender, TappedRoutedEventArgs e)
{
Frame.GoBack();
}
The second method, BackTapped, is actually an event handler for the Tapped event (when the user taps the element) on a TextBlock that was added to the ImagePage. Unlike Windows Phone in which every device has a mandatory Back button to allow the user to navigate back to the previous page, there is no such standard within Windows 8. As a result, it's up to you as the developer to provide visual cues to the user as to how to return to the previous page, in this case via a text element that says “Back.”
As you’ve seen, you can reuse a significant portion of a Windows Phone application in a Windows 8 application. While there are differences in the platform APIs, you can still get a high level of reuse by simply refactoring your code to ensure the differences are exposed as platform-specific code (for example, the navigation is done within the page code-behind that is specific to each platform).
About the Author
Nick Randolph runs Built to Roam, a consulting company that specializes in training, mentoring and assisting other companies build mobile applications. With a heritage in rich client applications for both the desktop and a variety of mobile platforms, Nick currently presents, writes and educates on the Windows Phone platform.