I’ve been toying with Silverlight over the last couple of weeks and, I have to say, it’s much more complete than ASP.Net ever was (**). Lots of new paradigms to get my head around, of course, but overall it’s a very complete solution for an RIA.
Anyway, to the point of this posting; the new (in Silverlight 3.0) Navigation Framework is great; it allows you to present a common interface and have new pages display in a know part of the browser window, supports browser back/forward, and URL rewrites. See here for more information.
If you are inside a page that is controlled by the framework, then you are able to call on the NavigationService.Navigate method to divert control to a new XAML page. For example;
this.NavigationService.Navigate( new Uri( String.Format( "/Views/Item.xaml?title={0}&type={1}", ...), UriKind.Relative ) );
This is all well and good if you inherited from the Page class within the Navigation Framework, but what if you are a user control, out on a limb, with no knowledge of the Navigation Framework? In my case, I have a user control that displays a treeview context list to the user. Some of the options are static (“select all machines”, “select all software”, etc) and some is dynamic (“select a machine of type…”). Once the user clicked on their desired option, we did some menu calculations and then finally asked the Silverlight Navigation Framework to display the required page in the Content frame. Except we couldn’t… a UserControl has no knowledge of the Navigation Framework and so can’t instruct it to navigate to a specific page.
Darn.
The ultimate solution is that the application’s “main page” (which we can find down the Application.Current map) is able to influence the navigation, so stage one was to add a “public bool NavigateTo(Uri uri)” method to the MainPage.xaml file;
public partial class MainPage : UserControl{public bool NavigateTo(Uri uri){return this.ContentFrame.Navigate(uri);}
Now, we just need to call the NavigateTo method from our non-navigational UserControl. To do this, we need to find the Main Page, which we do by tracking back to the “RootVisual” element in the MainPage.xaml file (if you renamed any of this, then you’ll need to change some of the references, below);
private void RunMenu(string requestedMenu, object o){MainPage mp = ((MainPage) Application.Current.RootVisual as MainPage);switch (requestedMenu.ToLower()){case "machines": mp.NavigateTo(new Uri("/MachineList", UriKind.Relative)); break;case "software": mp.NavigateTo(new Uri("/Home", UriKind.Relative)); break;
Here, we find a reference to the Main Page via Application.Current.RootVisual (remember MainPage.xaml?) Once we’ve done that, we call the main page object and ask it to “NavigateTo” a new page.
To put this into context, here’s the application browser view;
(** only in terms of the fact that ASP.Net always seemed to be a halfway house to me – you needed to jump through a lot of hoops to persuade the browser to do what you want it to do. Silverlight, of course, was designed as an RIA from conception so it seems to fit more easily than a native HTML / ASP.Net app. Of course, the trade-off is that you need to present a whole new runtime environment to the user, in terms of the Silverlight VM, but I can live with that for a) more rapid development and b) less concern over what the browser is going to do with our code.)

I ran into the same issue, and thought I would give this a try. But the problem I am running into is that Application.Current.RootVisual throws an exception when I try to cast it to MainPage. I get the following error "Unable to cast object of type 'System.Windows.Controls.Activity' to type 'AdjusterMetrics.MainPage'. (AdjusterMetrics is the name of my app.)
My app is pretty much the standard app. I started from the 'Standard Business Application' template, and haven't made any major changes to the structure. The only thing that might be different from your app is that the user control I am trying to navigate from is two levels deep.
Have you seen this? Any advice?
Thanks,
Rick
Well, its not pretty, but I found a workaround. I defined a 'Global Variable' class, and stored a reference to the main page there when the Main Page initializes. Then in the user control, I can reference that global variable. Like I said, its not pretty, but at least I can move on.
Thanks,
Rick
Hi Rick
huh… that's odd. I wonder if this is an issue with the original (July 2009) CTP version I was playing with. Lemme retry on the current version.
Still, I'm glad you got somewhat of a solution.
Scott
The solution can be found here:
http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/8921b434-5495-415d-a9b0-386cff873917/
Thanks for this post, I was able to use it to create a slightly more elegant navigation method, having had the same issue as Rick, thought I'd share it here for others.
Firstly I created a static MainPage variable in the MainPage.xaml.cs file, setting it to "this" in the MainPage constructor.
I then created a static NavigateTo method on the MainPage class. I can now navigate anywhere I like using MainPage.NavigateTo(string uriString).
Code below:
public partial class MainPage : UserControl
{
private static MainPage MainPageObject;
public MainPage()
{
InitializeComponent();
MainPageObject = this; //<<This is the added code
this.loginContainer.Child = new LoginStatus();
}
//Other template methods...
public static bool NavigateTo(string uriString, UriKind uriKind)
{
if (Uri.IsWellFormedUriString(uriString, uriKind))
{
return MainPageObject.ContentFrame.Navigate(new Uri(uriString, uriKind));
}
else return false;
}
}
It worked! Thanks – Silverlight 4 – final edition.
@Ani; huh?
@Matt; yep; that works well; thanks
@Edi; no probs!