With the latest release of nRoute we’ve added a trove of navigation-related features, to explain and demonstrate these features I’ve put together an end-to-end application that fronts the Netflix’s oData Catalog. And so in this post we’ll go through using these feature in a step-by-step manner to cumulatively compose a rich, extensible, and loosely-coupled application. Here is what we are going after:

PersonListing

MovieView View the nRoute’s Netflix App (note you can also install the application on your desktop).

Guide Scope

Right off the bat, let me be clear, this post does not show you how to consume oData services or explain how to design iPad’ish UIs or troll on the virtues of MVVM – it purely concentrates on plugging-in nRoute’s navigation and related features to compose our demo app. Familiarity with nRoute concepts is not a must, but will be much helpful. Secondly, just as I finalized this post, Netflix switched from a purely server-side paging-model to a hybrid client-server paging-model so I had to revisit the code, hence there might be a bit of mismatch from what’s shown here and the actual code.

Step 1. Application Setup

For our application we’ll split up the solution into two projects, one the main Silverlight (xap) application and the other a class library which will hold the Netflix service. And so first, in our data project we need to add a service reference to the Netfix oData Catalog.

CatalogService

We also need to need to add references to nRoute, so in both our Silverlight projects we add references to nRoute.Framework.dll, along with its dependencies System.Observable and System.Windows.Interactivity. And lastly you need to cross-reference the data project to the main application project. More...

Posted by Rishi on 30-Jun-10 1:21 AM, 42 Comments

Think HTML's IFrame, is perhaps the easiest way to describe what nRoute navigation containers are and do - or simply put when we navigate we do so in a container. Also, as I have mentioned enumerable times before, with nRoute we abstract content and resources as Urls, and in a yin-to-the-yang kind-of way navigation containers represent the realization of those abstractions. 

Overview

Below is the class-model for various navigation containers (hereon referred to only as containers) in nRoute.

On the left side of the diagram above are the interfaces that represent the functionality availed by the containers, and on the right are the usable controls that implement the interfaces. The reasons for interface based implementations is primarily to enable instance-less representation and also to enable tacking the container-related functionality onto existing controls - both these points are highlighted below.

INavigationHandler

If you look at the NavigationService API you'll notice you don't actually need any of the pre-defined containers to handle a navigation request - all you need is an implementation of the INavigationHandler interface. This interface's responsibility is to handle navigation responses, and it has a very simple signature - it contains two methods, the ValidateRequest method ascertains as to if the handler can process the request, if true then the ProccessResponse method is given the response to handle.

Now, normally you would use the specialized containers controls that implement the INavigationHandler and other related interface, however it is also possible to append this interface on existing controls to turn them in containers like a stack panel or list box. Further, it is also possible to use this interface and the navigation facilities in a non-visual context - like using it with the ViewModel however semantically it is not designed for such use-cases.

INavigationContainer(s)

The INavigationHandler interface mentioned above represents the bare-essential as far handling the response is concerned; however containers normally have richer use semantics and that is what the INavigationContainer interface encapsulates. The API is for the most part self-descriptive, it basically appends the INavigationHandler with informative events, state properties, and navigation-related methods.

Moreover, we usually build upon the INavigationContainer interface to create specialized containers such as the IBrowsingContainer - the idea of having specialized containers is to encapsulate different navigation-model functionalities such as back-forward navigation or history trails. Currently, out of the box in nRoute you get four usable container, implemented as controls and which I described earlier as follows:

NavigationContentControl This is essentially a bare-bone and light-weight container based on the content control type, it can handle a navigation response but that's about it. It offers no events, history, or state related functionality - basically it has none of the richer use semantics present in an INavigationContainer implementation. Think Vanilla Light.
NavigationContainer This is also a simple and basic control that inherits and extends the NavigationContentControl, but it offers richer use-semantics based on the aforementioned INavigationContainer interface. However, it stores no history or state, and it simply navigates onwards from one Url to another. Think Direct and Onwards.
BrowsingContainer This builds on the NavigationContainer control, and offers web-like behaviour and state management features. Basically, it stores a stack of back and forward page's info with state - akin to an Internet browser. It enables back and forward navigation based on the stored history, and allows purging of the history and state. Think Very Web-Like.
StatefullContainer This container also builds on the NavigationContainer, but stores state for any page you have visited - the state is restored onto the page once you navigate onto it again. It doesn't have back or forward stacks, it only keeps one list of states - so simply your last saved state is restored once you navigate to the same page. Think Good Memory.

The build-in containers cover most of the common use-cases, however the buck doesn't stop there - because it is relatively simple to create your own containers. And the benefit with custom containers is that you can precisely gear the container to whatever use-semantics your scenario mandates - see my forthcoming post for some samples. Also when designing your

State-Management

One of the important use-semantics that containers exhibit is state-management - which basically involves saving and restoring the content's contextual (and visual) state. This is an opt-in feature that the content can participate, however it is the container's mandate in as far as using, applying, and maintaining the states is concerned - so if the container (like the NavigationContentControl container) doesn't administer state-management then the content's participation becomes irrelevant. Further, the container defines how and when the state will be administered - so for example the BrowserContainer only restores the content state when navigated back or forward, and not when you navigate to a new Url.

The figure above show the opt-in API for participating in state-management - it does three things, one initialize the state with the passed-in name-value collection, two save and restore state on demand, and lastly provide for a content title. The initialization part which I described in an earlier post gets passed in a merged name-value dictionary consisting of the navigation request's name-value collection, the default parameters name-values set while defining the route, and the parsed tokens' name-values from the Url. Note, this is separate from the ISupportRestoreState interface - which asks for a name-value dictionary to save the content's state, and that same state is later passed-in when restoring the content's state.

Though the opt-in mechanism for supporting state is rather "manual", it is also quite simple and low-impact as opposed to reflection based approaches. Also, one other nice thing about the opt-in interfaces is that you can use these with your ViewModels, which makes for a tidy integration story. Lastly, the opt-in feature also helps with the "separation of concerns" principle I've been harping about - the containers are only responsible for managing the states, whereas the content is responsible for extracting and using the state.

Composition 

In my last post, I highlighted the composition capabilities brought to bear by nRoute - and without repeating myself, I'll just mention that containers are the fundamental building-blocks for composition in nRoute. However, the important thing to understand (and apply) is how you can construct and combine containers to create various use-models such like parent-child or tab-like constructs. See the demo app's technical overview, wherein we create a so-called workspace/blades model by composing a number of containers together. Also, as the container is only "tied" with the content at run-time, it gives you enormous flexibility and control as far the composition is concerned, independent of the content itself.

Default / Application-wide Containers

One of the common scenario with the use of containers is to annotate a default/application-wide container - which when processing navigation (without any specified target container) is automatically used to handle the response. To house a reference to the default/application-wide container we use the Application-class derivative (by default App.xaml), because it provides a well known host to similar application-wide functionality. For all the build-in containers we have corresponding Application-class derivatives, as shown below:

