Even a chimp can write code

Sunday, June 08, 2008

Pages in Silverlight

Pages as a paradigm likely pre-date the web itself although they have been popularized by document-based markup languages deriving from SGML. Windows Presentation Foundation (WPF) with XAML further codifies Pages as a first-class UI element from its heretofore usage as a logical element. Looking back from Silverlight, this is as good a point in history to start as any. WPF has built-in support for reusable pages in its applications, and the task of navigation between them. It provides the infrastructure for declarative navigation via hyperlinks or programmatic navigation via NavigationService, and a journal that remembers which pages were navigated to or from. For XBAPs on IE7+, WPF supports integration of the journal with the browser's Back and Forward buttons, while elsewhere it displays a substitute navigation bar with this functionality (the limitation is an effect of how the XBAP host plugs into the browser). There is also a building block called PageFunction which introduces a paradigm for invoking pages as if they were methods, providing a neat little way to build wizard UI. Unlike WPF, Silverlight does not support these things, as of version 2. This post isn't about brooding that absence though -- instead we'll look at the lay of the land in Silverlight 2 with regard to pages and navigation, and look at common workarounds.

Although Silverlight does not have the Page type at this time, the term is currently used for its root visual UserControl. That root visual object is analogous to the root window in WPF, can only be set once for the lifetime of the app, and is effective once the Application's Startup event is raised. The act of navigating from one page to another is similar in principle to the act of removing one child element from an invisible root and adding another child in its place. [Actually given the journal it is more like switching Z-indices between siblings, so that one gets prominence while the other is made inconspicuous]. You can use this same principle in devising the notion of paging in a Silverlight app.


This recipe requires the following ingredients:

  • App.xaml.cs: the code-behind for the Application

  • Frame.cs: a user control which will serve as our "navigation frame". This has no UI.

  • Page1.xaml/Page1.xaml.cs: a user control representing a unique "page". This has a button whose click event navigates away to Page 2.

  • Page2.xaml/Page2.xaml.cs: similar to Page 1 in logic and function, only this represents the second page in your app.

Step 1: Create a navigation frame

This is a really simple user control. It has a public Navigate method which is passed a "page" object for which it duly resets its original content.

// Contents of Frame.cs
public class NavigationFrame : UserControl {

// Navigate the frame to the specified content
public void Navigate(UIElement content)
{
// the existing content of this user control is
// discarded and the specified param is plugged in
// its place
this.Content = content;
}
}

// Optional abstraction for page config
public static class Pages
{
// for simplicity we're using static properties,
// but this could just as easily be a URI to
// Type mapping table
public static UserControl HOME_PAGE = new Page1();
public static UserControl ANOTHER_PAGE = new Page2();
}


Step 2: Wire up the navigation frame as the app's RootVisual

Ordinarily the code generated by Silverlight tools (Visual Studio or Blend) will have a user control hooked up as Application.RootVisual within the Startup event handler. You will change this to hook up the navigation frame. Then hook up the user control to be the navigation frame's content (by invoking the NavigationFrame.Navigate method you created above).

// Contents of App.xaml.cs

// the Startup event handler sets the root visual
private void App_Startup(object sender, StartupEventArgs e)
{
// Navigate to home page
Navigate(Pages.HOME_PAGE);
}

public void Navigate(UIElement content)
{
// Create frame on an as-needed basis
if (this.frame == null)
{
this.frame = new NavigationFrame();
this.RootVisual = this.frame;
}

// Navigate to content
this.frame.Navigate(content);

}


Step 3: Build your pages

Now that the navigation infrastructure is ready, go ahead and create a couple pages. These are merely user controls which would otherwise have been hooked up directly as the RootVisual. That part is simple. An important attribute in this equation is the fact that pages need to provide the experience of "navigating away". You can use a hyperlink, a button, or trap mouse/key events. We'll use a button and insert the navigation logic in it's click event handler.


// Contents of Page1.xaml.cs

// on btn click, this navigates to the second page
private void navigateButton_Click(object sender,
RoutedEventArgs e)
{
(Application.Current as App).Navigate(Pages.ANOTHER_PAGE);
}


Let's create another user control which will be navigated to.


// Contents of Page2.xaml.cs

// on btn click, this navigates to the first page
private void navigateButton_Click(object sender,
RoutedEventArgs e)
{
(Application.Current as App).Navigate(Pages.HOME_PAGE);
}


Well, that's all there is to it really. Build and run, and you've got your simple little navigation application.

In this pattern, the navigation frame element:


  • is the equivalent of the implicit navigation control

  • is a container with no visible visual properties (e.g. Background, etc.)

  • has only a Content element representing the current page


In the pattern, the page element:


  • is the equivalent of the navigable Page

  • has visuals or contains visual elements

  • can be attached and detached from the tree on a navigate event (i.e. the current element is removed, and a new one added in its place)

Our example used the Navigate By Object approach. Note that we invoked the Navigate method and passed it a page object. You could tweak the logic so your pages can be given data as part of the Navigate call. You could just as easily add URI-to-type mapping into the Pages utility type. That would allow you to replicate the Navigate By URI approach which is more organic to content on the web. If you use the Navigate By Object approach and you have a journal that maintains a back/forward stack of pages you've previously navigated to, you can incur a working set hit. This is because your journal will retain references the page objects in memory for long intervals - usually the life of the app - preventing the garbage collector from reclaiming them. Using the Navigate By URI approach in such situations can offer better use of memory. This is because you can delay instantiation of page objects until when the Navigate needs to happen.


