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 ofUriMappings
).
This effectively implies that you must define UriMapper
in XAML. Losing precious hours of my time, I notice that:
- You ‘should’ define
UriMapper
inApp.xaml
so it can recognize pages that might be loaded after initialization of the application domain. - You can’t define
UriMapper
inApp.xaml
when you are using a generic application loader (because the generic loader is not supposed to know about the pages it is loading).
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 byBiggestBoxCompositionHost.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 ofFrame.ContentLoader
(which would be myBiggestBoxNavigationContentLoader
). - Silverlight does not call the
Page.OnNavigatedTo()
method whenPage
is settingFrame.Content
programmatically. I threw together theIComposableView
interface, with itsDoNavigateFrom()
andDoNavigateTo()
methods in direct response to this issue by design.