DayPath Journal

Implementing INavigationContentLoader with an abstract class…

You’ve read the title. So, let me back up… In “Opening up Silverlight 4 Navigation: Introduction to INavigationContentLoader,” David Poll explains:

If you haven’t noticed by now (or been following my previous blog posts), I happen to really enjoy exploring the Navigation feature in Silverlight. A while back, I posted a number of workarounds and tips for some desirable scenarios using the Navigation feature in Silverlight 3. Then… I went silent. And the reason: I’ve been waiting for an extensibility point to be opened up in Silverlight navigation. In the Silverlight 4 beta, that extensibility point has arrived as the INavigationContentLoader.

Unlike David Poll, I was not in-the-loop-enough to “wait” for Silverlight Navigation to open up. I just noticed that I was unable to define UriMapper programmatically—this discovery is reinforced by the documentation—notice that:

  • UriMapper.UriMappings is read-only.
  • The UriMapper constructor takes no arguments (like, say, a collection of UriMappings).

This effectively implies that you must define UriMapper in XAML. Losing precious hours of my time, I notice that:

  • You ‘should’ define UriMapper in App.xaml so it can recognize pages that might be loaded after initialization of the application domain.
  • You can’t define UriMapper in App.xaml when you are using a generic application loader (because the generic loader is not supposed to know about the pages it is loading).
Silverlight Deep Linking with MEF

It turns out that I need to implement INavigationContentLoader because it has no dependency on UriMapper (and its associated conventions) and because it is friendly to my MEF-based composition. By wildly misinterpreting the work of David Poll (and, perhaps, the life’s work of Jeffrey Richter), I actually find it useful to implement an interface with an abstract class. I call it NavigationContentLoader. My reasons for doing this is because the CancelLoad() and EndLoad() members of INavigationContentLoader can have a default implementation (copied directly from David Poll). I mark methods BeginLoad() and CanLoad() abstract—declaring the intent that these members must be implemented. This intent becomes content in BiggestBoxNavigationContentLoader.

Concerns of BiggestBoxNavigationContentLoader…

The INavigationContentLoader interface brings us the concept of the “current URI,” the current fragment identifier representing a resource in the Silverlight application and the “target URI,” the next fragment identifier. It follows that the BiggestBoxNavigationContentLoader is concerned with:

  • Mapping the “target URI” (of INavigationContentLoader) to type names of pages (to be used by BiggestBoxCompositionHost.MefHost.GetPage()).
  • Parsing the “target URI” to map to type names of pages—and to extract parameters (user-control or packed-XAML sample names).
  • ‘Caching’ “target URI” parameter (user-control or packed-XAML sample name) to be inspected by the current page during the OnNavigatedTo() call (when the page finds a name it can use then the page loads a sample into a child window).

Supporting Deep Linking in a MEF-Composed Silverlight Application

This ‘caching’ behavior is very important because of the need to support “deep linking” into the Silverlight application. This support is challenging because a deep link can indicate a resource in the Silverlight application that is not currently available because of the asynchrony of MEF Composition. It follows that such an indicator has to be ‘cached’ until the application is composed.

It was tempting to try a simpler approach to solving this problem but these issues made such ease impossible:

  • Reloading the same URI with HtmlPage.Window.Navigate() is ignored by the current instance of Frame.ContentLoader (which would be my BiggestBoxNavigationContentLoader).
  • Silverlight does not call the Page.OnNavigatedTo() method when Page is setting Frame.Content programmatically. I threw together the IComposableView interface, with its DoNavigateFrom() and DoNavigateTo() methods in direct response to this issue by design.