1. Navigation
Your application does not have a window. This
is an important indication that, although it is called Windows Phone,
the “Windows” in the name is not an indication that Windows is
the operating system. You will have to get used to a different style of
development. The entire application model is built around the concept of
page-based navigation, which should be comfortable to any user of the
Web.
When you start an application, it navigates to a
particular page (as dictated in the WMAppManifest.xml file’s default
task). As the user navigates to other pages, the stack of pages grows.
The user can press the Back button on the phone to return to the last
page within your application. If the user is at the first page of your
application, pressing the Back button will exit your application, as
shown in Figure 3.
FIGURE 3 Page navigation explained
If you need to explicitly exit your application, you can use the Application.Terminate
method to do so. Although pressing back on the first page of your application will terminate the application, the Application.Terminate
method will enable you to specify exit anywhere in the application that makes logical sense.
Programmatically, you can interact with the navigation facility using the NagivationService
class. To get access to the application’s instance of the NavigationService
class, you have two options to find the navigation APIs:
• NavigationService
property on classes that derive from PhoneApplicationPage
(for example, MainPage.xaml or other “Page” project items added to your project).
• PhoneApplicationFrame
class, which has all the navigation functionality of the NavigationService
class. This frame is ordinarily a member of your application’s App
class.
Although they are functionally the same, these
two implementations do not depend on a common base class or an interface
to enforce them. They are identical by convention only.
The NavigationService
class has a number of useful methods, but the most common usage includes Navigate
and GoBack
. The Navigate
method starts a navigation to a new “page,” like so:
NavigationService.Navigate(new Uri("/Views/SecondPage.xaml",
UriKind.Relative));
// or
NavigationService.Navigate(new Uri("/Views/SecondPage.xaml?id=123",
UriKind.Relative));
The URI in the Navigate
call specifically searches for a file in the .xap file, and this is typically mimicked by your project structure, as shown in Figure 4.
FIGURE 4 URI mapping to the files in the project
HyperlinkButton
s are automatically wired to use the NavigationService
, so this works fine in your XAML:
<HyperlinkButton NavigateUri="/Views/SecondPage.xaml"
Content="Go to 2nd Page" />
Conversely, the NavigationService
class’s GoBack
method goes back to the top page on the navigation page stack (or back
stack, explained in the next paragraph). This method mimics the user
pressing the Back button:
NavigationService.GoBack();
As you navigate through an application, this NavigationService
class keeps track of all the pages, so GoBack
can walk through the pages as necessary. This stack of pages is called the back stack. The NavigationService
provides read-only access to the back stack by providing a property:
IEnumerable<JournalEntry> backStack = NavigationService.BackStack;
The backStack
property enables you to iterate through the back stack and interrogate the source of each page, but not change it:
// Iterate through the BackEntries
foreach (var entry in NavigationService.BackStack)
{
Uri page = entry.Source;
}
The only change that the NavigationService
allows is to remove the last entry in the backStack
. You can do this with RemoveBackEntry
, which removes only the current page from the backStack
:
// Remove the last page from the navigation
NavigationService.RemoveBackEntry();
When navigation occurs (even at the start of an
application), the class that represents the page has an opportunity to
know it is being navigated to. This is implemented as overrideable
methods on the PhoneApplicationPage
class. The first of these overrideable methods is the OnNavigatedTo
method:
public partial class MainPage : PhoneApplicationPage
{
// ...
// I was just navigated to
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var uri = e.Uri;
}
}
Additionally, there are overrideable methods for navigating away from a page (OnNavigatingFrom
and OnNavigatedFrom
):
public partial class MainPage : PhoneApplicationPage
{
// ...
// Navigation to another page is about to happen
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
}
// Navigation to another page just finished happening
protected override void OnNavigatedFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatedFrom(e);
}
}
The NavigationService
also has events if you want to react to changes in the navigation as they happen, including Navigating
, Navigated
, and NavigationFailed
. These events can be useful in globally monitoring navigation.
As shown earlier, the URI you navigate to can contain query string information. You could get the URI in your OnNavigatedFrom
method and try to parse the query string manually, but this is unnecessary. The NavigationContext
class supports simple access to the query string. For example, if you
wanted to get the ID from the query string, you could simply use the NavigationContext
instead:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.ContainsKey("id"))
{
var id= NavigationContext.QueryString["id"];
// Use the id
}
}
By using the NavigationService
, NavigationContext
, and PhoneApplicationPage
classes, you can control the navigation of different parts of your
application as well as give users a more intuitive experience.
During the OnNavigatedTo
method,
you can also discern the mode of the navigation. This can help determine
how to deal with the navigation to a particular page. The mode of the
navigation has to do with how the page is being launched. If the page
was reached by pressing back on a deeper page in the navigation stack,
you can think of the navigation as being a “back” navigation. The NavigationEventArgs
object that is passed into the OnNavigatedTo
method contains the mode of the navigation, as shown here:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode == NavigationMode.Back)
{
// Get changes from other page
}
else if (e.NavigationMode == NavigationMode.Forward)
{
// Initialize for a clean navigation
}
}
You can also check for NavigationMode
from within the OnNavigatingFrom
method:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
if (e.NavigationMode == NavigationMode.Back)
{
// Handle Back
}
}
In addition to the mode, you can determine
whether your application initiated a particular navigation. This is
useful for when you want to see whether the navigation to a particular
page started from inside your application or elsewhere (for example,
from a deep link). You can test this by checking the IsNavigationInitiator
property both in the OnNavigatedTo
and the OnNavigatingFrom
methods. For IsNavigationInitiator
to be true, both the origin of the navigation and the destination must
both be within the application. If you are navigating from a shortcut or
navigating to an external app (that is, a web browser), this property
will be false:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.IsNavigationInitiator)
{
MessageBox.Show("We initiated the navigation");
}
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
if (e.IsNavigationInitiator != true)
{
// Prepare to head outside the app
}
}
When you are handing the OnNavigatingFrom
method, there are times when this navigation is cancellable. You could imagine if a user isn’t done filling out a form or data and you don’t want him to lose what he’s filled in. The NavigatingCancelEventArgs
parameter contains a couple of properties that will let you see whether
a navigation is cancellable and enable you to mark it as cancelled if
necessary, as shown:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
if (e.IsCancelable)
{
e.Cancel = true;
return;
}
}
Setting the Cancel
property to true is valid only if the IsCancelable
property is also true. You could imagine that some navigations will not
support cancellation such as the user hitting the Windows button or
answering a phone call. Those types of operations are always given
precedence to your application’s requirements.