Introducing the Prism Navigation Framework for Silverlight MVVM Applications
Another framework? But why?
After receiving a lot of positive feedback and requests for source code for my previous post on Prism navigation i decided to release the code i had as open source project.
This is how Prism Navigation Framework project was born.
My intention was to create simple-to-use but powerful-and-feature-full Navigation Framework for building Silverlight MVVM applications with Prism.
This is the first introductory post (from series of planned similar posts) that will try to explain what this Navigation Framework for Prism can do to make your life as developer easier.
If you are too anxious to read the whole article you can just open the Sample Silverlight Navigation Application Application (for the Beta 1 version of the Framework) and try out some of the features and then read on if you find it to be interesting (in the demo you can see loading Views and their ViewModels to Prism Regions, visual transitions when loading Views, Browser URL and History integration – deep linking, deferred module loading, passing parameters to views etc).
If you are interested in the source of the Prism Navigation Framework and the Sample Application shown here you can download it on the Downloads section on the project site.
Note: Its important to say here that in this post I will not try to explain in detail how Microsoft Patterns and Practices Team Prism works – you must already be familiar with it, and if you never used it to develop Silverlight or WPF MVVM applications, i recommend you to try it out, also make sure to check Prism excellent documentation if you need to refresh your memory.
Basic Concepts
Since Prism Navigation Framework is based on the Prism guidance, naturally it is leaning on and reusing all of Prism main concepts:
- Modules – packages of application functionality (packaged as .dll or .xap) – they can be loaded immediately on application start or on demand (so called deferred module loading) – this framework completely supports loading of modules implicitly or explicitly.
- Views and their ViewModels that are packaged in those Modules (each View has its ViewModel set as value of its DataContext property) – this framework allows you to register Views and their ViewModels and then easily load them in many ways, initialize them with parameters etc.
- Regions – are actually Controls that are used as placeholders on the Views where you can load content – other Views of the application – this framework supports Regions and extends them in practical way – read more below.
One Region To Rule Them All
If you ever built Prism applications you know that by convention each Prism application has one Main View – called the ‘ShellView‘.
Shell View is created on the application start and loaded to represent the main screen of the application.
This main View has multiple Regions – this is where you can load your Views.
Prism Navigation Framework takes Prism’s concept of Regions a little further and defines two types of regions:
- Single Main Region – there is only one Main Region in the Application and its usually large region in the center of the Shell View. This Region is meant to be placeholder where we will load other Views – Pages of the Application with the main content. Only one View can be shown in Main Region at one time. This region is represented by dropping instance of PrismNavigationMainFrame control in your ShellView and setting its RegionName.
- Multiple Secondary Regions – represented by PrismNavigationSecondaryFrame control – you can place as many of them as you like in the Shell View and load any of application Views into any of those Secondary Regions – Secondary Region can (unlike the Main Region) show multiple Views at the same time (widgets, control etc).
Here is how you would add the single Main Region to your Shell View:
<Controls:PrismNavigationMainFrame cal:RegionManager.RegionName="MainRegion" />
And here is how you can add multiple Secondary Regions:
<Controls:PrismNavigationSecondaryFrame cal:RegionManager.RegionName="WidgetsRegion" /> <Controls:PrismNavigationSecondaryFrame cal:RegionManager.RegionName="OpenedDocumentsRegion" />
Loading Views
We have seen how we can define Main and Secondary Regions so lets see what are our options for loading our Views into those Regions:
- First and most useful way to load View into Main region is by typing meaningful URLs in the Browser. Our Framework supports convention over configuration so some defaults work out of the box, for example if you have View control called DetailsView in Module called ProductsModule, then to load this DetailsView into the Main Region of the application just point your browser to this URL: http://yourapplication.com/index.html#/Products/Details/ (Off course you can pass parameters through the url like this: http://yourapplication.com/index.html#/Products/Details/ProductId/1234 or http://yourapplication.com/index.html#/Products/Details/)
- you can also use standard Silverlight HyperlinkButton control and its NavigateUri property to specify URL of the View you want to load (for example “/Products/Details/Id/1”) and its TargetName property to specify to which Region you want View to be loaded
- and from your code in ViewModel use the framework’s IPrismViewLoadingService and load Views to any Region in multiple ways: by specifying the View type, or View interface type or just a string name of the View – choice is on you (more on that later). Its important to mention here that when view is loaded via URL – Prism Navigation Framework automatically checks from the URL what is the name of its Module and loads the module if its not already loaded before loading the View, but more on that later.
Modules And How They Relate To Their Views And ViewModels
Now that we covered how to load Views, lets see how the Views and ViewModels are defined, created, registered and initialized in your MVVM application done with Prism Navigation Framework:
Views and ViewModels are in Prism applications packaged in Modules – each module is Silverlight Library (packaged in .dll) or Silverlight Application (packaged in .XAP) that supports some application functionality.
View is usually a UserControl that is just a visual representation of the functionality and its bound to its ViewModel for the data and application logic.
Each module in Prism has to implement IModule interface and it contains InitModule method that is called by the Prism when the Module is loaded.
This is where you need to register your Views and ViewModels so that Prism Navigation Framework can help you instantiate them and load them into Regions.
So here is how your typical Module initialization would look like:
public class InitModule : IModule { private readonly IViewRegistrationService _viewRegistrationService; public InitModule(IViewRegistrationService viewRegistrationService) { _viewRegistrationService = viewRegistrationService; } public void Initialize() { this._viewRegistrationService.RegisterViewsAndTheirViewModels(); } }
This is possible because each ViewModel needs to inherit the generic type ViewModel<IView> and therefore carries the type (or interface) its View.
This way RegisterViewsAndTheirViewModels method can automagically scan the calling Assembly and look for the types that inherit ViewModel<TView> and registers them for you.
Here is how typical ViewModel definition looks like if you tie your ViewModel to the View interface type:
public class IndexViewModel : ViewModel<IIndexView> { }
But off course you can also tie your ViewModel directly to the View type if you want (but i don’t recommend that you do this, tying ViewModel to View interface is much cleaner):
public class IndexViewModel : ViewModel<IndexView> { }
Naturally, nothing stops you from manually registering your Views and ViewModels in your Module initialization like this:
public class InitModule : IModule { private readonly IViewRegistrationService _viewRegistrationService; public InitModule(IViewRegistrationService viewRegistrationService) { _viewRegistrationService = viewRegistrationService; } public void Initialize() { this._viewRegistrationService.RegisterViewAndViewModel<IUserDetailsView, UserDetailsViewModel>(); } }
As long as you use any of these methods of View-ViewModel registrations – your navigation will work.
View Initialization
So now that we know how to register our Views and ViewModels lets see how ViewModel initialization looks like.
Since base class ViewModel implements IAcceptInitializationData interface it is ready to accept initialization data like this:
public interface IAcceptInitializationData { void Initialize(ViewInitializationData initializationData); }
So whenever some View needs to be shown, behind the scenes its ViewModel will be created, and its Initialize method will be invoked with initialization data (that includes parameters from URLs or directly passed if its done via code) and afterwards this ViewModel will be set as DataContext of the View and then View will be shown in desired prism Region by our Navigation Framework.
Lets take a look at the ViewInitializationData class that is passed to the ViewModel:
public class ViewInitializationData { public string Url { get; set; } public string RegionName { get; set; } public Dictionary<string,string> Parameters = new Dictionary<string, string>(); }
As you see its enough data for the ViewModel to start functioning – it knows where its loaded – RegionName, how (if via URL then corresponding Url property is filled), and it gets its parameters via Parameters dictionary.
Parameters are parsed from the URL automatically by the framework so you dont have to worry about it.
Can I Show Views From Code in ViewModel?
If you are showing the View from the code, you can pass to it new instance of ViewInitializationData type where you can set your parameters etc.
Also its worth mentioning that if you show some View from code into the Main Region and pass parameters to it, then Browser URL will be updated to show the Url of the View showing the Module name, View name and parameters if any.
For example if you loaded DetalsView from ProductsModule with parameters ProductId = 1234 browser URL would change to something like this: http://yourapplication.com/index.html#/Products/Details/ProductId/1234).
Plumbing
As you can see the Prism Navigation Framework can do a lot of plumbing for you, so that you can focus on the implementation of the application functionality (don’t we all want to do that?).
In order to achieve all this framework is extending the Prism so it needs to call some initialization code in the Bootstrapper class.
To avoid forcing users to manually call that code in every application there is special class PrismNavigationFrameworkUnityBootstrapper that you can use instead of the regular UnityBootstrapper that will do all this initialization for you (for now MEF is not supported by this framework but maybe we will include it later).
So in your application bootstrapper that inherits PrismNavigationFrameworkUnityBootstrapper you don’t need to do anything other than what you would usually do, just create your Shell and register Modules etc, something like this maybe:
public class Bootstrapper : PrismNavigationFrameworkUnityBootstrapper { protected override DependencyObject CreateShell() { this.Container.Resolve<IViewRegistrationService>().RegisterViewAndViewModel<ShellView, ShellViewModel>(); return Container.Resolve<IViewCreationService>().CreateView<ShellView>() as DependencyObject; } protected override void InitializeShell() { base.InitializeShell(); Application.Current.RootVisual = (UIElement)this.Shell; } protected override Microsoft.Practices.Composite.Modularity.IModuleCatalog CreateModuleCatalog() { return Microsoft.Practices.Composite.Modularity.ModuleCatalog.CreateFromXaml(new Uri( "PrismNavigation.App;component/ModuleCatalog.xaml", UriKind.Relative)); } }
Wrap Up
This is just an introductory post and here you could see only part of what you can expect from this Framework.
I wont go into further details for now, so expect in the future more posts covering every feature of the Prism Navigation Framework in more details.
In the meantime check out the Prism Application Framework Sample Application just to get a feel of what it can do, or browse the latest Source Code or download it and use freely for building your next big Prism Silverlight MVVM application.
Your feedback (both positive and negative) and participation in the development of the project is very welcome!
This is a very informative piece of work. We are working with Prism Navigation as well. Please let us know if you would be open to answering a few specific questions.
Thanks!
Madhu
Hi Madhu,
off course, be free to post questions here so we can try to answer them together for everyone’s benefit.
thanks!
Thanks a lot!
1. We are showing a certain set of tiles on home.xaml which loads inside a navigation frame in shell.xaml
2. On click on any one of these tiles, we need to navigate to page2.xaml which again loads inside same navigation frame in shell.xaml
3. On page2.xaml there a set of corresponding telerik tiles for each of the tiles on the home.xaml
4. When user clicks on a tile on home page, we would like to show the corresponding tile in page2.xaml in maximized view.
5. We would also like to highlight the icon on the menu bar (which corresponds to page2.xaml)
The menu bar is in shell.xaml outside the navigation framework
What would be your suggesstion to implement this as we have been unsucessful so far
Thanks,
Madhu
Can you send a zipped test solution on my email so i can give it a look?
its very hard to imagine all this.
my email: webmaster [A t] roboblob [dot] com
Hi,
Great work!
I have a question:
If you are on a page and want to add parameters in the address according to the user action, is it possible to do it without reloading the module ? Just update the address and that’s it.
If yes how ?
Hi Again,
How do you deal with the module that are already loaded?
There is a amazing amount of memory use by your framework. If you use the demo app, and swap from one view to another, the memory is increasing all the time!
If i am right you are loading the view each times, are you ?
I have started to use it but now with that problem i start to regret it.
If you have any solution ….
Great job!
I have a question, how do we apply authorized mechanism with your framework, please give me a solution.