Windows Phone 7 Quick Tutorials: Part 3 - Page Navigation

In the previous articles (part 1 and part 2) we've seen how to create a simple WP7 application and how to use the application toolbar. But any serious application will probably require more than one page. This article is focused on navigating and passing data between pages.

Creating a Simple Application

Let's start by creating a simple Silverlight application (if you followed the first articles in the series, this should be already familiar to you). This application will have a simple main page with an empty grid as the content panel. The only thing we'll do is change the background color for the grid. However, we'll select the color in a second page that will display several radio buttons with the available colors. The application will have to pass the current color from the main page to the second page, and then back.

The image below shows how to add a new portrait page. We'll call this page, SettingsPage. You'll notice that the page the wizard adds is identical with the main page initially created with the project.

WP7 wizard

The main page will have an app bar with a single button, called Settings. When pressing this button the second page will be displayed. The button will use the appbar.feature.settings.rest.png image available with the WP7 Developers Tools. After adding it to the Images folder in your project, remember to set the Build Action to Content, otherwise it will not be packaged and deployed to the phone. You can read more about this in the previous article of the series. This is how the XAML code for the app bar on the main page looks like:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.settings.rest.png" 
                                        Text="color"
                                        x:Name="btnSettings"
                                        Click="btnSettings_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

The button has a handler for the Clicked event, and in the handler we'll navigate to the settings page. We'll get into the details in the next paragraph.

private void btnSettings_Click(object sender, EventArgs e)
{
   this.NavigationService.Navigate(new Uri("/SettingsPage.xaml", UriKind.Relative));
}

The settings page will have a group of radio buttons representing available colors for the main's page grid background. The content panel in this page will look like this (for the moment):

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <RadioButton Content="Black" />
        <RadioButton Content="White" />
        <RadioButton Content="Red" />
        <RadioButton Content="Yellow" />
        <RadioButton Content="Blue" />
        <RadioButton Content="Green" />
    </StackPanel>
</Grid>

This page will have its own app bar, again with a single button, which we'll call Select. When pressing this button the application will return to the main page and change the grid background color. The XAML code for this app bar is:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar.check.rest.png" 
                                        Text="select"
                                        x:Name="btnSelect" 
                                        Click="btnSelect_Click" />
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

To navigate back we have to handle the Clicked event and use the navigation service to navigate back.

private void btnSelect_Click(object sender, EventArgs e)
{
    this.NavigationService.GoBack();
}

This is how the two pages of the application will look at this point.

how the two pages of the WP7 application will look

Navigating Through the Page Stack

In the previous paragraph I've used a property of the Page class (from which PhoneApplicationPage is derived) called NavigationService. This is an object of the type NavigationService (yes, same name) that provides several methods for navigating through the pages of the application:

  • Navigate: Navigates to the content specified by the uniform resource identifier.
  • GoBack: Navigates to the most recent entry in the back navigation history.
  • GoForward: Navigates to the most recent entry in the forward navigation history.
  • StopLoading: Stops asynchronous navigations that have not yet been processed.

Method Navigate() takes the Uri of a XAML page and navigates to it. The application has a stack of pages. Initially when the application starts, the stack contains the main page. When you call Navigate() a new instance of the specified page is created and placed on top of the stack. Methods GoBack() and GoForward() allows you to navigate through the existing pages on the stack. In the main page, when we want to select a new color, we create a new instance of the settings page. But from the settings page, when we want to return to the main page, we don't call Navigate, because this would create a new instance of the main page and place it on the stack. We call GoBack and return to the already existing instance of the main page.



Related Articles

Windows Phone 7 Quick Tutorials: Part 3 - Page Navigation

Selecting the Appropriate Radio Button

In order to select one of the radio buttons in the group I could write a series of if-elses or maybe a switch. Though that might be the simplest solution, it's not necessary the most elegant. In Silverlight we can do something rather automatic, and I'll show how. However, this is not something specific for WP7, so you could skip this part.

I'll start by creating an enumerator, called ColorOption that defines the available colors the user can select.

public enum ColorOption
{
    Black,
    White,
    Red,
    Yellow,
    Blue,
    Green
}

Then I'll create a value converter to convert from this enumerator to bool and the other way around.

public class EnumBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || parameter == null)
            return value;

        return value.ToString() == parameter.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || parameter == null)
            return value;

        return Enum.Parse(targetType, (String)parameter, true);
    }
}

An implementation of INotifyPropertyChange will be used for the binding mechanism. This view model will be the data context for the settings page.

