Saturday, March 12, 2011

Jounce Part 13: Navigation Parameters

This was a busy morning for Jounce. I checked in some changesets that unfortunately break backwards compatibility but address a few highly requested changes. The first was CLS compliance, and that involved refactoring some weirdly named methods. The second was consistent namespaces (had view vs. views, etc.) so that was cleaned up. The final and the focus of this post was the addition of navigation parameters.

This was something definitely lacking in the navigation harness for Jounce. In most projects I use direct view model to view model communication to pass setup information. For example, if I want to display an order detail, the order master passes the order detail to the detail view model, then raises the navigation event for the order detail. This, of course, creates coupling between view models and requires a coordination of actions to make one event happen.

The addition of parameters changes that. You can now raise a navigation event with a payload, and the payload will be passed into the view model when the view is activated. This not only provides some flexibility around initializing the view model with context, but also enhances the "back stack" experience. If you like, you can make your view models completely stateless and dependent on the parameters passed in. Then, if you implement a back stack as in the previous post, the back stack itself can simply hold the navigation structure. This means a back event can restore the initial payload and the view model can reconstruct the state based on the prior request.

The parameters come with a simple set of extension helpers that make it easy to fluently pass values when navigation events are raised. To demonstrate the new functionality, I changed the "Simple Navigation" quick start to pass a GUID payload. Only the green circle has a view model, but this was updated to listen for the payload and update it to a property that is then bound and displayed on the circle.

I've been asked why I don't implement the back stack functionality in Jounce directly or use a URI-based navigation system. I don't discount the merits of those systems but I feel a lot of URI-based navigation comes from the memory of the web and trying to get a Silverlight application to behave like a web one, which I don't believe is the right approach. I also don't believe in imposing a navigation paradigm because users may have their own requirements. For example, a back stack sounds simple until you are dealing with nested regions. What if I am three levels deep, initiate a pop-up with a wizard, and "go back"? In this scenario, there are multiple levels of what "back" means, and you may want to handle those differently. This is why I've focused on giving Jounce just the basics: an event to trigger navigation, now a payload to pass, the binding of the view to the view model and a region manager to get the view on the visual tree for you.

To add a parameter to a navigation request, you provide a name and pass the value of the parameter, like this:

EventAggregator.Publish(view.AsViewNavigationArgs().AddNamedParameter("Guid", Guid.NewGuid()));

The "add named parameter" extends the navigation args and returns the same instance, but will use the internal parameters collection to add the requested parameter.

Consuming it is just as simple. The ParameterValue extension allows you to extract the parameter to a type and will provide the default if it doesn't exist (so if you wish to take exception to a missing parameter, check for its existence first):

var guid = viewParameters.ParameterValue<Guid>("Guid");

The view model used to have a method called _Activate that was passed the tag for a view whenever the view was navigated to. That method is now more appropriately named ActivateView and is passed both the view tag and the parameter dictionary. It's also important to note that the actual dictionary is passed, not a copy. This means the navigation args will stay as long as you keep the reference to the dictionary. This isn't necessarily a bad thing because the object is lightweight, but most of the time you'll simply extract the values and it will go out of scope anyway. Because you have the actual dictionary, it is possible to manipulate the dictionary. I allow this on purpose in case you need to add meta data or other information for historical purposes if you are saving a navigation stack.

The latest version of Jounce and the examples for this feature can be downloaded from CodePlex.

Jeremy Likness