Caliburn Micro Navigation for Silverlight

Users who are accustomed to browser based apps find navigating between screens and having a back button to be a very natural way to interact with applications. In addition, they also make it easy for screens to be bookmarked.

In Silverlight, navigation is integrated with the web browser through the use of Uri fragments. Uri fragments look like this:

http://site.com/sample.htm#/Page1

. When the Uri fragment is changed, it triggers a Frame control to load new content in two steps:

  1. Firstly, the fragment is mapped to the path to a XAML file using the frame’s UriMapper
  2. Then, the XAML file referred to in the previous step is loaded and the navigation events are raised.

Caliburn.Micro, being a ViewModel-First framework can integrate with the Silverlight Navigation Framework. Rob Eisenberg has already provided one for the Windows Phone. With a little work, we can make it work for Silverlight as well. I have provided it in this GitHub project.

A quick note on how Caliburn.Micro integrates navigation: Caliburn.Micro defines an INavigationService interface that includes methods like Navigate() and GoBack(). The implementation, called a FrameAdapter, calls the equivalent methods on the Frame control.

The best way to apply the FrameAdapter is in the bootstrapper, once the RootVisual has been loaded. There is a bit of fiddling around initially, and we cannot inject a Frame into the ShellViewModel’s constructor because the ShellView’s Frame isn’t loaded yet. So there is a precise order we need to apply to bootstrap this properly. All is done for you in the sample. We load the ShellView, find the frame, and then inject it back into the ShellViewModel.

	protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
	{
	    DisplayRootViewFor<IShell>();

            // finds the Frame control in the ShellView, 
            // registers it, 
            var frame = FrameAdapter.FindFrame(App.Current.RootVisual, null);
            container.RegisterInstance(typeof(System.Windows.Controls.Frame), null, frame);

            // and then reinitialize the shellviewmodel
            BuildUp((App.Current.RootVisual as System.Windows.Controls.UserControl).DataContext);
	}

Here’s a sample ShellViewModel that gets injected with INavigationService during the BuildUp() call.

public class ShellViewModel
{
        public INavigationService NavigationService { get; set; }

        public void Link1()
        {
            NavigationService.UriFor<Page1ViewModel>().WithParam<int>(p => p.PageId, 42).Navigate();
        }
}