You might have noticed that the Application-class derivatives above specifically implemented the container's defining interface (e.g. IBrowsingContainer on BrowsingApplication type), the reason for that is to make the application class a fully-implemented proxy of an actual container (which would be in a VisualTree on some Page). This might look somewhat confusing, but what it allows us is  to swap the actual container, without breaking the event-references or direct reference to the default/application-wide container represented by the Application derivative. With this behaviour you could enable tab like containers; in fact the demo app uses this proxy-type behaviour for setting the active workspace/blade as I explained earlier.

Two other thing enabled by using the application-level containers archetype is browser shell-integration and deep-linking support, however I'll have to cover those aspects in a separate post.

Behaviours

nRoute comes with a wide-array of navigation related behaviours build-in, and these behaviours when triggered automatically try and resolve a container to handle the response. The logical sequence to resolve the container is as follows; firstly, if you have specified the handling container (either by binding or by specifying an ElementName) it will just use that, and it's done. If that is not specified, it will look in its visual tree first and try and find the first parent that implements the INavigationHandler interface. This also means that it kind of works like a browser whereby when you click a link the hosting container (like the Html Page or IFrame) handles the navigation automatically for you, unless specified otherwise. If no handler is found in the parental visual tree, it will look at the current application object and see if a default/application-wide container is specified and use that. If it doesn't find a container at the application level, well it gives up and throws an exception.

Parts & States Model

Lastly the build-in containers use the parts and states design model introduced with Silverlight 2 - this enables you to customize both the visuals in various "navigation-states" as well as the transition effects in the course of the state changes. Also, the inheritance-root for all the containers squares up to the content container type - which as you known allows for a custom look-and-feel. One other point for customizing the visuals is the "ShowFailedVisualState" protected method - by default it shows a message box when the navigation fails, you can override it to customize the error visuals.

This concludes another part of the documentation for nRoute, however I am going to follow this post by a couple of examples that demonstrate the customizability and composition capabilities of the container infrastructure in nRoute.

Posted by Rishi on 15-May-09 10:14 AM, 9 Comments

Categories: nRoute

I've yet to cover all the features of nRoute in depth, but since a lot of people are downloading the framework and the demo app, I thought let me step through code in the demo app to at least provide a rounded overview of the features in nRoute. To structure the content, I will headline each significant part and highlight the relevant aspects in the code. Further, to make some distinctions clear, this post is divided into two parts, one relates to the Infrastructure in the demo app and the other relates to how the consumable/actionable content integrates with the infrastructure, using nRoute of course. Moreover, to get the most out of this long-long post please familiarize yourself with the basics of nRoute, using my series of earlier posts.


PART I: INFRASTRUCTURE SETUP

The Application Class

Right off the bat, in the app.xaml.cs we can see that the application class inherits from a custom type defined in nRoute called StatefullApplication. I've mentioned this before, but the idea of deriving off a custom application type is to primarily give home to a default container. The default container is an application-wide navigation container hook-up point, wherein any navigation without a specified container is handled - think unhandled exception handling as a mental model.

public partial class App 
	: nRoute.Navigation.Application.StatefullApplication { .. }
Now we have several types of navigation containers build into nRoute, and equally you can define you own custom ones. In this instance we are using a "Statefull Navigation Container" and its companion application class derivate (the StatefullApplication type) - I had earlier defined the Statefull Container as follows:

This one basically stores the state of the page when you leave it, and restores it when you navigate onto it. This doesn't have a stack of back or forward pages history, it keeps one list of states and applies it whenever you navigate onto the page. So simply your last state is restored once you navigate to the page, in either back/forward or direct navigation manner.

The StatefullApplication type provides a number of features, one that is pertinent here is the NavigationContainer property which allows us to set any navigation container as the application's default container - and as you will read ahead that is exactly what we do.

The Workspaces/Blades Model

Before we can dig deeper into the application class, I want to explain one of the core aspect of the UI - which is the blades/workspaces model that provides one or more tab-like containers for the application. Each workspace/blade is basically a StatefullContainer which can collapse or expand into full view, and features a title bar to its left (see Workspace.xaml). I've exposed all the interaction functionality of the workspace through an interface called IWorkspace, nothing special really - basically think of it as a specialized tab. Further, in order to show the blades/workspaces I created a custom panel called WorkspacesStackPanel - essentially it is like a horizontal accordion, specialized to only show one active workspace and vertical title-bars for all the other collapsed workspaces.

Workspace/Blades UI

To manage the workspaces there is the WorkspacesViewModel type, which should have really been called the WorkspacesController type. And like my suggestion, this is basically a simple application-wide controller for managing all the workspaces. Again nothing special, however for your own use you might want to amend this to allow adding/removing workspaces, with perhaps a more streamlined workspace-interaction pattern. This controller is exposed on the Application class using the read-only WorkspacesViewModel property.  

The Application Startup

The fun starts in the startup event of the application, we do a couple of things here:

  • We set the root visual to the MasterPage.xaml [Line 3]
  • We then create the workspaces, which initializes the mentioned WorkpsacesViewModel type [Line 5]. Note you can create your own strategy to customize the number of workspaces, in the demo app's case we try and read the "workspaces"  querystring key for values between 1 and 9 (try appending "?workspaces=7" to the Url, before the bookmark)
  • We set the DataContext of the root visual to the WorkpsacesViewModel, this cascading'ly hooks up the visuals [Line 6]
  • Next, we register for browser integration, this functionality is availed through the inherited base application class. Note, while registering we also specify the default Url, which in this case we read from the browser's current bookmark or use the specified default value ("Home/")  [Line 8]
  • Next, like before we set the active workspace's Initial Url by reading the browser's Bookmark or use the Default value ("Home/") [Line 9] - this allows for the deep-linking feature
  • And also we set up a handler that updates the Browser's Title when the active workspace changes [Line 11]
private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new MasterPage();

    CreateWorkspaces(3); // delegates to a helper function
    ((FrameworkElement)this.RootVisual).DataContext = _viewModel;

    base.RegisterBrowserNavigationHooks(base.GetCurrentBookmarkOrDefaultUrl("Home/"));
    _viewModel.ActiveWorkspace.InitialUrl = BrowserIndexedNavigationBridge.RemoveBookmarkIndexerInfo(GetCurrentBookmarkOrDefaultUrl("Home/"));

    _viewModel.ActiveWorkspaceChanged += (s, e2) =>
        base.SetBrowserWindowTitle(this.NavigationContainer != null ? this.NavigationContainer.Title : null);
}

Browser Integration

One of the side-effect of the multiple blades/workspaces is that it complicates the browser-integration strategy, because the browser only keeps a single history-stack whereby we need multiple of these on a per workspace/blade basis. Not to worry though, because like everything else in nRoute the browser integration strategy is totally configurable. The solution in this case is that we append the bookmark with an indexer that identifies which workspace the bookmark belongs too (it looks like ¶3 or ¶1 postfix). The integration is achieved by overriding the CreateBrowserNavigationBridge method in the application class, and providing a custom implementation of type WorkspaceBrowserBridge. When the "bridge" adds to the browser history our custom implementation appends the bookmark with the active workspace index, and when the browser navigates back or forward it parses the workspace index and accordingly sets the active workspace and the Url. This allows us to have a single history-stack for multiple workspaces/bridges integrated within the browser, with full deep-linking support. As a notable, the browser integration behaviour is based on a simple interface of type IBrowserNavigationBridge.

Master Page

The master page (or shell if you prefer) is actually simpler than it appears, it is basically a three column by two row grid (as shown by the red lines), and three borders fill the grid area to define the rounded shape of the visual (as shown by the green borders). We also dynamically clip the grid as per the edges of the borders. The Icons on the left, are placed in a canvas and aligned to the bottom. The bar at the bottom is also a grid, and on the right we have stack panel that block-by-block fills in the side bar. 
 Master Page Visual Breakdown
The main content is an ItemsControl element which is data bound to the workspaces list, and uses the aforementioned WorkspacesStackPanel as the ItemsPanelTemplate.

<!-- WORKSPACEs -->
<ItemsControl ItemsSource="{Binding Workspaces}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <cnt:WorkspacesStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl> 
Visual Effects

Or the lack off, should be title as I absolutely took a minimalistic approach in recreating the reference UI. Since I made the app in SL2, I purposely deferred using visual goodies in favour of the goodness on offer in SL3 - particularly the GPU based shader effects and Perspective 3D. The one thing I did attempt was the hover-effect as without the visual-highlighting the navigation-links wouldn't be self-apparent. The hover-effect uses attached behaviours with an extensible model so that you can extend it to your custom controls, still it is quite comprehensive. The use template is also pretty simple, have a look:

<Border Style="{StaticResource SideButtonBorderColourBrush}" 
    hvr:Hover.HoverBackground="{StaticResource SideButtonHightlightBkgBrush}">
In the snippet above, we attach the behaviour to a border and say on hover (mouse over event) change the background to the brush identified. Depending on the visual element you can change the background, foreground and the border on hover. Note, you can adopt this behaviour for use elsewhere, but consider using storyboards to give transitions a visually engaging lift.


PART II: CONSUMABLE & ACTIONABLE CONTENT
 

Content Composition

To this point we have had the application's infrastructure basically all spelt out, without even having to delve into the actual content - which is good because we want minimal intermingling of the content and the infrastructure that surrounds it. In fact this is the forte of nRoute, that we can keep the content and containers loosely coupled from each other using only Urls - and nRoute takes care of the composition for you. Read about the basics of navigation-based  composition in my previous post.

Content Mapping

Now, for the composition to occur we need to mark the content using Urls as their identifier, and to that end we have a couple of way build-in into nRoute - though yet again, you have a flexible pass in that you can create your own loading/mapping strategy. My preference is normally to use attribute-based mapping system in nRoute owing to its simplicity (and which again like everything else you can customize/extend). Below, are some relevant snippets from the code-behinds of various content files, using attribute based mapping:

// Mapping in HomePage.xaml.cs
[MapNavigationContent("Home/", "Home Page")]
public partial class HomePage : UserControl { .. }

// Mapping in InformationPage.xaml.cs 
[MapNavigationContent("Pages/AboutInformation", "About")]
public partial class InformationPage : UserControl { .. }

// Mapping in FuturePageViewModel.cs
[MapViewModelViewNavigation("Pages/FuturePage/{Name}", 
	typeof(FuturePageViewModel), typeof(FuturePage))]
public class FuturePageViewModel { .. }
Using the MapNavigationContent attribute is one the simplest way to annotate content as Urls; as shown above we put it on the content type (in this case the HomePage class), and specify it's Url Identifier ("Home/") and optionally a static title ("Home Page") associated with that Url. The other attribute shown is MapViewModelViewNavigation which as evident is helpful when using the M-V-VM pattern, and therein we can associate the Url with the View type (in this case the FuturePage type) and its associated View-Model type (FuturePageViewModel type). Further, using this attribute the View-Model type is automagically injected into the View, by setting it as it's DataContext when the Url is requested for. Do note, you have flexibility as to where you put these attributes, see my other posts for examples.

The other build-in way to register is akin to what is present in ASP.NET 3.5sp1 or MVC, which is via statically registering against the NavigationService static class. Below, is an abbreviated snipper from the application class in the demo app:

protected override void RegisterNavigationsRoutes()
{
    NavigationService.MapLoadedAssembliesRoutes();
	NavigationService.MapRoute("Images/HeartYourWeb",
        new NavigationResourceHandler("Images/HeartYourWeb.xaml", NavigationResourceHandler.XamlLoader));
	...
}
Here the first call is actually to enable attribute based mapping (for all loaded assemblies), and there after we specifically map the route for a loose-xaml file using the Url Identifier "Images/HeartYourWeb". And as this is a resource in the assembly we use the NavigationResourceHandler to help with that. For more information on mapping navigation routes please see my other blog posts, however the big-picture take-away should be that mappings serve the function of abstraction, in this case abstraction of resources as Urls.

Content Navigation

Given the mappings, we can use the registered Urls to navigate to the content; and for most purposes I recommend using the extensive-set of behaviours in nRoute to avail navigation. Throughout the MasterPage you will see exclusive use of the navigation behaviours for content navigation, show below are some examples:

<!-- NAVIGATE ON MOUSE DOWN ON TEXTBLOCK -->
<TextBlock .. nav:MouseDown.NavigateUrl="Pages/FuturePage/Folders" />

<!-- NAVIGATE ON Ctrl+H KEY DOWN, note this is part of a Behaviours Collection -->
<bhv:KeyUpNavigate NavigateUrl="Home/" Key="H" KeyModifiers="Control" />
In the first case here, we attach the navigate behaviour to a TextBlock which on the (LeftButton)MouseDown event will navigate to the specified Url. And in the second case, we have attached another behaviour on the UserControl, that on recognizing the Cntrl+H key combo will navigate to the "Home/" Url. As you can see for the most part this is all pre-defined, however it doesn't have to be, you can also use the Url Address bar at the bottom of the MasterPage to manually navigate to any valid Url (try about:blank). I hope you can see how we at first abstracted the content via Urls, and using the same Urls also realized them visually.

State Management

In order to use the state management facilities in nRoute, one has to opt in using a simple interface called ISupportNavigationState - which is the case with the FuturePageViewModel type. As mentioned earlier this is the View-Model class backing the FuturePage.xaml, and the logic is trivially simple - all it does is provide a random brush (see OnChangeColour which is exposed by the ChangeColourCommand) from the application resources, and also saves/restores both the selected brush and any text entered by the user. It also makes use of tokenized Urls, as specified in the MapViewModelViewNavigation attribute, have a look:

[MapViewModelViewNavigation("Pages/FuturePage/{Name}", 
	typeof(FuturePageViewModel), typeof(FuturePage))]
public class FuturePageViewModel 
	: ISupportNavigationState, INotifyPropertyChanged { .. }
In the Url registered we have the {Name} token, which gives us the UI's title to display; this token-value pair is availed in the InitializeState method via its "state" parameter (a name-value dictionary). So a Url like "Pages/FuturePage/LightUpTheWeb/" will display "Light Up The Web" as its title in the page, try here. This is rather vain, but you could easily get in your OrderId or CustomerId via the Url and do something more constructive.

The second key construct here is the SaveState method that basically takes the two key properties (the selected colour and entered text) and returns it in a dictionary - which represents the state of the page. Now when you navigate onto the page (via either Back/Forward/New navigation manner) the StatefullContainer will restore the state by passing back the dictionary you saved earlier (as seen in the RestoreState method), and in this case we just use that to restore the two key properties. It is really very straightforward, and the benefit being that the visuals (and the View-Model in this case) don't need to be persisted in the memory, only the relevant state is stored and restored as required by the container.

Action De-Composition

In addition to the content composition and navigation, nRoute also features a set of functionality that allows you to address actionable code via Url identifiers; please refer to my actions related post for detailed information. In the Demo app, you will find trivial examples of how to use the Action infrastructure, the snippet below (from WorkspaceNextPreviousActions.cs) shows an action handler that allows you to select the next or previous workspaces/blade:

[MapActionHandler("Workspace/Actions/Previous")]
[MapActionHandler("Workspace/Actions/Next")]
public class WorkspaceNextPreviousActions : IActionHandler 
{
    public bool CanHandle(IActionDefination action)
    {
        return true;
    }

    public void Handle(IActionDefination action)
    {        
        var _viewModel = ((App)Application.Current).WorkspacesViewModel;
        var _newIndex = 0;

        .. // implementation details

        // we select a new workspace, note the use of the dispatcher
        action.Request.Dispatcher.BeginInvoke(() => _viewModel.ActivateWorkspace(_newIndex));
    }
}
The idea behind actions is to decompose your application's reusable code into a set of independent handlers, which are addressable in a loosely coupled fashion using Url. In the case above we mark the IActionHandler implementation with two Url Identifiers (see the MapActionHandler Attributes) that either select the previous workspace or the next workspace as per the requested Url. Also observe that Actions are consumed off the UI thread, hence the use of the dispatcher [Line 18]. What makes the investment of creating IActionHandler worthwhile is the structured but loosely-coupled way of consuming the exposed functionality, as show below:
<!-- NEXT PREVIOUS ARROWS -->
<TextBlock ToolTipService.ToolTip="Select Previous Workspace" ... 
           acn:MouseUp.ActionUrl="Workspace/Actions/Previous"  />
<TextBlock ToolTipService.ToolTip="Select Next Workspace" ... 
           acn:MouseUp.ActionUrl="Workspace/Actions/Next" />
As you can see here, we have basically attached behaviours to the TextBlocks which on (LeftButton)MouseDown event execute the action identified by the Url (without any direct reference to the handler). Another point to note if you try and execute an Action Url that isn't registered, it will raise an exception - this is design, because Actions per se have no visual component and without any indicator to their existence, calls might go unheeded which is an unacceptable outcome. So Urls in nRoute might be very loosely-coupled locators, but they are also structured and unambiguous as content/action providers (i.e. not magic-strings).


PART III: SUMMARY

Even though the demo app lacks substantial content, it demonstrates a range of features available in nRoute - however, beyond the technical ins-and-outs I hope what is also apparent is what nRoute brings to the table. From my perspective, nRoute sits in-between the infrastructure code and the actual consumable/actionable content, it bridges the chasm between the two - which in turn makes your code more adaptable and equally your infrastructure more changeable, largely independent of each other.

EDIT: added the State Management section

Posted by Rishi on 16-Apr-09 1:46 PM, 23 Comments

Categories: nRoute

In this post, I want to describe how the Navigation Service works and how it relates to the all important INavigationHandler. To be honest, the Navigation Service is a very thin wrapper around the Routing Service; but it is semantically very important because it puts a context around the consumption of the routing responses - for navigation purposes obviously. I'll take on each part related to the Navigation functionality in a piece by piece manner, but first below is what I consider my mind-map relating to Navigation and Routing in nRoute:

Click for a Larger Picture

Navigation Requests

Basically all requests constitute of two principle things, one the Url and two a name-value collection; and this is accordingly reflected in the IUrlRequest interface. The other member in that interface is the ServiceState property, which is supposed to be for internal use of the services to carry any service-specific data. 
NavigationRequests
Now, the only thing that the NavigationRequest type adds is the the NavigationMode property, which is an enumeration that tells the container what kind of request (back, forward etc) we are dealing with - it's usage is container specific and some containers totally ignore it. However, I made it a first level concept to enabled browser integration and minimize ambiguity. All the same, for most purposes you will only need to be concerned with the the Url alone. In fact I suggest that you should try and minimize the need for passing name-value parameters, and consider using defaults parameters if necessary. One other point, the Url that you register or use should not contain a "?" and it should not start with either "/" or "~" (these are trimmed on use, but avoid them), I've carried these requirements intact from asp.net routing specifications as it made the relative Urls sort of consistent.

Navigation Response

The navigation response is based on the IUrlResponse interface, that defines five almost self-describing properties. The important ones are Content, which holds the actual result. And the other is ResponseParameters, which is basically a merged name-value collection of the original request parameters, the default parameters set while defining the route, and the parsed token-parameters from the Url. And priority level for merging is such that request parameters override Url parsed parameters, which in turn override the default parameters.
NavigationResponses 
The navigation response adds one things, which is the NavigationStateManager property of type ISupportNavigationState interface. The ISupportNavigationState is a mechanism to participate in state management but participation is optional, and also the reason it is separated from the actual result is to facilitate M-V-VM like pattern. So for example, the response itself can be an Usercontrol, but it doesn't have to implement the state management, whereas it's ViewModel can do that or a parent controller can do that. One last thing, the Error property only holds exceptions that are occur in the course of getting the actual response (from the IRouteHandler) and not say from an invalid Url.

IRouteHandler Implementations

You can think of the IRouteHandler as the your end of the bargain, that when posed the Url-specific request provides the response - which is basically the same in asp.net routing or MVC.  Now you can create your own handlers very easily, but I provide two build-in implementations - one specializes in loading assembly-embedded resources like images, xaml-only files, text files etc. The other, more general purpose one, takes in two delegates yielding the response and optionally an ISupportNavigationState object.   

// NavigationRouteHandler Constructor
public NavigationRouteHandler(Func<ParametersDictionary, Object> responseResolver,
           Func<Object, ISupportNavigationState> navigationStateManagerResolver) { ... }