public class ColorOptionViewModel : INotifyPropertyChanged
{
    private ColorOption _userOption;
    public ColorOption UserOption
    {
        get
        {
            return _userOption;
        }
        set
        {
            if (_userOption != value)
            {
                _userOption = value;
                RaisePropertyChanged("UserOption");
            }
        }
    }  

    public virtual void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Then, in the XAML code of the second page, we'll have to add two things: first, the enum-to-bool converted as a static resource in the page:

<phone:PhoneApplicationPage.Resources>
     <local:EnumBoolConverter x:Key="ConvertEnum" />
</phone:PhoneApplicationPage.Resources>

Second, we'll alter the definition of the radio buttons to look like this:

<StackPanel>
    <RadioButton Content="Black" 
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Black}" />
    <RadioButton Content="White" 
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=White}" />
    <RadioButton Content="Red" 
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Red}" />
    <RadioButton Content="Yellow"
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Yellow}" />
    <RadioButton Content="Blue"
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Blue}" />
    <RadioButton Content="Green"
                 IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Green}" />
</StackPanel>

With this in place, we'll be able to use ColorOptionViewModel as data context for the page and do the following:

// in the constructor
this.DataContext = new ColorOptionViewModel();

// anywhere in the page
(DataContext as ColorOptionViewModel).UserOption = (ColorOption)colorindex;

The radio button corresponding to the specified color will be selected automatically.



Related Articles

Windows Phone 7 Quick Tutorials: Part 3 - Page Navigation

Passing Data Between Pages

Having all this in place, the next thing we have to do is passing data between pages. There are several methods to achieve this, and in this article we'll see two of them.

The first way is to use the Uri passed to the Navigate method of the NavigationService. We can pass arguments with the query string of the URI. For instance, giving that we store the current selected color in a variable called m_option, we can pass this value to the settings page like this:

this.NavigationService.Navigate(new Uri(
    String.Format("/SettingsPage.xaml?Color={0}", (int)m_option), 
    UriKind.Relative));

The Uri string will look like "/SettingsPage.xaml?Color=1". The page that is navigated to will be able to query for these values using the NavigationContext property of the Page class. The following example shows how to do it.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    IDictionary<string, string> parameters = this.NavigationContext.QueryString;
    if (parameters.ContainsKey("Color"))
    {
        int colorindex;
        int.TryParse(parameters["Color"], out colorindex);
        // do something with the value
    }

    base.OnNavigatedTo(e);
}

Method OnNavigatedTo is called by the framework after the page is created and before it is displayed, to allow you to examine the navigation request (the arguments passed through the query string) and prepare the page for display. This method is called for each request, even when the page is retrieved from the cache. So if you have code that must be executed for each request it should be placed here, not in the constructor.

There are also two method called OnNavigatingFrom and OnNavigatedFrom. The first is called just before a page becomes inactive, while the second is called after the page has become inactive.

That being said, this is how the complete code for the MainPage and SettingsPage looks like:

public partial class MainPage : PhoneApplicationPage
{
    ColorOption m_option;

    // Constructor
    public MainPage()
    {
        InitializeComponent();
        m_option = ColorOption.Green;
    }

    private void btnSettings_Click(object sender, EventArgs e)
    {
        this.NavigationService.Navigate(new Uri(
            String.Format("/SettingsPage.xaml?Color={0}", (int)m_option), 
            UriKind.Relative));
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        Color color = Colors.Black;
        switch(m_option)
        {
            case ColorOption.Black: color = Colors.Black; break;
            case ColorOption.White: color = Colors.White; break;
            case ColorOption.Red: color = Colors.Red; break;
            case ColorOption.Yellow: color = Colors.Yellow; break;
            case ColorOption.Blue: color = Colors.Blue; break;
            case ColorOption.Green: color = Colors.Green; break;
        }

        ContentPanel.Background = new SolidColorBrush(color);
    }
}

Notice that in the MainPage we handle the Loaded event, and set the appropriate brush for the grid background, based on the user selection stored in the m_option variable.

On the other hand, in the OnNavigatedTo method of the SettingsPage, after reading the value from the query string we set it to the UserOption property of the view model that represents the DataContext. Due to the bindings we put earlier in the XAML code, the appropriate radio button will be selected.

public partial class SettingsPage : PhoneApplicationPage
{
    public SettingsPage()
    {
        InitializeComponent();
        this.DataContext = new ColorOptionViewModel();
    }

    private void btnSelect_Click(object sender, EventArgs e)
    {
        this.NavigationService.GoBack();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        IDictionary<string, string> parameters = this.NavigationContext.QueryString;
        if (parameters.ContainsKey("Color"))
        {
            int colorindex;
            int.TryParse(parameters["Color"], out colorindex);

            (DataContext as ColorOptionViewModel).UserOption = (ColorOption)colorindex;
        }

        base.OnNavigatedTo(e);
    }
}

