Site Navigation in Silverlight 4


In any application there will be many pages and user controls to display the content to the users. Also content will be displayed based on the some variable values, which may be passed from one page another page by using session and query string parameters. So, question comes here is how does Silverlight navigation works? How do we pass query string parameters from one page to another page?

Silverlight Navigation framework

In Silverlight, We will be implement application/site navigation using the Frame and Page controls. Page controls correspond to separate sections of content, but whereas frame is container for page controls, and provides navigation across pages controls. That states, frame display one page content at a time. We will be learning about the frame control more in details. It has got big picture to learn in both declarative and programmatic.

Frame

In Silverlight, Frame is a Control to support the Navigation, either to or from, across the xaml's page controls. We need to reference to System.Windows.Controls.Navigation dll and its namespace is System.Windows.Controls.

We will be creating many different Silverlight pages to present the content in our application and then navigate to those pages from the frame. When a Uniform Resource Identifier is requested, either programmatically or through a user action, the frame navigates to the page that matches the URI.
The requested URI can include the page to display and also values that represent a particular state for the page. But within the page, we need provide logic to process the URI values and create the page in the correct state.

<sdk:Frame x:Name="employeeModuleContentFrame" Source="/EmployeeHome"
   
<sdk:Frame.UriMapper>
        <sdk:UriMapper>
            <sdk:UriMapping  Uri="" MappedUri=""/>
             .
             .
        </sdk:UriMapper>`
    </sdk:Frame.UriMapper>
</
sdk:Frame>

The Source Property here helps us to define the default page to be displayed. We will see at what are UriMapper and UriMapping class details at basic.

UriMapper Class

UriMapper class contains the collection of UriMapping objects. That actually converts a uniform resource identifier (URI) into a new URI based on the rules of a matching object specified in a collection of mapping objects.

UriMapping Class

Using UriMapping class we can define the pattern for converting a requested URI to a Mapped or resolved URI. We will see some examples of the patterns and how they are resolved. When we create a navigation application in VSTS 2010 following is the default navigations available. Let us see what is this first. I just removed style and events.

<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">
        <Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}">
 
<navigation:Frame x:Name="ContentFrame" Source="/Home">
                <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>
            </navigation:Frame>
        </Border>
 
        <Grid x:Name="NavigationGrid">
 
            <Border x:Name="BrandingBorder" >
                <StackPanel x:Name="BrandingStackPanel">
 
<TextBlock x:Name="ApplicationNameTextBlock" Text="Application Name"/>
                </StackPanel>
            </Border>
 
            <Border x:Name="LinksBorder" >
                <StackPanel x:Name="LinksStackPanel">
 
<HyperlinkButton x:Name="Link1"                                     NavigateUri="/Home"
TargetName="ContentFrame"
Content
="home"/>

                                                                                 <HyperlinkButton x:Name="Link2"
                           NavigateUri="/About"
TargetName="ContentFrame"
Content
="about"/>
 
                </StackPanel>
            </Border>
        </Grid>
    </Grid>

We have two patterns by default, one yellow highlighted and Gray color is the second one. Assume that our application URL is http://localhost:2904/SilverlightApplicationTestPage.aspx

When we type the above URL and press enter in the browser address bar, what happens now, or what should be displayed here as a default. Here we have not requested for any page so the URI is empty. So the UriMapping pattern matching is

<uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
<
navigation:Frame x:Name="ContentFrame" Source="/Home">

And also we have Source Property of the Frame set to "/Home", so it picks up the MappedUri and displays the Home.xaml from the view folder. Now you can notice the browser URL as http://localhost:2904/SilverlightApplicationTestPage.aspx#/Home which is user friendly.

Snavigation1.gif

Now let us do one thing, to test the pattern, remove the Source property of the Frame control and test the page. We can notice that Home page still displayed but browser url is different, i.e. http://localhost:2904/SilverlightApplicationTestPage.aspx, this is because the Uri pattern.

Snavigation2.gif

Now click on the about link, next home, what happens and why? Let us see. If you see the xaml for about, as bellow. We are requesting for About.

<HyperlinkButton x:Name="Link2" NavigateUri="/About" TargetName="ContentFrame" Content="about"/>

Now see which pattern matches to it. If see the first navigation pattern where Uri="", that mean when the there no specific request for the URI this particular navigation pattern will match. Coming to the second pattern, following

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>

Uri is referring to {pageName} which is a parameter, when we click on the about link we are requesting for "/About" which will be parameter to the Uri and for the MappedUri so the request maps to the NavigateUri. That is

Uri ="/About"
MappedUri="/Views/About.xaml"

Let us Add a new one to it. Name it as NewPage.xaml, and assume that we will be having many page with the pattern like {page}Page.xmal in future. Now what will be the pattern for the same?
Comment out the second navigation pattern, and Add the following Uri pattern to the UriMapping.

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}Page.xaml"/>

Add following link to the links section in xaml

<HyperlinkButton x:Name="Link3" NavigateUri="/New" TargetName="ContentFrame" Content="New"/>

Run the application and see what happens after clicking on "new", it works. See the new pattern that we added. "new" is the parameter now Uri become "/New" and the MappedUri becomes "/Views/NewPage.xaml". Now click on the home or about it will throw an error because the there is no mating pattern.
Ensure that we can't have following patterns at a time. Because when we click on about or home first pattern also matches but there is no page called New.xaml, so it won't work. So be careful on deciding the page name standards .

<uriMapper:UriMapping Uri="/{page}" MappedUri="/Views/{page}.xaml"/>
<
uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}Page.xaml"/>

So, this way we can avoid declaring many mapping tags to the UriMapper object. Same explanation goes for home link as well. Now we know what is user friendly URL's, Mapping Uri's and mapped Uri's. Now let us see more patterns. Now let us see more patterns by passing query string parameters, which will be using more frequently in our application.
There are many more patterns which will help us in the Navigation across pages.

Refer to http://msdn.microsoft.com/en-us/library/cc838245(VS.95).aspx 

Passing Query String Parameters

Assume we have Employee List Page, like employee search that displays the list of employees. Upon selection and by clicking on one button we need to pass the selected employee if to employee details page, where we will be displaying the employee details. Here search for employees and grid selection is out of scope of the context. What I will be doing is upon clicking on the button how we will be passing the parameter by using a navigation pattern.

  1. Add a new page called EmployeePage.xaml to the View Folder
  2.  
  3. Add a text block, , lblSelectedEmpID, in which we will display the selected employee.
     
  4. Add a Button to the MainPage.xaml, btnEmployeeDetails.
     
  5. Add the following code to the Button click event and Add the following code.
     

            private void btnEmployeeDetails_Click(object sender, RoutedEventArgs e)

            {

                Uri uri =new Uri("/EmployeePage?empId=10",UriKind.Relative);

                this.ContentFrame.Navigate( uri);

            }
     

  6. Add the following code to the EmployeePage.xaml
     

    protected override void OnNavigatedTo(NavigationEventArgs e)

    {

        if (this.NavigationContext.QueryString.ContainsKey("empId"))

        {

    this.lblSelectedEmpID.Text = "Selected Employee ID is : " +this.NavigationContext.QueryString["empId"].ToString();

     

        }

    }
     

  7. Run the application and test it.

    Snavigation3.gif

    Snavigation4.gif
Frame Events

Frame control has two main events, one is Navigated and NavigationFailed. We will see what they are how to use them in our Silverlight navigation.

Navigation Failed Event

I would like to cover Navigation Failed event first. Because, if the page that we are requesting is not found or invalid path had been given we will need to handle the error.

Add the following event to the Frame in our xaml and code behind.

<navigation:Frame x:Name="ContentFrame" NavigationFailed="ContentFrame_NavigationFailed" >
// UriMapper and UriMapping goes here
</
navigation:Frame>

// If an error occurs during navigation because of invalid path or page not found
private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{

}

Some of the important point we need to know is

  1. Handled -> When we handle NavigationFailed event to process an exception that is thrown as a result of a failed navigation, and we don't want Silverlight to continue processing the exception, you should set the Handled property to true, which is by default false.
  2.  
  3. Uri -> gets the uniform resource identifier (URI) that could not be navigated to.
So our code becomes, change btnEmployeeDetails click event by giving wrong xaml page path and test the code.

  private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
  {
            e.Handled = true;

     MessageBox.Show(e.Exception.Message.ToString() + " \n " + e.Uri);
}

If you need to display a nice user friendly message there are many techniques available in Silverlight like child window or new page itself, for now out of scope.

Navigated Event

Now we will see about Navigated event. This event occurs, if we implement, when the requested Uri is found and available. The event arguments object provides data for the OnNavigatedTo and OnNavigatedFrom methods, and handlers of the Navigated and NavigationStopped events.
The data that it provides is Content and Uri. Content get the content of target and Uri get the target Uri.