In closing, there is a good case for pages, navigation service, journalling, transition animations, deep linking etc. to be part of the Silverlight platform. This is certainly of interest to us as we look toward the next version of the runtime and it's framework. I'd like your thoughts and feedback on these features and how important they are to your scenarios.


Further reading:


PS: For Ben. Sorry this has been long overdue.

PPS: Thanks to Michael Weinhardt for valuable insights.


Update [06/16/2008]: Dave has a new post on navigation with transition effects. Link under the Further Reading section above.

Labels: , , ,

Email this | Bookmark this

10 Comments:

  • I'll give you the same feedback that I gave Rob Relyea when he asked me what I wanted to see in the next version of WPF. I told him, Microsoft needs to add things to the framework that I can't build myself. Navigation is something that's easy to build, as you have demonstrated - so I wouldn't bake it in (especially since it tends to be application specific). But, there are other important features that can't be built from the outside. Here's a list of a few things I think are critical for SL to succeed:

    1. Hardware acceleration
    2. Shader effects
    3. Printing support
    4. 3D

    Note that Flash 9 has #3 and that Flash 10 has #1,2 and 4. I'd like to point out that there is a huge casual gaming explosion that is about to take place, and #1,2 and 4 are critical if SL is going to take a piece of that market. Because when it comes to this area, developers will choose Flash over SL because of pure graphics features.

    I'd also like to see SL get a full implementation of WPF's Triggers, Gestures and Commands, because the lack of these features greatly compromises the ease with which one can build a sound UI architecture.

    By Anonymous Anonymous, at June 8, 2008 at 8:42 PM  

  • First of all a very nice and informative tutorial. Secondly, i dunt think that people would prefer to use flash or flex for that matter unless the scripting language becomes as strong as C# and honestly speaking, i dun see that happening in the near future unless Adobe decides to add support for other languges. AS and C# cant even be compared. C# is far more better and powerful. I would never opt for AS for making applications and games.

    By Anonymous Anonymous, at June 8, 2008 at 8:50 PM  

  • >>I'd like your thoughts and feedback on these features and how important they are to your scenarios.<<

    This a discussion that I had with ScottGu, Time Sneath, Mike Harsh and someone else, ever since version 1.1 came out and I was hoping we would have the WPF structure.

    Currently, for my future apps, I'll implement two separate techniques: a) Like a Winform that has a modal form which can call another form and goes deep and the children can see the parents data and then the user has to back out to the original parent.
    b) Is the page system you showed which I had begun to change a part of my app which passes control to another independent section.

    So, both will be very useful to have for it's own cases. I see SL as a Winform application and Web page development that live in the same environment.

    I'm very happy to see people like yourself behind this product and it's future!
    Thank you for the great work!
    ..Ben

    By Anonymous Anonymous, at June 8, 2008 at 9:09 PM  

  • I just created a prototype of a deeplinking facility for Silverlight and the concept works very well.

    By Anonymous Anonymous, at June 9, 2008 at 1:59 AM  

  • >>PS: For Ben. Sorry this has been long overdue.<<

    Ah, you did remember that I had asked for this a while ago and I had been waiting...patiently ;-)

    Thanks for remembering!
    ..Ben

    By Anonymous Anonymous, at June 9, 2008 at 5:38 AM  

  • >>deep linking etc.<<

    Ashish, I was looking at the latest SL Demo from Telerik where I noticed, as I change from one User Control to another, the URL was changing. I send a question to support, and they told they do this to jump to a particular page.
    Then I realized the statement you had made in your blog that, this could be deep linking. Could you please give us a bit more on this deep linking? I like to create some kind of strategy for this.
    Secondly, can this URL addition be useful for SEO problem?
    Thanks!

    By Anonymous Anonymous, at June 9, 2008 at 5:56 AM  

  • @rob

    You mentioned telerik and I remembered they had blogged about deep linking a while back:

    History Enabled Script Manager and Silverlight

    Of course it would be great if we can have more information on the topic or a place to discuss it.

    SEO and deep linking are quite important for anything beyond a simple video player/twitter badge/etc.

    Deep linking itself would not be very helpful for SEO since it uses page anchors (#) and that part of the url is normally ignored. SEO would have to involve some kind of redirection but then we would be talking about cloaking (from a search engine's perspective) so it is actually not that trivial :)

    By Anonymous Anonymous, at June 9, 2008 at 9:05 AM  

  • my previous comment was really a response to ben's

    >>Secondly, can this URL addition be useful for SEO problem?<<

    By Anonymous Anonymous, at June 9, 2008 at 9:12 AM  

  • Hey Ashish,

    Its a pleasant coincidance that we both share the same name and surname!
    I have gone through your posts and they are very interesting :-).

    I am new to the blogging world and very recently started a personal blog :-)

    Cheers!
    Ashish

    By Anonymous Anonymous, at July 22, 2008 at 12:54 AM  

  • Rob: roughly 8 months after your comment, I am happy to say that Silverlight 3 (now in beta) supports #1, #2 and #4. And #3 is really high on our list of things to do for the next version.

    Thank you and others for your feedback! Keep it coming.

    By Blogger Ashish Shetty, at April 13, 2009 at 5:28 PM  

Post a Comment | Home | Inference: my personal blog