Suppose initially the selected color was green (as seen in the main's page constructor), the selected radio button when loading the settings page will be the last one, representing Green.

[WP7navigation_3.png]

As stated earlier in this paragraph, this method has an important drawback: it's only one way. We cannot pass parameters with the GoBack() method when navigating to the previous page on the stack. This method does not take an Uri, so we cannot provide a query string. We cannot call Navigate either, because that would create a new instance of the main page. So we need an alternative way for passing data between pages.

Using Application's State

A more flexible way is based on the application's state. If you look in the XAML code for your App object, you'll notice the following part:

<Application.ApplicationLifetimeObjects>
    <!--Required object that handles lifetime events for the application-->
    <shell:PhoneApplicationService 
        Launching="Application_Launching" Closing="Application_Closing" 
        Activated="Application_Activated" Deactivated="Application_Deactivated"/>
</Application.ApplicationLifetimeObjects>

PhoneApplicationService is a class that provides access to aspects of the application's lifetime, including the management of the idle behavior and the application's state when it becomes active or inactive. One of its members is a property called State, which is a dictionary of object values with string as the key type. This dictionary can be used for storing transient data, i.e. data available only while the application is running. You cannot store data that must be available between different executions of the application. We'll discuss about that in a future article. One requirement for the objects stored in this dictionary is that they are serializable.

We can do the following to pass the color between the main page and settings page:

  • In the Clicked handler for the Settings button in the main page, put the current color in the State dictionary
  • In the OnNavigatedTo method of the settings page, extract the value from the dictionary and set it in the view model representing the DataContext

To pass the new value back from the settings page to the main page we have to do these steps:

  • In the Clicked handler for the Select button of the settings page app bar, put the selected color in the State dictionary
  • In the OnNavigatedTo method of the main page, extract the color from the dictionary and store it in the m_option variable (that we've seen earlier).

The new code for the two pages will be:

public partial class MainPage : PhoneApplicationPage
{
    ColorOption m_option;

    public MainPage()
    {
        InitializeComponent();
        m_option = ColorOption.Green;
    }

    private void btnSettings_Click(object sender, EventArgs e)
    {
        PhoneApplicationService.Current.State["Color"] = m_option;
        this.NavigationService.Navigate(new Uri("/SettingsPage.xaml", UriKind.Relative));
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        object obj;
        if (PhoneApplicationService.Current.State.TryGetValue("Color", out obj))
        {
            m_option = (ColorOption)obj;
        }

        base.OnNavigatedTo(e);
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        Color color = Colors.Black;
        switch(m_option)
        {
            case ColorOption.Black: color = Colors.Black; break;
            case ColorOption.White: color = Colors.White; break;
            case ColorOption.Red: color = Colors.Red; break;
            case ColorOption.Yellow: color = Colors.Yellow; break;
            case ColorOption.Blue: color = Colors.Blue; break;
            case ColorOption.Green: color = Colors.Green; break;
        }

        ContentPanel.Background = new SolidColorBrush(color);
    }
}
public partial class SettingsPage : PhoneApplicationPage
{
    public SettingsPage()
    {
        InitializeComponent();
        this.DataContext = new ColorOptionViewModel();
    }

    private void btnSelect_Click(object sender, EventArgs e)
    {
        PhoneApplicationService.Current.State["Color"] = (DataContext as ColorOptionViewModel).UserOption;
        this.NavigationService.GoBack();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        object obj;
        if (PhoneApplicationService.Current.State.TryGetValue("Color", out obj))
        {
            (DataContext as ColorOptionViewModel).UserOption = (ColorOption)obj;
        }

        base.OnNavigatedTo(e);
    }
}

Now, when we run the application, after selecting a color and pressing the Select button in the settings page, the application will navigate back to the main page and the background color of the grid will change to the selected color. The following image shows how it works.

[WP7navigation_4.png]

Conclusions

Real WP7 Silverlight application will probably require more than just one page. We can navigate between them using the NavigationService property of a page. The pages are stacked in the order of navigation and this service allows you to navigate back and forward through the stack. Passing data between pages is possible in several ways, and in this article we've seen how to use the query string of a Uri and the application's State dictionary.

Related Articles



About the Author

Marius Bancila

Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for a Norwegian-based company. He is mainly focused on building desktop applications with MFC and VC#. He keeps a blog at www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++/VC++ programmers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds