Thursday, 31 March 2011

WP7 - URI Mapper

Overview
I am always on the lookout for ways to improve code; short term benefits are nice but there is a great feeling in identifying potential problems way in advance, and putting in the framework early to make life easier later. Using a UriMapping when developing WP7 apps is one of the many ways you can help eliminate annoyances later.


So first of all, a brief description of how page navigation works in WP7, what a UriMapper is, and why it pays to use it. Scroll down passed these descriptions if you'd rather just see how to implement one.


WP7 Navigation
For those unfamiliar with the navigation architecture in WP7, and Silverlight navigation in general, you can navigate between different pages of your application by utilising a centrally available NavigationService. From this, you can switch to a different page using the following:


String uriString = "anotherPage.xaml";
NavigationService.Navigate(new Uri(uriString, UriKind.Relative));


Where "anotherPage.xaml" is relative to the current page - in this case, it's in the same directory. What you're doing here is explicitly identifying the page you want to move to, and then instructing the navigation service to take you there. It's nice, it's simple and it works well. It is one of these systems however, that work extremely well for small apps, but quickly become a maintenance nightmare as soon as your app scales. 


Why? Well let's say you end up with quite a few different ways of navigating to your "anotherPage.xaml", when you suddenly realise, "Hey, I'm not a rubbish programmer, why the hell have I called my page something entirely meaningless?!* I'd better change that." So after renaming your page, you then have to go sift through your code attempting a horrific and always trouble-prone copy/replace. Or perhaps you're building a library which will be embedded in someone else's application. Your library contains some page layouts and you want to allow these pages to be navigated to. Having the containing app reference these layout files directly could be a nightmare if you chose to rename the layouts later. There must be a better way. 


Uri Mapper
There is a better way of course, and as you may have gathered it involves the use of a UriMapper. A UriMapper allows you to define, in XAML, a mapping between the explicit layout file (the XAML) and a Uri of your choosing. It effectively allows you to decouple the knowledge about the layout file, and give it an alias of your choosing. This means should you rename the layout file later, you only have one place in the entire application to reflect this change.


Defining a UriMapper
As I mentioned, a UriMapper is defined in XAML. Open up your App.xaml, which should be living in the root of your project. You will see the <Application.Resources> tag and the corresponding closing tag. It is within these tags that you want to add your mappings. Note, you will have to include a statement to use the nav namespace. Here's an example:



xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=Microsoft.Phone"


<Application.Resources>
    <nav:UriMapper x:Key="uriMapper">
        <nav:UriMapping Uri="/BetterNamedPage/{param}" MappedUri="/view/anotherPage.xaml?param={param}" />
    </nav:UriMapper>
</Application.Resources>



You have now defined a mapping between BetterNamedPage and /view/anotherPage.xaml meaning that if you rename the xaml file, or even move it to a different directory (as we have here in this example), this mapping is the only place in the application you will have to update. Also note here, we are allowing for a parameter to be passed as part of the query string; the use of a UriMapper doesn't stop us from doing this.


Instructing your App to use the UriMapper
After defining in your App.xaml, you need to actually put your mappings to use. To do this, open the code-behind, App.xaml.cs, and insert the following line to the end of your public App() constructor:



RootFrame.UriMapper = Resources["uriMapper"] as UriMapper;



And that is you done. You have defined your mapper and set your app to use it. Using this method, your mappings will be available for use across the whole app (and not just at the individual page method).


Navigating Using the Mappings


Let's now convert our early example to use our shiny new mapping. The hardcoded way:


String uriString = "/view/anotherPage.xaml";NavigationService.Navigate(new Uri(uriString, UriKind.Relative));


now becomes:



String uriString = "/BetterNamedPage/paramValue";
NavigationService.Navigate(new Uri(uriString, UriKind.Relative));



Not much of an immediate improvement, sure, but hopefully you will now see why this might save you a fair bit of hassle in the long run, as your app scales, as the pennies roll in your bank and you'd rather be focussing on other things.


* An interesting aside, after a recent conversation at work, it has come to my attention that there is actually a name for a question mark followed by an exclamation mark. It's called an interrobang, and can actually be written as 



Well I say interesting.... but is it really?! See what I did there?