nRoute: Silverlight abstractions of the higher kind

Posted by Rishi on 03-Apr-09 2:17 AM - Comments (5)

| Categories: .NET, nRoute, Architecture

200px-Close_Encounters_posterIf I could annotate a tag line for the software industry as a whole, I would put down "we are in the business of abstractions". And perhaps, if I was in marketing for the software industry, I would append that with "because you can't have it all". Well, more to the point, in my last post I mentioned a lot of working parts that constitute the nRoute Framework while also repeatedly mentioning there is a higher level API, and that is what I want to cover in this post.

First, how is nRoute an abstraction of a "higher kind"? In two ways, it abstracts resources and actions in the form of URLs (or more correctly as URL requests). So for example you want to show the customer page you abstract it as a relative Url like "Customers/Details/StevenSpielberg/" and similarly you want to abstract a Log Entry action with a relative Url like "System/Log/Critical/" whist passing in the actual log object in a name-value collection with the request. This also represents classic loose coupling, and what it gives you is the ability to link-up actual .net objects/controls in a systematic and structured way without having direct reference to each other (very much like hyperlinks) - of which I am sure you know benefits. Another characteristic this yields, is resilience to both changes and partial break-downs for a lack of a better word. Think about how the Internet works, just because a link has 404'ed it doesn't mean the entire system comes with an unhandled exception - it still works albeit without the missing resource. And just to point the obvious, with Urls if you say put in a new user control for the the customer's details page you wouldn't have to move a dime elsewhere ,as long as it still registered at the same Url.

Firstly, let me show you how you consume Urls within the UI. Below is a simplified xaml snipped from my demo app, in which you have a "Home" persuado button (Border actually) that links to the "Home/" Url.

<!-- nav NAMESPACE -->
xmlns:nav="clr-namespace:nRoute.Behaviours.Navigation;assembly=nRoute.Silverlight"

<!-- HOME BUTTON, with STYLES -->
<Border Style="{StaticResource HomeButtonBorderStyle" nav:MouseUp.NavigateUrl="Home/">
	<TextBlock Style="{StaticResource HomeButtonTextStyle}" Text="HOME ." />
</Border>
As you can see the xaml is very simple, we are using a border to wrap a text block that reads home with a dot. What is interesting is the "nav" namespace, which brings into play attached properties that allow us to register a behaviour with the border control - the behaviour effectively says on "MouseLeftButtonUp" event navigate to this Url. That's it, and you are done as long as the Url is registered to return a visual it should work. The question of how it works, I'll need to defer to another post but basically in this case we have a Navigation Container that is registered at the application level which would handle this request.

Second example is a xaml snipped again from the demo app, and it's for registering actions to take on certain keyboard events. If you have seen the sample app, it has 3 workspaces (aka blades) onto which you can navigate, however by design it is actually configurable from 1 to 9 workspaces.  So in the code below we register keyboard shortcuts for shifting from one blade/workspace to another (though in some case it does conflict with the browser shortcuts).

<!-- bhv NAMESPACE -->
xmlns:bhv="clr-namespace:nRoute.Behaviours;assembly=nRoute.Silverlight"

<!-- KEYBOARD ACTION MAPPINGS, MAPPED TO/WITH THE ROOT USER CONTROL -->
<bhv:Behaviours.Multiple>
    <bhv:BehavioursCollection>
        
        <!-- WORKSPACES 1 to 9 MAPPED WITH NUMBERS  -->
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/1" Key="D1" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/2/" Key="D2" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/3/" Key="D3" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/4/" Key="D4" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/5/" Key="D5" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/6/" Key="D6" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/7/" Key="D7" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/8/" Key="D8" KeyModifiers="Control" />
        <bhv:KeyUpAction ActionUrl="Workspace/Actions/Select/9/" Key="D9" KeyModifiers="Control" />
        
        <!-- OTHER MAPPING -->
        <bhv:KeyUpNavigate NavigateUrl="Home/" Key="H" KeyModifiers="Control" />
        
    </bhv:BehavioursCollection>
</bhv:Behaviours.Multiple>
Again we are using attached behaviours, and in this case we are attaching to the root Usercontrol, which is also used as the root visual of the app - so kinda this is like global keyboard mapping if you may. As apparent, we are importing the "bhv" namespace  which allows us to register multiple behaviours against a single element (with a caveat). And we are attaching KeyUp events for Ctrl+(Digits 1 to 9), against an action Url that was registered as "Workspace/Actions/Select/{WorkspaceIndex}". The action handler for that Url basically gets the token WorkspaceIndex and it changes the active workspace using the workspaces' ViewModel; pretty simple really, and it gets rid of the ugly code-behind file like forever. There is another mapping shown below that, which is using Ctrl+H KeyUp events to navigate to the "Home/" Url. Now, equally well you can do all this with code and but you'll have to use the NavigationService and ActionService static classes for the same.

Now, there are a lot of attached behaviours in nRoute to do three main things:

  1. Navigate to an Url
  2. Take an Action
  3. Use an ICommand

You can attach these with almost all Silverlight build-in controls events, and I mean almost all. There is so much detail in these behaviours that I'll have to post it separately, but suffice to say, if you don't use nRoute take out the ICommand behaviours at least and use them - you will spare a lot code and time.

nRoute Demo App : Future Desktop
The demo app, available with complete source code, makes uses of all the features mentioned here.

The requirement for using the enlisted features is registering navigation and action Urls (with handlers to be precise). In my previous post I've shown how you can register via code, but now, I wanted to show the "higher abstraction" version of the same.

using System.Windows.Controls;
using nRoute.Navigation;

namespace nRoute.Samples.FutureDesktop.Pages
{
    [MapNavigationContent("Home/", "Home Page")]
    public partial class HomePage : UserControl
    {
        public HomePage()
        {
            InitializeComponent();
        }
    }
}
This is the code behind for the Home Page of the demo app, and there is nothing special except for the little attribute named MapNavigationContent. And it does exactly that, maps the resource (you have put it above-on) as an Url and also gives the page a title which is pretty self-evident here. And that's really it; moreover if you page were to implement either ISupportNavigationState or IResolveNavigationState it would also pick that up and use it for state management automatically. One other way to use this is to put it on your ViewModel class and specify the "NavigationContentType" parameter of attribute, and it will use the given NavigationContent type as the content and target type (of the attribute) as possibly the ISupportNavigationState provider. One additional point, you can put multiple of these attributes to respond to multiple Urls on the same class.

Now  registering actions is purposefully very similar to what is shown above. Here we have an action handler class that handles the foreshown KeyUp events. Also, note it doesn't point to any particular KeyUp event or any potential triggering mechanism, it just handles the action and the Action Service + behaviours take care of the plumbing infrastructure.

[MapActionHandler("Workspace/Actions/Select/{Number}")]
public class WorkspaceSelectionAction : IActionHandler
{

    const string NUMBER_KEY = "Number";

#region IActionHandler Members

    public bool CanHandle(IActionDefination action)
    {
        return (action.ParsedParameters.ContainsKey(NUMBER_KEY));
    }

    public void Handle(IActionDefination action)
    {
        // if we can't get the value
        int _newIndex = -1;
        if (!Int32.TryParse(action.ParsedParameters[NUMBER_KEY] as string, out _newIndex)) return;
        
        // we get the view model and check if the index is avaliable
        var _viewModel = ((App)Application.Current).WorkspacesViewModel;
        if (_newIndex < 0 || _newIndex > _viewModel.WorkspacesCount) return;

        // else we activate, note the use of the dispatcher
        action.Request.Dispatcher.BeginInvoke(() => _viewModel.ActivateWorkspace(_newIndex));
    }

#endregion

}
The one important thing in the code is the use of the dispatcher, since actions are called-upon asynchronously and off the UI-thread, we need to use the dispatcher to effect any changes onto the UI - that's just the standard platform stuff.  Plus like the navigation mapping attribute, you can apply multiple attributes to the same handler. Also note, for demonstration purposes I separated this from the Workspaces' ViewModel, but you could just as well have tacked it onto the ViewModel class using an ICommand.

How do the mappings get registered? Well, in this case we make two calls on the action and navigation services as show below. This is from the demo app's App class, where we override the custom base applications class's register methods which kind of akin to overriding in global.ascx class in asp.net:

protected override void RegisterNavigationsRoutes()
{
    NavigationService.MapLoadedAssembliesRoutes();

    // we register the images
    NavigationService.MapRoute("Images/HeartYourWeb",
        new NavigationResourceHandler("Images/HeartYourWeb.xaml", NavigationResourceHandler.XamlLoader));
    NavigationService.MapRoute("Images/MixLab",
        new NavigationResourceHandler("Images/MIX09_Color_RGB_tag.xaml", NavigationResourceHandler.XamlLoader));
    NavigationService.MapRoute("Images/MixLogo",
        new NavigationResourceHandler("Images/MIX09Logo.xaml", NavigationResourceHandler.XamlLoader));

}

protected override void RegisterActionsRoutes()
{
    ActionService.MapLoadedAssembliesActions();
}
The two evidently important calls are MapLoadedAssembliesActions and MapLoadedAssembliesRoutes that basically enumerate all public types and look for the aforementioned attributes. You can also see, I am registering three xaml-only resource files that don't have a code-behind file in the same call. Moreover, I purposely chose not automatically map loaded assemblies, so that you can use whatever loading strategy that suits you - including dynamically loading and mapping assemblies. All in all, this is a very simple way of doing the whole kibosh of Url routing et al., but using attributes for mapping has limitations in that you can't specify default or constraints for Url tokens on attributes.

I hope I've show how you can perhaps work at a higher abstraction with respect to the constructs and plumbing in nRoute. Unfortunately, there is a lot more to show and tell, and I defer again to future posts especially with respect to using this with M-V-VM and custom loading/mapping strategies. My next post will go much deeper into behaviours and how they really are "code-savers" in the ever enduring adventures of "abstractions of the higher kind".

Comments

trackback
Ork Pad
on 22-Mar-09 9:13 PM
Trackback from Ork Pad

Introducing nRoute: an application-flow framework for Silverlight and WPF

trackback
DotNetKicks.com
on 24-Mar-09 6:07 PM
Trackback from DotNetKicks.com

nRoute: Silverlight abstractions of the higher kind

trackback
DotNetShoutout
on 24-Mar-09 11:11 PM
Trackback from DotNetShoutout

nRoute: Silverlight abstractions of the higher kind

Bill
Bill United States
on 05-Jun-09 8:33 PM
thanks
just learn something.

trackback
geff zhang
on 19-Jun-09 7:20 PM
Trackback from geff zhang

?? nRoute ??????? Silverlight ?????

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading