Silverlight - Navigate to a specific Page using a Hyperlink

Sometimes I think it’s hard to know what I should write about, my ideas comes from different sources. Some of my posts is based on questions people are asking on forums. If someone ask one specific question, it’s a big chance that someone else have the same kind of question, so I often blog about the solution so others kind find them. So this blog post will be about how we can from a HTML page navigate to a page hosting a Silverlight application and navigate to a specific .XAML page. Probably someone else have blogged about this before but I also use my blog as a reference for my self :P

If we have our Silverlight control hosted on a page, for example default.aspx, the Silverlight will always show the same .XAML page when the page is loaded. This is because the visual root is set within the App file when the Application is started:

private void Application_Startup(object sender, StartupEventArgs e)
{
     this.RootVisual = new MainPage();
}

We must specify a RootVisual when the application is started and that will make the Silverlight application always show a specific page when we request the the page hosting the Silverlight application. So how can we from a hyperlink navigate to our Silverlight host page and specify which page we want to show? The RootVisual is of type UIElement, so we can set any kind of UIElement to be the RootVisual, for example a Grid and add a UserControl as a Child to the Grid, like this:

private void Application_Startup(object sender, StartupEventArgs e)
{
     var rootGrid = new Grid();
     this.RootVisual = rootGrid;

     rootGrid.Children.Add(new MainPage());
}


The following code will still show the MainPage UserControl but this time inside of a Grid. Because we can specify any kind of UIElement as the RootVisual, and in this example a Grid, we can now dynamically add UIElements to the RootVisual’s UIElement. By using Reflection we can create an instance of the UserControl from a string, so we can for example get the name of the UserControl we want to display form a QueryString. We can use an URL like this “default.aspx?page=Page2” when we want to request the page hosting the Silverlight application and show the Page2.xaml at startup.

Here is the code which will get the Page from a QueryString:

private void Application_Startup(object sender, StartupEventArgs e)
{
    var rootGrid = new Grid();
            
    this.RootVisual = rootGrid;
            
    var pageToShow = GetPageFromQueryString();

    if (string.IsNullOrEmpty(pageToShow))
        pageToShow = "MainPage";

    var userControlToShow = LoadPage(pageToShow);

    rootGrid.Children.Clear();
    rootGrid.Children.Add(userControlToShow);
}


private static UserControl LoadPage(string pageToShow)
{
   var appType = App.Current.GetType();

   var userControlTypeString = appType.Namespace + "." + pageToShow;

   var userControlToShowType = Type.GetType(userControlTypeString);

   if (userControlToShowType == null)
      throw new Exception(string.Format("Can't find the Page with the type {0}", userControlTypeString));

   var userControlToShow = Activator.CreateInstance(userControlToShowType) as UserControl;

   if (userControlToShow == null)
      throw new Exception(string.Format("Can't create an instance of Page with the type {0}", userControlTypeString));

   return userControlToShow;
}


private static string GetPageFromQueryString()
{
    string pageToShow = null;

    if (HtmlPage.Document.QueryString.Count > 0 && HtmlPage.Document.QueryString.ContainsKey("page"))
         pageToShow = HtmlPage.Document.QueryString["page"];
            
    return pageToShow;
}

Note: This code will not do any security check etc, it's only used to give you the basic ideas and the concept. So type-injection and what kind of pages a user are allowed to see etc is not taken care of in this example, I wanted it to only show the basic concept and not make the code too complex.

To do something similar as the above code we can instead use the Frame control on our MainPage. The Frame control can show a specific control based on a URL like this “defaul.aspx#/Page2.xaml” (This URL will show the Page2.xaml content inside of the Frame control). To use the Frame Control you only need to add the Frame control to the XAML:

<UserControl
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"  
   ....>
  <Grid x:Name="LayoutRoot">

      <navigation:Frame></navigation:Frame>
      
  </Grid>
</UserControl>

I hope this blog post have given you some ideas about how you you can navigate to your Silverlight application and specify which Page (UserControl) you want to show when the application is started.

If you want to know when I publish a new blog post, you can follow me on twitter: http://www.twitter.com/fredrikn

4 Comments

  • Great post! But I have a few comments. :-)

    First, your GetPageFromQueryString method is unnecessarily complex. It would be enough to check that the QueryString is not null and the do a QueryString.TryGetValue("page", out pageToShow). That would make the method somewhat easier to read.

    Second, you use Activator.CreateInstance to create an instance of the user control you want to display. Aren't there some rather severe limitations on using reflection in Silverlight? I have experienced problems when trying to create instances defined in external assemblies for instance.

    Oh, and one more thing. There is a (very) slight chance that your code may be vurnerable for a "type-injection" attack. You use the query string to specify which type of page the user wants to show. In theory the user may change the query string to access pages he isn't supposed to see. :-)


  • @Rune:
    I can agree on the complexity of the GetPageFromQueryString, but I didn't spend so much time with refactoring when I wrote the example in this blog post, and I don't like methods that uses out parameters.. they are so ugly. I was actually thinking about rewrite it, but left it as it was because it's not important for the whole concept.
    Second: The code will only Create an instance of the UserControls located within the same namespace and assembly as the SL project. So it will not be any problem as far as I know.
    And about the "type-injection", yes that is true. Sometimes it's hard to demonstrate the concept and not make the code to large and diffcult to understand, so sometimes some code must be left out, but it's a good point.. but I hope some developers can also use the brain :P
    Thanks for the comment, I hope other readers will read your comment.

  • Haha! Developers with a brain? Never heard of them...

    Regarding your use of CreateInstance. It would be nice to have an example where you download an assembly at run-time to display a page dynamically. That would allow you to write very flexible applications. But in that instance I am quite sure CreateInstance will fail.


  • @Rune:
    Must have some faith in developers.
    Check this post out: weblogs.asp.net/.../how-to-create-a-module-based-silverlight-application-part-1.aspx

Comments have been disabled for this content.