My XAML Navigation System in Layers
Layer 1: the Frame
At the core of my stinky onion is the Frame
control, hosted in a UserControl
. I prefer to have one Frame
per application, which implies that the UserControl
is my conventional Client
control (Microsoft culture has called this Main
or Home
—in the ASP.NET MVC space). Anyway, because I call my default visual “Client”, the navigation frame is called ClientFrame
:
<navigation:Frame x:Name="ClientFrame"
BorderThickness="0">
<navigation:Frame.UriMapper>
<uriMapper:UriMapper>
<uriMapper:UriMapping Uri="" MappedUri="/Views/IndexPage.xaml"/>
<uriMapper:UriMapping Uri="/" MappedUri="/Views/IndexPage.xaml"/>
<uriMapper:UriMapping Uri="/{key}/{id}" MappedUri="/Views/{key}Page.xaml"/>
<uriMapper:UriMapping Uri="/{key}" MappedUri="/Views/{key}Page.xaml"/>
</uriMapper:UriMapper>
</navigation:Frame.UriMapper>
</navigation:Frame>
This declaration comes with several implications:
- The only content to be loaded in a Frame is a
System.Windows.Controls.Page
. To need an exception to this rule suggests that the application is getting too complex. - The default Page is
IndexPage.xaml
. This strongly suggests that this page is some kind of human-readable summary of the application domain. - Every
Page
name in the application should end with ‘Page.’ - Visuals representing the Domain can be located by a “key” or a key with an ID.
It is my intent that all of these keys should be stored in NavigationBookmarkData
(see below).
Layer 2: Frame Eventing in Code Behind
My PasteBin.com code sample shows (almost) all of the eventing required for a navigation Frame
. The table below summarizes:
|
This event is primarily used to extract the |
|
This event is handled to allow the application to cancel navigation. When a service layer is built into the application, this event can be used to start service calls related to the navigation target. The data associated with this event, Because I use MVVM Light, I have written an Extension Method, |
|
The conventional error “window” is used here. |
Layer 3: NavigationBookmarkData
Back in 2012, in “Silverlight Page Navigating with MVVM Light Messaging and Songhay NavigationBookmarkData,” I went into detail about this class definition. Another PasteBin.com sample for this class is available.
Layer 4: Handling NavigatingCancelEventArgs
It’s the NavigationBookmarkData
that can be used to determine whether a Navigation should or should not be cancelled. My ClientViewModel
has concerns for Messaging and Navigating. Messaging logic supports the LightNavigationMessage<NavigatingCancelEventArgs>
sent by Client UserControl
. Navigating logic handles this ‘light-navigation’ message by evaluating it with the NavigationBookmarkData
. A rough sketch of the handler illustrates:
void HandleClientNavigating(LightNavigationMessage<NavigatingCancelEventArgs> message)
{
navigationBookmarks.SetBookmarkContext(message.TargetLocation);
string category = (message.RelativeUriSegments.Length > 1) ?
message.RelativeUriSegments[1] : null;
int? id = (message.RelativeUriSegments.Length > 2) ?
FrameworkTypeUtility.ParseInt32(message.RelativeUriSegments[2]) : null;
if(category == "category1" && id.HasValue) this.DoRiaOperationGetMyData(id.Value);
else if(navigationBookmarks.IsBookmarkUnknown()) message.Content.Cancel = true;
else throw new NotImplementedException("Page is not yet supported.");
}
First the handler puts the navigating location in ‘context’ so the NavigationBookmarkData
can ‘validate’ it. The LightNavigationMessage
class contains logic to split the navigating location into an array of RelativeUriSegments
(see PasteBin.com listing). This array can contain a key and/or ID, according to the conventions declared on Layer 1, the ClientFrame
.
When we have "category1"
and an ID we can then call the RIA service (in DoRiaOperationGetMyData()
). The navigation system built into the .NET Framework (on Level 1) will display the page at the navigating target location. This page of course contains visuals bound to the data returned by the RIA operation.
However, when it is clear that the “bookmark” (or the navigation target location) set in context is unknown (if(navigationBookmarks.IsBookmarkUnknown())
), then the navigation is cancelled.
Layer 5: Actually Doing the Navigation
MVVM Commanding is what starts the funky Navigation going through each layer of our onion. The commanding eventually reaches logic that looks like this:
void DoNavigation(string category, int id)
{
var format = this._bookmarks.GetPatternForGetDataFormat();
var bookmark = string.Format(format, category, id);
HtmlPage.Window.NavigateToBookmark(bookmark);
}
So Layer 1 uses what comes in the box with the .NET Framework and Layer 5, with its call to HtmlPage.Window.NavigateToBookmark()
, is also using what ships from Microsoft. Or should I say what shipped from Microsoft?
The “death” of Silverlight in general strongly suggests that HtmlPage.Window.NavigateToBookmark()
is dead (while the Frame
lives on) in particular.
Related Links
“Windows 8 Metro Style Apps – Implementing Navigation with XAML” |
This article shows that Windows 8 app design goes ‘back’ to calling |
“Navigation in Silverlight Without Using Navigation Framework” |
“Silverlight 3 introduces Navigation Framework which takes care of this, but let's first try to achieve navigation and state management without this framework; or you can say, the way it is done in Silverlight 2.” |
“On-demand loading of assemblies with Silverlight Navigation – Revisited for Silverlight 4 Beta” |
“With Silverlight 4’s INavigationContentLoader extensibility point, we can address this scenario much more effectively, and are no longer locked into the workarounds and strict constraints that Silverlight 3’s navigation feature placed on us. In this post, I’ll walk through the use of another ContentLoader I’ve been working on and look at how it simplifies building multi-xap applications.” |
“The good news is that MEF is included in the .Net base class libraries as part of the Metro-style application profile so is included in-the-box for all such applications. Unfortunately if you look at the documentation you will see references to ‘Assembly.GetExecutingAssembly()’ that is not available in the Metro-profile…” | |
“Navigation Overview [.NET 4.5]” |
Introduces the |
“If you’re using some container to create your view models, the | |
“You can get it from the app’s
| |
“So the string type methods allow you to specify the page to navigate by string. Before you all think I really lost my marbles this time, re-introducing weakly typed navigation just as the Windows team introduced strongly typed: I specifically added this as to allow navigation to be initiated from a |