In the first parameter of the constructor signature above, we pass into the Func the request's name-value collection and it is supposed to return the result, which we then pass into the second Func to get the state management related interface. However, like I said before, the second parameter to resolve the state is optional. Let me stress this again, you can provide your own implementation as per your needs, say for example a handler that dynamically downloads assemblies on demand.  

INavigationHandlerINavigationHandlerInterface

The INavigationHandler Interface is a very simple interface, almost ICommands'que. The ValidateRequest is asked as to if the handler can process the request, if false it yields a NavigationResponse will null content and Cancelled status. The ProccessResponse, is equally simple in that it is asked to process the response. Now, the inbuilt navigation containers all use this to get the response and display it, but if you were devious enough you will know that it doesn't have to be that. You could for example use this as a signalling mechanism or even get data - and you can get even more creative if used with navigation behaviours I mentioned in my last post. However, you must be careful because the calls to the handler are all synchronous, which means you can potentially block the UI thread inappropriately.

Navigation ServiceNavigationServiceClass

Like I said earlier the Navigation Service is a wrapper around the Routing Services, but it importantly provides the capability to inject responses into through the INavigationHandler interface. And it does use that mechanism, when you use the Navigate method that takes in a NavigationRequest and an INavigationHandler - to which it will given back the response to process. The other main method is Resolve, which takes in a NavigationRequest and gives out a NavigationResponse. That is important in a way, because it means you can get the response from without having to go through a handler. So you have two ways to get the response, but you will almost always use the Navigate method which in a manner of speaking injects the response into the passed in INavigationHandler container.

The MapRoute method provides a couple of different ways to register a Navigation Route, some of which I shown before. However, truth be told, in as far as registering routes you can totally forgo the NavigationService and register against either the RouteTable or the RoutingService itself. Lastly, the MapLoadedAssemblieRoutes and MapAssemblyRoutes methods provide support for mapping Url to content using attributes. MapLoadedAssembliesRoutes enlists all loaded assemblies and passes them to the MapAssemblyRoutes method which auto-registers navigation routes - as described next.

Attributes Mapping

In a lot of use-cases the "strong-by-convention" way of registering routes can frankly be burdensome, so I created an extensible AOPish way to auto-register Url and it's required IRouteHandler.

  MapNavigationAttributes

The shown MapNavigationBaseAttribute is the attribute the NavigationService looks for when asked to map an assembly, and then to register the navigation route it calls upon the attribute's ResolveHandler abstract method. That is very simple, but what this also means is you can create your own derived attributes to suit your loading strategy, say one that uses some kind of dependency injection tool. And the other obviously useful thing about attributes is that it is registered at runtime, so you can say load in a different assemblies on a per user basis (like don't load Admin related assemblies when the user is a not an Admin). I'll also show in another post a derived attribute that does M-V-VM automagically for you.

That's it for now, these posts are really getting long and dry - but the drudgery is necessary because these are meant to be persudo documentation for nRoute. Anyhow, I hope you have learned how the whole request in and response out thing works. I'll leave you with some immortal words from Karate Kid:

Daniel LaRusso: Hey, what kind of belt do you have?
Mr. Kesuke Miyagi: Canvas. J.C. Penny. Three ninety-eight. You like.
Daniel LaRusso: No, I meant...
Mr. Kesuke Miyagi: In Okinawa, belt mean no need rope to hold up pants.

Posted by Rishi on 04-Apr-09 3:27 PM, 17 Comments

As weird as the title sounds, I love what behaviours using attached properties bring to Silverlight and WPF. They are kind of what Javascript is to HTML elements - the wonder that extends controls beyond their limited initial intentions.

Now, with respect nRoute, what I did was map every event in almost all the controls in SL 2 base library to do three things, one navigate, two take an action and three execute an ICommand (sorry this is like a little mantra I keep repeating). The core point being, this is a very extensive body of abstraction that should help with over 75% of your UI interaction at the very least. Also, this is based on the Prism 2's behaviours' code pattern, but in their instance they were limited to Buttons - here it is starts right at the bottom from UIElement. Tabulated below are the behaviours available for events in nRoute:

Control Behaviour for Control's Event NAV ACN CMD
UIElement GotFocus x x x
  LostFocus x x x
  KeyDown x x x
  KeyUp x x x
  LostMouseCapture x x x
  LeftMouseButtonDown (named MouseDown) x x x
  LeftMouseButtonUp (named MouseUp) x x x
  MouseEnter x x x
  MouseLeave x x x
  MouseMove x x x
         
FrameworkElement BindingValidationError (Silverlight Only, N/A in WPF) x x x
  LayoutUpdated x x x
  Loaded x x x
  SizeChanged x x x
         
Control EnableChanged x x x
         
ButtonBase Click x x x
         
RangeBase ValueChanged x x x
         
Selector SelectionChanged x x x
         
ToggleButton Indeterminate, Checked, UnChecked (into one StateChanged Event) x x x
         
TextBox SelectionChanged x x x
  TextChanged x x x
         
PasswordBox PasswordChanged x x x
         
Scrollbar Scroll x x x
         
Image ImageFailed x x x
         
Timeline* Completed x x x
         
Abbreviations: NAV = Navigation Support, ACN=Actions Support and CMD = ICommand Support

Believe it or not these are almost all the events exposed by SL2 base-library controls with exception of four events (Tooltip's opened and closed events, and ComboBox's DropDownOpen and DropDownClosed events) and three other controls' (MediaScaleImage, MediaElement, and Thumb Control) events. Note, the Timeline object is a DependencyObject and not an UIElement derivative, so it exhibits a bit different usage semantics (explained ahead).

How do we use it? In Visual Studio, you import the relevant namespaces and basically attach them to what ever element you want. Below we have behaviours attached to a Button, Border, ItemsControls and it can be almost anything deriving for any of the classes listed above so thing like ListBox, CheckBoxes, Storyboard or your custom controls.

Declare XML Namespaces
Showing Navigation Behaviours Avaliable to a Border Control  Showing Loaded Action Behaviours Parameters for an Items Control

Showing Command Behaviours Parameters Avaliable to a Button Control
Samples of attached behaviours on various controls

Let me explain the three behaviours attached to the buttons in the screenshot just above. In the first case we have attached a behaviour that on the click event of the button executes the NewOrderCommand. In the second example, we have an action behaviour attached to the mouse-enter event of the button, and the action is identified with the Url shown. Also the action can have zero or more handlers at the back end, which can do whatever as necessitated by the business logic. Lastly, we are attaching a behaviour that will execute if there is a binding validation error, and it will navigate to the shown Url. Now, where (as in which container) the navigation occurs is configurable, but you can say put up a side panel that could show the content from that Url.

I just want to raise this point here again, you can attach to all the listed events above and execute any of the three categories of behaviours from just about any control, including your own. If you see the code for the demo app posted, I used these behaviours and had exactly zero code-behind the xaml files (except where I had to do element to element binding or triggering property updates - both these facilities are now there in SL3). This is very significant for both M-V-VM design pattern, and in general to raise the level of abstraction (along with flexibility, changeability etc benefits) from having to deal with Urls rather than instance types.

