Custom routing using the Uri Mapper in Silverlight 3

by StefanOlson 3. June 2009 15:34

Update: see the latest version of this code here.

The new navigation architecture in Silverlight 3 is very exciting as it substantially improves the viability of Silverlight for use replacing html websites (except for the lack of a hyperlink class, which I can't understand not being included in Silverlight 2). I'm currently developing a website which will be running Silverlight 3 and have discovered some interesting issues with the URI mapping architecture as it currently stands in the beta.

The first thing I wanted to do with the URI mapping architecture was to add my own routing system where I could examine the URI and decide which page to send it to.  Out of the box the Silverlight has a Uri Mapper class that allows you to use regular expressions to convert the display URI into the URI you wish to use.  For example (in App.xaml):

<Navigation1:UriMapper x:Name="_uriMapper">
    <Navigation1:UriMapping Uri="About" MappedUri="/Views/About.xaml"/>
    <Navigation1:UriMapping Uri="About/{person}/" MappedUri="/Views/About.xaml?person={person}"/>
</Navigation1:UriMapper>

Unfortunately with the way that the URI mapping is structured at the moment, the only way to do this in code, rather than in xaml is to completely duplicate the UriMapper class and create your own specialized type of mapping. Ideally, there should be a UriMappingBase class and UriMapping should be derived from that. The classes that I have developed for this are shown below:

[ContentProperty("UriMappings")]
public class CustomUriMapper : UriMapperBase
{
    // Methods
    public CustomUriMapper()
    {
        UriMappings = new Collection<UriMapping>();
        CustomUriMappings = new Collection<CustomUriMapping>();
    }

    public override Uri MapUri(Uri uri)
    {
        Collection<UriMapping> uriMappings = UriMappings;
        if (uriMappings == null)
        {
            throw new InvalidOperationException("MustNotHaveANullUriMappingCollection");
        }
        foreach (UriMapping mapping in uriMappings)
        {
            Uri uri2 = mapping.MapUri(uri);
            if (uri2 != null)
            {
                return uri2;
            }
        }

        foreach (CustomUriMapping mapping in CustomUriMappings)
        {
            Uri uri2;
            if (mapping.MapUri(uri, out uri2))
            {
                return uri2;
            }
        }
        // now nothing...
        return uri;
    }


    // Properties
    public Collection<UriMapping> UriMappings { get; private set; }
    public Collection<CustomUriMapping> CustomUriMappings { get; private set; }
}

public abstract class CustomUriMapping
{
    public abstract bool MapUri(Uri unMappedUri, out Uri mappedUri);
}

This allows me to create my own custom routes so that if I need to do some custom analysis of the URI before I decide which page to use, this can be done, as shown below:

internal class CategoryRoute : CustomUriMapping
{
    public override bool MapUri(Uri uri, out Uri mappedUri)
    {
        // check for a category
        CachedCategory category = ClientStaticCache.GetCategoryByPath(uri.ToString());
        mappedUri = category != null
                        ? new Uri(string.Format("/Pages/CategoryPage.xaml?categoryid={0}", category.CategoryId),
                                  UriKind.RelativeOrAbsolute)
                        : null;
        return mappedUri != null ? true : false;
    }
}

MapUri returns a bool because there are situations where you want to return a null URI. Why would you want to do this you ask, because in some scenarios you may wish to use a URI to display a dialog, for example, an about dialog.  In this case you might have a mapping that looks like this:

internal class AboutBoxRoute: CustomUriMapping
{
    public override bool MapUri(Uri unMappedUri, out Uri mappedUri)
    {
        mappedUri = null;
        if (unMappedUri.ToString()=="About")
        {
            AboutDialog dialog = new AboutDialog();
            dialog.Show();
            return true; // we are not going to go anywhere, just let the dialog display
        }
        mappedUri = null;
        return false;
    }
}

If the unmapped URI was equal to “About”, the about dialog would display and null would be returned from the UriMapper, so no navigation would occur . Unfortunately there is a bug in the Silverlight beta, which means that returning null from the UriMapper can in some scenarios cause an exception. I’ve filed a bug on connect for this and hopefully it will be fixed before RTW.

Adding the custom URI mapper to your project is the same as adding the Microsoft one, except that you reference a different class.

<routing:CustomUriMapper x:Key="uriMapper">
    <Navigation:UriMapping Uri="search/{searchfor}" MappedUri="/pages/searchpage.xaml?searchfor={searchfor}"/>
<routing:CustomUriMapper.CustomUriMappings> <routing:AboutBoxRoute/> <routing:CategoryRoute/> </<routing:CustomUriMapper.CustomUriMappings> </routing:CustomUriMapper>

As you can see, you can add any custom routes directly in the xaml.

So there are some solutions for some issues with the URI mapping as currently implemented in Silverlight 3. In my next post I will explain some of the issues that I haven't been able to work around.

Tags:

Silverlight

Comments

6/4/2009 7:50:56 PM #

Pingback from silverlightcoder.net

Custom routing using the Uri Mapper in Silverlight 3 | Silverlight Coder

silverlightcoder.net

8/17/2009 1:39:48 AM #

This is an excellent post, i have been attempting to do exactly what you achieved without much success.

silverlight

2/18/2010 7:15:34 PM #

Issues with the Silverlight Navigation architecture

Issues with the Silverlight Navigation architecture

Stefan Olson's Blog

Add comment




biuquote
  • Comment
  • Preview
Loading



About the author

Stefan Olson is the Managing Director of Olson Software.  He has been developing software using Microsoft Technologies for nearly 20 years.

He is currently working on building the next generation Virtual Tour software in WPF and Silverlight for www.palacevirtualtours.com.

Tag cloud