This article is Day #13 in a series called 31 Days of Windows 8. Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.
We have spent the last few days getting pretty technical around updating the system with information to the let user know what’s going on. In fact, in the first 12 days of this series, we’ve spent very little time talking about how to do one of the most common activities we’ll encounter in Windows 8 development: navigating between XAML pages.
Today, we’ll right that wrong by diving in to this subject. There are three specific scenarios I want to make sure we cover in this article:
- The simple act of navigation from Page A to Page B. What happens when, and what information is available to us?
- Passing data from one page to another. This doesn’t just include strings and integers, we can also pass entire objects between pages.
- Caching pages. When a user clicks the back button, they don’t want to discover that all of the data they entered has disappeared. We’ll discuss this more later.
Navigating Between Pages in Windows 8 XAML
It seems that every time we encounter a new platform, there’s also a new way to navigate between pages in a XAML application. Windows 8 is no different in that regard. This process is easily explained by walking you through creating some pages, so let’s start there.
Create a new Blank App Template, and then add two new Basic Page items to it. For this example, you can call them PageA.xaml and PageB.xaml.
In each of those pages, there is a line of XAML that sets the AppName.
<Page.Resources>
<x:String x:Key="AppName">My Application</x:String>
</Page.Resources>
Normally, we would move this to our App.xaml file, and let it apply to all of the pages in our application. For today, we’re going to use this simple mechanism to give each of our pages a distinct name that we can see when it is loaded. Set the value of PageA.xaml’s AppName to “Page A” and PageB.xaml’s AppName to “Page B” so that your pages look like this:
To make your sample easy to use, we’re also going to make a quick change to our App.xaml.cs file. Near the bottom of the OnLaunched() method in App.xaml.cs is some code that looks like this:
if (rootFrame.Content == null)
{
if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
}
You will want to change the reference from typeof(MainPage) to typeof(PageA). Like this:
if (rootFrame.Content == null)
{
if (!rootFrame.Navigate(typeof(PageA), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
}
This will cause your application to start up with the initial page being PageA.xaml. At this point, go ahead and run your project. You should see it start with the Splash Screen, and then PageA should load. You should also notice that even though our preview in Visual Studio 2012 shows an arrow icon next to our page title, it is bound to whether or not the page is capable of “going back” with the Frame.CanGoBack property. We’ll talk more about that a little later.
For now, let’s get to navigating. You’ve technically already seen it in our App.xaml.cs file, but it looks a little different when you’re just going from page to page. I’ve added a <Button> control to my PageA.xaml file, right before the <VisualStateManager> section. The XAML for that looks like this:
<Button x:Name="PageBButton"
Grid.Row="1"
Content="To Page B"
Width="150"
Height="150"
Click="PageBButton_Click" />
In the event handler for that <Button>, we write our first navigation statement (finally!)
private void PageBButton_Click(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(PageB));
}
As you can see, it’s a pretty simple process. So let’s make it a little more complicated now, by adding a navigation button to Page B. This might seem like a repetitive, simple task, but you’ll find that we can get into trouble rather quickly. (You should run your project at this point to make sure that you actually navigate to your Page B file.) Add the same type of <Button> and event handler to PageB, but obviously point it at PageA this time. Now, you have a button on each page that points to the other. If you only use those buttons to do your navigation, you end up creating a HUGE back navigation stack, and it makes any concept of a Back button completely irrelevant. What we need to do in this case is to implement the GoHome event handler which is part of the LayoutAwarePage object that we’re using. To effectively illustrate this example, add a second button to your PageB.xaml, with an accompanying new event handler. In this event handler, however, we’re not going to navigate to a page. Instead, we’re going to create the GoHome event, and call that. Here’s how that looks:
protected override void GoHome(object sender, RoutedEventArgs e)
{
if (this.Frame != null)
{
while (this.Frame.CanGoBack) this.Frame.GoBack();
}
}
private void HomeButton_Click(object sender, RoutedEventArgs e)
{
base.GoHome(sender, e);
}
As you can see, our GoHome method runs a quick loop attempting to “GoBack” until it can’t anymore. At that point, we end up back at the top of the navigation stack, on our initial page. It’s a nice trick, but I think all of this is best illustrated with a quick video:
Passing Data Between XAML Pages in Windows 8
Next, we’re going to want to pass data between our pages. Maybe an object, maybe a string, but data nonetheless. As it turns out, our Frame.Navigate() method is overloaded to also accept a data parameter. For this sample, I’ve added a TextBox and a Button to my PageA, but at this point, I’m going to assume you know how to do that. The important part of this is where I have an event handler that pulls the text value out of the TextBox and sends it as part of the Navigate() method. Like this:
private void GoButton_Click(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(PageB), TextBoxValue.Text);
}
On the other side, on PageB, we need to catch this data and display it. Again, I’ve added a TextBlock in my sample code (which you can download at the end of this article), but the important piece is my new OnNavigatedTo event handler, and catching our data as it arrives.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string text = e.Parameter as string;
if (!string.IsNullOrWhiteSpace(text))
{
TextBlockValue.Text = text;
}
else
{
TextBlockValue.Text = "You need to pass a string. Press back and enter a value.";
}
}
In PageB, we can grab the passed value from NavigationEventArgs.Parameter, where I am casting it as a string value. You will need to cast your value to the appropriate type before you can use it, but once you have, you’ve tossed data from one page to the next!
In addition, I mentioned earlier that you can send complete objects using this mechanism as well. Remember our Element class from way back in Day #4? I’m going to pass a custom Element object from PageA to PageB in the next example. Here’s PageA’s code:
private void Gallium_Click(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(PageB), new Element { AtomicNumber = 31,
AtomicWeight = 69.72,
Category = "Other Metals",
Name = "Gallium",
Symbol = "Ga",
State = "Solid" });
}
You can clearly see that we’re passing a brand-new Element object to PageB. Here’s PageB catching that data:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Element gallium = e.Parameter as Element;
TextBlockValue.Text = gallium.Category;
}
Caching Pages For Back Button Performance
The last thing we want to look at related to navigation is page caching. For this final example, I am using our sample application in the state where I was passing string values back and forth. You may have noticed, if you built your own sample app, that if you type something in the TextBox on PageA, and send that data to PageB, when you click the Back button to return to PageA, it doesn’t remember what you typed in the box. This is an incredibly frustrating user experience pattern that you see on the web all the time.
We can avoid this with one simple line in the constuctor of the pages we want to cache:
public PageA()
{
this.InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
After you set the NavigationCacheMode, you will find that your TextBox values (and everything else on your page) will be cached in the back stack. Give it a try…it’s a much better experience, and doesn’t require us to populate and repopulate the form every time the user returns to the page.
Summary
This was an extensive overview of navigation in XAML apps for Windows 8. We covered moving between pages, passing data between pages, and caching those pages for when the user clicks the back button.
If you would like to download the complete code sample from this article, you can click the icon below. (Please note that the Gallium button is currently disabled, you’ll need to uncomment the event handler for that button, as well as the code in PageB.xaml.cs to make that work. PageB can only accept one type of parameter at a time, and it’s currently set up for the string.)
Tomorrow, we’re going to start our conversation determining a user’s current location using GeoLocation, including the rules that go with using this technology. See you then!
Leave a Reply