Now, the "bhv" namespace imported above is a bit special, it is specifically used to attach multiple behaviours. And because of how the internals of Silverlight work, it has one limitation which is you can't use binding expressions with it (the same is the case with the timeline behaviour), but you can use static resources binding expressions just fine. Below is an example of multiple bindings where the arrow keys are mapped to some action Url.

<!-- KEYBOARD ACTION MAPPINGS, MAPPED TO/WITH THE BORDER -->
<Border>
    <bhv:Behaviours.Multiple>
        <bhv:BehavioursCollection>
            <bhv:KeyDownAction Key="Up" ActionUrl="Move/North" />
            <bhv:KeyDownAction Key="Right" ActionUrl="Move/East" />
            <bhv:KeyDownAction Key="Left" ActionUrl="Move/West" />
            <bhv:KeyDownAction Key="Down" ActionUrl="Move/South" />
        </bhv:BehavioursCollection>
    </bhv:Behaviours.Multiple>
</Border>

Next, I wanted to explain the common properties you will see for each of the three categories of behaviours. As an exception note, KeyUp and KeyDown behaviours have two added properties specifying the key and the key modifier, rest all have the same properties as show below.

Navigation Behaviours

NavigationBehaviours

  • NavigateHandler: Here you can specify (rather bind to) any object that implements INavigationHandler to handle the navigation response. Note this is also a way consume navigation requests and responses in non-visual ways.
  •  NavigateHandlerElementName: Alternatively if the handler is an element in the user interface you can give it's element name and it will be asked to handle the result of the navigation. NavigateParameters: These are name-value collections you can pass with the request.
  • NavigateUrl: Obviously the Url to navigate to, and it is required.
  • NavigateUrlStringFormat: This is used to provide a format for the NavigateUrl property, it is similar to what was introduced in .NET 3.5sp1 binding and it helps in that it can avoid the use of type convertors in some scenarios.

One thing to know, and this is important, is how the behaviour chooses the container that will handle the response. Firstly, if you have specified the handler (either by binding or by an ElementName) it will just use that and life is simple. If you do not specify, it will look in it's visual tree first and try and find the first parent that implements INavigationHandler interface. This is also means that it works kinda like a browser whereby when you click a link the immediate most container (think IFrames here) handles the navigation for you. If no handler is found in the parental visual tree, it will look at the current application object and see if it has is a registered container (of type INavigationContainer) and use that. If it still doesn't find a container at the application level, well it gives up and throws an exception.

Action Behaviours

ActionBehaviours

  • ActionHandler: Here like above you can specify (rather bind to) any object that implements IActionHandler interface to handle the action request. Note the handler doesn't need to be registered against with the ActionService, and so this is also a way to bypass the registered Url handlers ie. they will not be called.
  •  ActionHandlerElementName: Alternatively if the handler is an element in the visual tree you can give it's element name and it will be asked to handle the action. Again, as above this is a way to bypass the registered action handlers.
  • ActionParameters: Like before these are name-value collections you can pass with the request.
  • ActionUrl: Quite intuitively the Url for the action, and it is a required parameter.
  • ActionUrlStringFormat: This is used to provide a format for the ActionUrl property, and helps in that it can avoid the use of type convertors sometimes.

The resolution of the action is basically similar to the navigation behaviours, but with exception that it doesn't look up in the visual tree for anything that implements IActionHandler and directly goes onto any registered handlers for the action Url.

Command Behaviours

CommandBehaviours

  • Command: You basically specify, like WPF, any object that implements the ICommand interface.
  • CommandParameter: And the accompanying parameter can be specified here.

In this case the hooking up is rather direct, you must have specified the ICommand implementing handler and the behaviour will execute it for you whilst also passing in the parameter. This helps overcome the lack of build-in support for ICommand but also extends the usage semantics in WPF.

As this has gotten quite long I'll skip the demo for another post, but I hope you can see how these behaviours bridge the element's events with a handler/consumer either directly (like with ICommands) or indirectly (via Urls) saving you a lot of work and raising the abstraction level. In fact off the 35,000+ lines of code in nRoute 60% are dedicated to bring you these behaviours (over 75 of them) - so it's quite a bite.

Posted by Rishi on 03-Apr-09 3:05 PM, 19 Comments

Categories: Architecture, nRoute

In the data world people often say that the database structure outlives any application consuming it, I suppose in the Web world it can increasingly be said that the Url will outlive any web application represented by it. In this context, nRoute is an attempt to make URLs a first class construct in Silverlight and WPF applications. Today, I've got the first release out on Codeplex, along with a sample application.

I am surely repeating myself, but below is the highlight of what is in the box today. I will go into details about each feature in a series of forthcoming posts that unfortunately will have to double-duty as some semblance of documentation, as does this post.

1. Routing Services: This is a direct port of the asp.net routing made available in .Net 3.5 release. However, in this case it deals exclusively in relative Urls and the semantics are changed in that it returns an IUrlResponse for a IUrlRequest send in.

Routing Services Overview

Normally you will not have to interface with the routing services, because you will consume services that build upon this.

2. Navigation Services: This deals exclusively in a navigation type requests/responses model that is usually consumed by some kind of a visual container. Just think of how the web works, you send a request, which is followed by a response that is consumed in a container (better known as a browser). Now, you don't really need a container to handle the response, you can consume it directly by using the NavigationService.

Navigation Services Overview
Again this is a bit low level plumbing you will normally deal with the NavigationService which is a static class and also with two build in IRouteHandler implementation that help serve the responses. The two handlers are NavigationRouteHandler and NavigationResourceHandler, let me show you the usage API for them:

// Navigation Route Handler usage
NavigationService.MapRoute(new Route("Customer/NewOrder",
    new NavigationRouteHandler(p => new DummyControl1(), u => (ISupportNavigationState)u)));

NavigationService.MapRoute(new Route("Customer/EditOrder/{OrderId}/",
    new NavigationRouteHandler(p => new DummyControl2(), u => (ISupportNavigationState)u)));

// Navigation Resource Handler usage
NavigationService.MapRoute("Images/MixLogo/", new NavigationResourceHandler("Images/MIX09Logo.xaml",
    NavigationResourceHandler.XamlLoader));

NavigationService.MapRoute("Images/2/", new NavigationResourceHandler("Images/iPhone2.jpg",
    NavigationResourceHandler.ImageControlLoader));
This is very similar to what is there is asp.net routing, basically all we are doing here is mapping an URL to a lambda expression that can initialize the responses on request. The second type of IRouteHandler shown above is the NavigationResourceHandler type, which basically makes it easy to materialize assembly resources as responses to any URLs like an image, text-file, or an xaml only file.

Again there is a higher level abstraction available for consuming the Navigation Services, please reference my forthcoming posts.

