The navigation framework was first introduced in Silverlight 3. This framework is based on the concept of having a frame that displays pages and takes care of the whole navigating process from one page to another. The navigation framework supports many of the modern concepts of navigation, such as back and forward navigation, journal histories, and deep linking. The easiest way to create an application that uses the navigation framework is to select the Silverlight Navigation Application template while creating a new Silverlight project. Go ahead and create a new Silverlight project with the Silverlight Navigation Application template and name it Chapter2-Navigation.
As you look at the solution explorer, you may notice that the structure of this application is different from the one we are used to. It has a Views directory, which holds several XAML files, and the MainPage.xaml
file already has some code inside of it. In a navigation application, the MainPage.xaml
file, which we are used to working with as the main page of our application is nothing more than a Frame
control and a Grid
control to host the upper banner. You can think of this upper banner area as an ASP.NET master page—no matter what page you navigate to on your site, the upper part stays the same, so your application has a consistent feeling and style among all its pages. Before we explore the new MainPage.xaml
page, go ahead and build the application. Once you have finished, run the application, and you should get the result, as shown in the following screenshot:
A few things to notice about this application are as follows:
Click on the about button on the right-hand side of the navigation menu. You should see how the ending of the URL now points to the about page and the title in the browser tab changes to about. If we click on the browser's back button at this point, we will go back to the previous page we watched (home) just like any regular website. As you can see from this simple example, Silverlight's interaction with the browser navigation is quite tight!
Currently our application has only two pages—about and home. For the vast majority of cases, you will want to add additional pages to the application containing additional logic and information. Adding a page to the application requires two steps:
view
) to the Views
folder.While the first step is as easy as adding a new item, the second step involves some work on the MainPage.xaml
file.
To add a new page to your application, right-click on the Views directory, click on Add, and then click on New Item.
In the New Item dialog box, select the Silverlight Page template and name it NewPage.xaml. The page template derives from the navigation:Page
class, and in its core is UserControl
with some added support for the navigation framework.
At this moment, our new page is nothing more than a blank grid, but we will get back to it later. Right now, our users have no way to reach the new page, and in order to solve that issue, we will use a control we discussed before, the HyperlinkButton
control.
Open up MainPage.xaml
and look for the LinksBorder
border control. As you can see, we have two HyperlinkButton
controls, and a rectangle, which acts as the divider between the two buttons. To add a new button here, all we have to do is add another HyperlinkButton
and Divider
. Add the following code snippet just below the Divider1
rectangle:
<HyperlinkButton x:Name="NewPageLink" Style="{StaticResource LinkStyle}" NavigateUri="/NewPage" TargetName="ContentFrame" Content="new page"/> <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
There are a few interesting things to notice about our newly added HyperlinkButton
as follows:
NavigateUri
property points to /NewPage
when the actual page is stored at /Views/NewPage.xaml
. This is handled by UriMapper
, which we will discuss in a bit. TargetName
property points to ContentFrame
. We've mentioned this property when we discussed the HyperlinkButton
control earlier in the chapter, and here you can see that other than the usual _self
or _blank
values, it can also accept a Frame
control name. If you scroll up a bit in the MainPage.xaml
file, you'll see a Frame
element named ContentFrame
that is used to host the pages. Style
property, although not limited to the navigation framework, is used to style the different components of the page. We will discuss styling in Silverlight later on in this book.Build and run your application, and you should see our new HyperlinkButton
control in the navigation bar. Clicking on the new HyperlinkButton
control will switch the active viewing page to our newly created NewPage.xaml
.
The navigation:Page
class includes four navigation-related events, as shown in the following table:
Event name |
Description |
---|---|
|
This event triggers when a fragment inside the application is navigated to. An example for such a case would be |
|
This event triggers when a page is no longer the active page in the frame. This event is usually used when you wish to perform a final cleanup for a page. |
|
This event triggers just before a page is swapping with another page. If the criteria you specify isn't met, you can use this event to cancel navigation to a page or remind the user to save the content before continuing. |
|
This event triggers when a page becomes the active page in a frame. This event is usually used instead of the |
If you take a look at MainPage.xaml.cs
, you'll see that you already have the event handler for OnNavigatedTo
ready to be used in your page. This is the most commonly used event for page navigation as you get to work with caching retrieval, as well as perform any page initialization setup.
The NavigationService
class is used when you wish to work with the navigation system of the hosting frame from code behind. The NavigationService
class provides five useful methods for controlling navigation, as shown in the following table:
Method name |
Description |
---|---|
|
This method navigates to the previous entry in the currently active history journal. If no entry is available, an exception will be thrown. |
|
This method navigates to the next entry in the currently active history journal. If no entry is available, an exception will be thrown. |
|
This method is used to navigate to any given URI. |
|
This method reloads the current page. |
|
This method is used to cancel any asynchronous navigation action that hasn't been processed yet. |
The GoBack
and GoForward
methods are typically used in the full-screen applications where you might want to build your own UI for controlling navigation. The Refresh
and StopLoading
methods are similar to the browser's refresh and stop/cancel buttons. Navigate
is used to navigate to a different page in your application. Navigating using the Navigate
method will fire the navigation events at their appropriate time.
As an example of going backward using the NavigationService
class, add a button to NewPage.xaml
using the following line of code:
<Button Content="Back!" x:Name="backButton" Click="backButton_Click" Width="100" Height="20"/>
Switch over to the NewPage.xaml.cs
file, and add the following event handler:
private void backButton_Click(object sender, RoutedEventArgs e) { if (NavigationService.CanGoBack) { NavigationService.GoBack(); } }
In order to prevent an exception, we first check if it's possible to go back (CanGoBack
) and if so, perform the action.
Build and run your application, click on the New Page button on the top-right navigation bar and then click on the new back button. You'll get right back to the page you were coming from.
Other than these methods, the NavigationService
class also exposes some useful events. Three of these events are equivalent to the page navigation events we've mentioned earlier. FragmentNavigation
is equivalent to Page.FragmentNavigation, Navigated
is equivalent to Page.OnNavigatedFrom
, and Navigating
is equivalent to Page.OnNavigatingFrom
.
It is more common to use the events directly exposed from the Frame
class than the Page
one, but it is perfectly fine to use the ones in Page
if it fits your purpose better.
Other than the three equivalent methods, an additional two methods are exposed, as shown in the following table:
Event name |
Description |
---|---|
|
This event is raised when the frame fails to navigate to the requested page. It provides exception information. |
|
This event is raised when the navigation is stopped. |
The UriMapper
property of the Frame
class is responsible for translating real URLs of views such as /Views/NewPage.xaml
to a more user-friendly URL such as /NewPage.xaml
.
The UriMapper
class exposes the UriMapping
objects, which represent a single pair of URIs to be mapped. Each of these UriMapping
objects must contain the Uri
property, as well as the MappedUri
property.
Let's look at the default UriMapper
that our page currently has:
<navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper>
The Uri
property is what the UriMapper
class will activate upon and the MappedUri
property sets the full path to the value of the Uri
property. In our example, the first UriMapper
maps any blank page entry to the home page. Assuming our application resides at http://MyNavApp.com/Application.aspx
, if the user visits that address, the first UriMapping
will come into play and redirect the user to the home page. On the other hand, if a user visits http://MyNavApp.com/Application.aspx#/MyNewPage
, the second UriMapping
will come into play and redirect the application to the Views
directory and the MyNewPage.xaml
file. Mappings are read top to bottom, and completed when a match is hit. Also, note that UriMapping
will only work when the hashtag fragment is presented in the URL address.
A common use of the UriMapper
classes is to pass data from one page to another. In HTML's world, the common pattern of passing parameters is the use of query strings. A query string is basically a name/value set that comes after a question mark sign in a URL address. While we can use the same approach in Silverlight, we have got used to showing a friendlier URL to the user. Instead of showing http://MyNavApp.com/Application.aspx#/MyNewPage?UserID=50123
, we can map this address to http://MyNavApp.com/Application.aspx#/MyNewPage/50123
.
To do such a thing, we need to add the following UriMapper
class to our application:
<uriMapper:UriMapping Uri="/MyNewPage/{UserID}" MappedUri="/Views/MyNewPage.xaml?UserID={UserID}"/>
Retrieving the UserID
parameter in the target page is done using the NavigationContext
object. The NavigationContext
object has a QueryString
property, which can be used to retrieve the query string parameter from the URL. The most common place to retrieve the query string parameter is in the OnNavigatedTo
event handler. This is the first event that gets fired once the page is navigated to, so if the query string parameter is used to initialize some content on the page (for example, showing the details of a user whose ID is 50123), this would be the perfect place to retrieve it.
The code for retrieving the UserID
property is as follows:
protected override void OnNavigatedTo(NavigationEventArgs e) { if (NavigationContext.QueryString.ContainsKey("UserID")) { string id = NavigationContext.QueryString["UserID"]; } }
In Silverlight, a journal is used to save the browsing history of the user, just like your browser does when you visit the pages with it. You can always go back and forth on the pages you visited during the current user session. The Frame
class has a property called JournalOwnership
, which lets you decide who should own the history journal. The default value of the JournalOwnership
property is Automatic
. This value will set the journal to the browsers if the frame is both the top-level frame and running in the browser. If the application is running out of browser or the frame is not the top-level one, the frame itself will be in charge of maintaining the history journal.
Unless you want to build a specific navigation logic that will be used with your frame, keeping the JournalOwnership
property to its default value will be sufficient in most cases.
Whenever you navigate to a page using the navigation framework, a new instance of the page will be created, even if you use the back button for example. If you want Silverlight to cache a certain page, you'll have to enable caching at the page level (the navigation:Page
node at the top of the XAML file) using the NavigationCacheMode
property. The default value of this property is Disabled
, which means no caching will be happening. Other than Disabled
, you can set the NavigationCacheMode
to Required
or Enabled. Required
will always use the cached version of a page once it's been cached. Enabled
will also cache the page, but when the Frame
instance's cache limit (we will discuss more on that in a second) is reached, the cache will be discarded.
The Frame
instance is responsible for caching the pages it loads. By default, the size of the cache limit is set to 10 pages, but it can be changed very easily using the CacheSize
property. The cache is ordered in a queue—new pages are added on top of old pages and when the limit is reached, the oldest page in the queue will no longer be cached and it will be taken off the queue to make space for the cached version of the new page. Only the pages that are marked with the Enabled
value for the NavigationCacheMode
property are counted for the CacheSize
limit. The pages marked with Required
will always cache, no matter the limit size. When a cached page is dropped from the cache, it will reload from the server the next time the user hits it. In order to save logic or inputs from a page, you should override the OnNavigatedFrom
event. This event fires as soon as the user navigates from one page to another page. Even if your page is cached, the navigation events will fire, so you should handle all kinds of initialization or saving processes inside those events without having to worry whether or not the page is cached.
3.140.188.244