Before I move on with my journey of using NavFx to build VRC3 I want to fulfil a special request and cover how to use the Interfaces to add NavFx to support to an application built with UserControls instead of the templates or base classes. For those that have existing applications using the interfaces requires the least changes to code.
NavFx includes four interfaces
- IApplication
- IHostPage
- IPage
- ITransitor
I am not going to cover ITransitor in this article as it warrants its own article which I will add at some point.
IApplication
IApplication defines only one method that must be implemented GetNavigator. Why do you need this?
Well if you bundle all of your pages in the application package (.xap) you don't because you can always access the App class and it's properties directly so you could implement whatever you like to provide a singleton instance of the NavFx Navigator to pages in the same package. However if you are going to seperate some of your pages into Silverlight Libraries and load them dynamically they won't build if you try to do something like App.GetNavigator() because the App class won't be available at build time.
Implementing IApplication in App.xaml.cs provides a known interface to the current application within pages loaded dynamically from libraries. So with something like this in App.xaml.cs:
public partial class App Application ,NavFx.IApplication
{
private NavFx.Navigator navigator;
#region IApplication Members
public NavFx.Navigator GetNavigator()
{
if(this.navigator == null)
{
this.navigator = new NavFx.Navigator();
}
return this.navigator;
}
#endregion
//remainder of code.....
}
With a reference to NavFx in your page library you can do something like this to get a Navigator to work with:
NavFx.Navigator appNavigator = ((NavFx.IApplication)Application.Current).GetNavigator();
IPage
IPage defines two properties Path and IsPinned and must be implemented by any page that you want to register with Navigator.
To add NavFx support to an existing or new UserControl simply add , IPage to the end of the class declaration in the code behind file and implement the interface something like this:
public partial class Page: UserControl, NavFx.IPage
{
public Page()
{
InitializeComponent();
}
#region IPage members
public string Path
{
get
{
//return "Page";
return this.GetType().Name;
}
}
public bool IsPinned
{
get{ return false;}
set{ throw(new NotImplementedException());}
}
#endregion
}
Path must return a unique string representing a key for referencing the page. In the example I have commented out a string literal return value and returned the name of the class implementing the page. This is what the PageBase does provided by NavFx and works fine for a flat heirarchy of pages where the naming convention is suitable for use as a key.
With the page in the application package you can register it with something like this in your Application_Startup handler
Page page = new Page();
this.GetNavigator().RegisterPage(page);
Then navigate to it like this (of course you are free to create constants for the page keys).
this.GetNavigator().GoToPage("Page");
With the page in an external library, from your application package you can load, register and access the page in one statement like this
this.GetNavigator().GoToPage("Page", false, new Uri(@"MyPageLib.dll", UriKind.Relative));
This requires MyPageLib.dll to be in the same folder as the .xap file.
IHostPage
IHostPage implements IPage so the above section applies to pages that implement IHostPage. This interface is intended for pages that host pages. For example a page that provides a menu or some other navigation feature would implement IHostPage and include a container of some sort where IPage instances would be displayed, perhaps a StackPanel. IHostPage defines a Navigator property and a SetContent method.
Navigator should simply be a get/set property it is set to this when the IHostPage is passed to Navigator.RegisterPage so an automatic property is perfect for the implementation of this
public NavFX.Navigator Navigator{get; set ;}
SetContent was added when I discovered that the Content property of a ContentControl is protected and can only be set by subclasses. As I started using NavFx myself I found it useful to create a Shell page, basically an empty UserControl and started out looking to set the Content property in my SimpleTransitor implementation of ITransitor. Then it occured to me the right thing to do is leave it up to the host page where it displays a guest page and added the SetContent method. In the HostPage implementation included in NavFx SetContent simply sets this.Content to whatever is passed, but there are other possibilities. Assume you have a UserControl that implements IHostPage and includes a StackPanel names contentPanel the implementation of SetContent might look like this.
public void SetContent(IPage page)
{
this.contentPanel.Children.Clear();
this.contentPanel.Children.Add(page);
}
There it is, these three very lightweight interfaces will allow you to use the full power of NavFx in your applications with only a little effort. For existing applications this means minimal code changes for a lot of benefit IMHO.
All my examples are in C# because that is what I work with, but if you are using VB and you are not sure how to transpose these examples just let me know in a comment and I will provide an sample.