3. Action Services: This essentially is like a mapping for Urls to some handlers that work in the background. Think ICommands as a usage-model for this, basically you send in an ActionRequest that is handled by one or more IActionHandlers asynchronously off the UI thread. Usage scenarios include, fetching some data or processing some images all with the flick of an Url. Note, the action services do not produce a response, however they can issue some kind of response or signal once they are finished on the dispatcher thread.

Action Services Overview

This might seem quite weird but once you get a hang of it you will see how it really helps in usability and getting rid of the code-behind. The following is an example of registering an action and it's handler.

// We need to register an action url
ActionService.MapAction("Route 1", "Messenger/{MessengerType}/{ActionType}");

// and follow it up with by registering a handler for the action
ActionService.RegisterHandlerByRoute("Route 1", new MsnMessengerHandler());

// we can add another handler
ActionService.RegisterHandlerByRoute("Route 1", new GTalkMessengerHandler());
Here we register an action Url, we follow it up by registering two handlers that respond to the action call. Yet again, there is a higher level abstraction available for consuming the Action Services.

4. Navigation State Management: One of the thing the web is fraught with is state management (just see how bulky Viewstates can become or how complex forms handling can be), but they are core to the navigation experience that the web offers. Everyone is now familiar with back and forward semantics and my attempt to capture that is with these three interfaces:
StateSupportOverview

Ideally you will implement these interfaces in your ViewModel and when registering your Url handler you will point to how we can extract/get the ISupportNavigation interface. Note, it's use is optional.

5. Navigation Containers: When we navigate we do so in a container, and that is what these quintessentially are. However, they play the very important role of modelling different ways to navigate like a browsing with back-forward capabilities or navigating onwards only. I have four containers built in, shown below.


NavigationContainersOverview

The four objects to your right, are actual usable visual controls that reflect the models onto their left.  Below is a summary of the behaviour exhibited by the four controls:

  NavigationContentControl This is a vanilla and light-weight content control that can handle a navigation response and that's it. It offers no navigation history, events or related behaviour. Think Vanilla Light.
  NavigationContainer This is also a plain control that inherits and extends the NavigationContentControl but offers a richer model with events and hook ups at the application level. However, it stores no history, state or anything and it plainly navigates from one url to another. Think Direct and Onwards.
  BrowsingContainer This builds on the NavigationContainer and offers a web-like behaviour and state management features. So it stores a stack of back pages history and states it has been to and the forward pages you've backed-up on. Plus, like a browser the state is only stored for back or forward pages, and opening a new pages initializes a new state. Think Very Web-Like.
  StatefullContainer This one basically stores the state of the page when you leave it, and restores it when you navigate onto it. This doesn't have a stack of back or forward pages history, it keeps one list of states and applies it whenever you navigate onto the page. So simply your last state is restored once go navigate to the page. Think Good Memory.

There is a lot of detail in these controls that will require a separate post, but the point to take away is that you can easily create your own container - it's not too hard. And your custom container can exhibit whatever semantics or behaviours that suit your needs, for example you can extend these to store the state in isolated storage and instantiate it back on arrival so that the user can continue where he/she left. I assure it this is very extensible, which is why you see a lot of base classes and interfaces.

5. Application-level Integration: I describe nRoute as an application-flow framework and to fulfil those ideals I created a set of "Application" types that mirror the containers models I listed above. Think of these as application-level containers, that in effect link up with a container say on the root visual. So what does that mean, it means you set up a default container for navigation through out the application using hooks-up with the actual container. I suppose this is a bit hard to describe, until you see it in action, but below is a visual connection between the controls and the application they tie up with.

NavigationApplicationModelsOverview

I am not sure how much you can get out of these diagrams, but again the core idea is to register any container as an application-level container to which Urls can be navigated to by default - without having to specify who is going to be the handler. Please see the sample app for what this in effect means.

6. Browser Navigation Integration: This is already getting very long, but one important thing the "application-containers" also provide is Browser integration - which means deep-linking and deferring to the browser-shell for things like navigating back-forward and stuff. I have a very cool implementation, that uses the UnFocus History Keeper script but without any js files to reference etc in the HTML page as it just injects the javascript into the page. Critically, the javascript file itself is only 4KBs uncompressed, and is embedded into the assembly itself.  Like everything else you can extend this too, and for details you can look forward to a future post (so I'll skip another class diagram :).

There is so-much detail to elucidate with a framework like this, but I am perhaps poor in both explaining and documenting - still my hope is, this helps. Forthcoming will be a series of posts that will go over each aspect in detail. But as a highlight please do check out two things, one the sample application with full source code and second a post on how you can consume all this with a higher level API. Both these posts will give a richer idea of what's possible, than the plumbing thesis I just gave. Also, I would greatly appreciate any feedback on the API and usability aspect of the code, especially since this is my first open-source project.

Code at http://nRoute.codeplex.com - as of now specifically for SL2 no WPF version yet, but should work with SL3.

The la MVC routing framework I showed last week, well I have decided to call it "nRoute" (pronounced enroute). With "n" being ambiguous for either any or .net, which by some coincidence carries the suggestion to route anything or routing for .net ;)

So now that it has a name badge, let see what's under the hood:

  1. Routing Services

    The routing service is almost a direct port of the asp.net MVC routing engine. However, in this case it is relatively more generic and has a more forthright request-reply semantics. Also, we only do play with relative URLs here, that said you normally wouldn't have to interface with this directly. This falls under the Routing namespace.

  2. Navigation Services

    This builds directly atop the routing services, but offers its own set of APIs to handle navigation functionality. The interesting thing (and what I wacked my brain around a lot for) is that you can navigate against a handler (as in saying you want this thing to handle the navigation for this URL) or directly consume the navigation reply. That flexibility befits a lot of edge-cases scenarios, yet is very simple. This falls under the Navigation namespace.

  3. Navigation Containers

    I like to think navigation containers represent models for consuming navigation in different ways. They ultimately manifest as UI controls, but what they represent is different ways to navigate like a browsing with back-forward capabilities or vanilla navigating without any trace. In WPF you have something similar, but it is inherently very opaque and inflexible in this case it is relatively simple to cook up a custom container. However, you can do without them entirely. They fall under the Navigation.Containers namespace.

  4. Application-Level Integration

    One of the most wanted features in RIA apps is deep-linking, and that requires hooking with (at the application-level) a navigation container to handle URL requests. For that purpose, I want to provide a specialized Application class with accompanying navigation events, and maybe a Visual Studio template to shrink-wrap it. Also, I have modelled an asp.net like Site Map API, again for application level consumption. This is work in progress. 

  5. RoutingCommand Services

    The idea is to use URLs as signifiers of commands, kinda like the commanding pattern in WPF. I am still working on the details but I think it might useful in application-wide commanding scenarios, especially since it is loose-coupling through URLs, and like elsewhere we can also pass-in name-value parameters. Further, I am looking to support event type multi-casting, but it is still entirely on the drawing board.

  6. Re-routing Services

    The idea here to have some kind of built-in re-routing capabilities that allows you to change the URL without breaking existing links and the apps. This is just a future-proofing initiative, as it might be useful in cases where you have many independent / modular components and links need to be changed. Also work in progress.

That's a 10,000 ft overview; I hope you enjoyed the flight and the pilot informs me the code will be landing soon on Codeplex (too cheesy?)

Anyway, I am well aware that possibly by next week this could all be an elaborate road-kill courtesy Silverlight 3, but I am holding the fort till then. In any case, SL 3 will definitely have an application-level navigation framework, but my understanding is that it will be more akin to asp.net type mapping with real folder/file-path mapped one-on-one with relative URLs. Thus, the contrast it might have with nRoute will be in the same order that MVC routing has to asp.net file mapping; lets see how it figures.

One other thing is even though nRoute offers a somewhat different paradigm for application flow, it doesn't pre-empt the use for frameworks like Prism or Caliburn. In fact I would rather this be used in hand with the mentioned frameworks.

Posted by Rishi on 15-Mar-09 12:48 PM, 13 Comments

Categories: Architecture, Silverlight

In a lot of things that MVC brought (besides reminding me how much I hate classic asp) what I loved the most was the routing engine. The routing feature was beautiful because it provided for loose-coupling, whilst being structured and rich enough to be actionable. Given that, and for structural and development modularity purposes I adopted the .NET 3.5sp1 routing engine for an URL based-navigation framework for Silverlight.

Basically it works pretty much like MVC/asp.net routing, you set up the routes to yields UI Objects. In this case we pass in a relative url (like "Customer/EditOrder/39/") along with an optional name-value collection (a dictionary basically) as request parameters. You can think of the request parameters as QueryString's collection or Form's input-value collection. What you get out from the routing engine is a "NavigationResponse" type, that contains a UI Page (basically a FrameworkElement) and another name-value collection. The name-value collection in the response is a merged collection of the request name-values, plus what it parsed from the URL and any defaults name-values you had set up with the route.

The other thing I've tried is to marry navigation with the routing, particularly because Silverlight controls aren't inherently statefull as pertinent to navigation - think html pages, like when browsing a web-form and you click back, the page is rendered in a state you left it in, with some exceptions to the rule.  How we achieve this "statefulness" is with the following interfaces.

PageState

Mostly we just use the IPageNavigator interface that brings together the two base interfaces, along with a title property. Hopefully it should be self-descriptive, but IPageInitializer interface is called to initialize a page pre-displaying it. The InitializePage method takes in a name-value collection, which as you can guess comes from the routing engine's response. The IPageState interface is used to extract and inject state into a page, so when we browse forward or back the UI get's a name-value collection to help it restore it's state. It's kind of like the viewstate in concept, but you have to put the relevant state in and out yourself as the controls themselves don't collude in state management. Also one of the thing we want with M-V-VM pattern, is to manage the state in the ViewModel - these interface helps with that by separating the state management. Additionally, the routing engine is geared to take this into consideration, so in the code to plug in routes we register a route handler with the following constructor.

public NavigationRouteHandler(
	Func<ParametersDictionary, FrameworkElement> pageResolver,
	Func<FrameworkElement, IPageNavigator> navigatorResolver) 
{ 
	... 
}

Basically, it takes in two handlers, one to get a FrameworkElement for displaying in the UI and the other to get the page navigator (IPageNavigator) out for helping with the navigation.  This separation of the FrameworkElement and IPageNavigation is in recognition of the ViewModel, but it doesn't force you - so, if you don't need a IPageNavigator or don't implement the interface just pass in null. Or if your page itself implements the IPageNavigator, then return the page. All the three scenarios are shown below, which also shows how to register routes, defaults and constrains:

void SetupRoutes()
{
	// we can pass in default values in routes
    var _defaults = new ParametersDictionary();
    _defaults.Add("OrderId", -1);

	// we can also pass in constraints 
    var _constraints = new ParametersDictionary();
    _constraints.Add("OrderId", "\\w{2}");

	// here we register a route with no IPageNavigator
    RouteTable.Routes.Add("Sample1", 
		new Route("Customer/NewOrder", null,
        new NavigationRouteHandler(p => new DummyControl1(), null)));

	// here we register a route, with the page itself implementing 
	// the IPageNavigator and it also has default values passed in
    RouteTable.Routes.Add("Sample2", 
		new Route("Customer/EditOrder/{OrderId}/", _defaults,
        new NavigationRouteHandler(
			p => new DummyControl2(), u => (IPageNavigator)u)));

	// here we register a route with the page's ViewModel implementing the 
	// IPageNavigator via the DataContext and we also have constraints set
    RouteTable.Routes.Add("Sample3", 
		new Route("Customer/DeleteOrder/{OrderId}/", null, _constraints,
        new NavigationRouteHandler(
			p => new DummyControl3(), u => (IPageNavigator)(u.DataContext))));

}

This is very similar to asp.net or MVC routing, the difference being we have a custom route handler for Silverlight / WPF that implements the IRouteHandler. And if you don't like it or your setup is hooked up differently, just create your own IRouteHandler implementation and you are good to go. Essentially it has the same extensibility semantics that MVC has, and the workflow is geared towards a request-response type of setup. I hope you did notice the {OrderId} parameters in the route URL, they are parsed and included in the response's name-value collection. One omission for Silverlight is that we can't pass in anonymous types to set up constraints or default values because we can't reflect on them outside the declaring assembly.

Like I said earlier I have tried to marry routing and navigation, and so we have something called navigation containers. The containers are simply interfaces that imply a navigation model, and I have two of them build-in - one is a simple navigation container which you pass in a URL and it just returns the UI but doesn't store any history or anything. Another one is called BrowsingContainer that stores history, caches the state, and can browse forward or back. Just like everything else you can create you own containers to implement say roles-based validation on URls or extend them for deep-linking within the browser.

NavigationContainers

A benefit with these interfaces is that you can expose them through a dependency injection container, and thus loosely couple the "pages" within the app. Another important point to remember is that you route against or rather navigate in a container even though the routes are registered statically/globally - this means we can have two or more navigation containers controls (think WPF BrowserControl) and each will work with all the routes registered. I also use these interfaces as ViewModels for navigation container controls, so you can easily create your own custom skins with all the glitter and jazz you want - I just sampled one with the Silverlight.FX transitions. It's quite easy.

There is a lot more to say, but for now lets keep it for the future - however I am working on integrating this with Prism 2 and creating an asp.net like site map control. In the sample below do notice that the pages keep their state during back/forward navigation and their memory consumption is constrained to the loaded page (plus the saved states).  I'll put up the code later on CodePlex, it needs some scrubbing, Cheers.

Get Microsoft Silverlight

PS: I wanted to mention and thank this excellent series on MVC routing.

Posted by Rishi on 10-Mar-09 5:39 AM, 18 Comments