Tech Blog

Jay's Technical blog

Analysis of My SL4 Navigation Template with MVVM Light (plus 1 more template)

03 May 2010
Jay Kimble

[WARNING! This is an archived post and as such there may be things broken/missing here.. you have been warned.]

In my last post I offered a MVVM Light version of the standard Navigation Template in Silverlight 4 (There’s an updated version at the bottom of this post). In this post I will I want to walk you through all the interesting things found in this template (it is in and of itself a learning tool). In fact this template contains a much fuller example of various techniques that you may want to employ.

As mentioned in our last installment you need to make sure you have MVVM Light installed properly. Additionally you need the 2 templates attached to this article (I have re-attached the original project template and a new item template to this post) unblocked, and placed in the appropriate places in your vs2010 templates directory (My project template is in “c:\Users\{UserName}\Documents\Visual Studio 2010\Templates\ProjectTemplates\Silverlight\Mvvm” and the item template is in “C:\Users\{UserName}\Documents\Visual Studio 2010\Templates\ItemTemplates\Silverlight\Mvvm”

Directory Structure

Probably the first thing you notice is that there are a couple new directories. Along with Assets and Views there is a ViewModel, a Model, and a Controls folder.

ViewModel Folder

If you open the ViewModel folder you will see classes that correspond to the XAML files in the views folder with 2 exceptions:

  1. The ErrorWindow.xaml doesn’t have a ViewModel for a number of reasons not the least of which is that it’s an error window we want to keep that fairly simple (it’s launched by the Application); if something were wrong with our ViewModel structure then no Error Message would appear – we wouldn’t want that to happen.
  2. There is a ViewModelLocator class in the ViewModel folder which has no XAML file. If you don’t know what this is you may want to read one of the getting started posts on the Laurent Bugnion’s MVVM Light site.
MainPageModel (ViewModel)/MainPage.xaml Enhancements

This page uses RelayCommands (an implementation of the ICommand interface) to bind the various clicks and actions to functions in our ViewModel. Here’s a snippet from the Commands region of the file (I laid these out into regions to make it easier to hide this code).

  1:public RelayCommand<NavigationEventArgs> NavigateCommand
  2: {
  3:     get;
  4:private set;
  5: }
  6:publicvoid NavigateAction(NavigationEventArgs e)
  7: {
  8:foreach (var link in AllNavLinks)
  9:     {
 10:         link.LinkIsActive = link.URIPath.Equals(e.Uri.ToString(), StringComparison.InvariantCultureIgnoreCase);
 11:     }
 12:
 13: }

Normally you would use the regular non-generic RelayCommand, but in this case (actually in both cases in this class) our code is setting up to handle an Event which does not provide a Command property we can bind to. MVVM Light provides us with a handy dandy Silverlight behavior (I think it’s a behavior) that we can bind to in our XAML. Here’s the XAML snippet (from MainPage.xaml) that pertains to this:

  1:<i:Interaction.Triggers>
  2:<i:EventTriggerEventName="Navigated">
  3:<cmd:EventToCommandCommand="{Binding NavigateCommand, Mode=OneWay}"
  4:PassEventArgsToCommand="True"/>
  5:</i:EventTrigger>
  6:<i:EventTriggerEventName="NavigationFailed">
  7:<cmd:EventToCommandCommand="{Binding NavigationFailed, Mode=OneWay}"
  8:PassEventArgsToCommand="True"/>
  9:</i:EventTrigger>
 10:</i:Interaction.Triggers>

Lines 2-5 are where we are identifying the event (line 2) and then we are binding the event to a command and telling it to pass the event arguments (3 and 4). Line 5 simply closes the tag. Lines 6-9 hook up the NavigationFailed event to another RelayCommand in our ViewModel.

In the constructor, we see the following code in the MainPageModel (ViewModel) class:

  1: AllNavLinks = new ObservableCollection<NavigationLinkInfo>();
  2: AllNavLinks.Add(new NavigationLinkInfo { NavText = "home", URI = "Home", IsFirstElement = true });
  3: AllNavLinks.Add(new NavigationLinkInfo { NavText = "about", URI = "About" });
  4: NavigateCommand = new RelayCommand<NavigationEventArgs>(
  5:     (e) => NavigateAction(e),
  6:     (e) => true);
  7:
  8: NavigationFailedCommand = new RelayCommand<NavigationFailedEventArgs>(
  9:     (e) => NavigationFailedAction(e),
 10:     (e) => true);

Lines 4-6 (and lines 8-10) sets up the glue for the RelayCommand <T>which will not only provide a connection to the method that actually fires (Lines 5 and 9) , but also provides a way (lines 6 10) to notify the UI when a command should not be available (on a button this will automatically make it go into a disabled state). In the latter we have hard coded a true value here, but you could actually have a function here instead that runs some state check. I’m not sure if this will do the same thing for the actual navigation control, so test this out before you actually try to disable a navigation option.

This template actually provides a little more power over the state of the “hyperlinks” in the navigation. Lines 1-3 set up an observable collection of NavigationLinkInfo objects which is the class that the Navigation is actually bound to. This is one of the places where our version makes a few things easier than the standard MS Supplies Navigation template. Lines 2 and 3 set up 2 navigation links: home and about. If we had additional navigation items we would simply add a line here and we would be all set to go which is similar to what we get in the MS supplied template, but I personally prefer to do this in code (we could also retrieve this value via MEF or some other injection framework… so we have a lot more flexibility in my opinion.. more on the MEF option later).

There’s another section of code you will want to know about but more on that after we cover the NavigationLinkInfo and the NavigationControl.

NavigationLinkInfo (in the Models folder)

Here’s what the class looks like in the Class View of VS 2010:

image

If you look back at the code above the code in the MainPageModel provided the URI which left off the opening “/”. It also se the Navtext, and IsFirstElement. The URIPath is simply the URI with the opening “/” added back in (and is used to bind to our NavigationControl. IsFirstElement actually ties to the ShowNavRectable which is the separator between navigation links (the first one doesn’t need a separator.

This leaves us with the LinkIsActive and LinkVisualState properties which are related. Since for the most part the code simply needs to set a visual state the LinkVisualState will automatically set the appropriate value which will in turn end up bound to the VisualState of the hyperlink control in the NavigationControl (this is a big deal and it took me a bit to get this working). The LinkIsActive property tells the link that it is the current one. Under the covers this property sets the correct Visual State which in turn is picked up by the NavigationControl to identify that it should switch its hyperlink control’s visual state (making it look like it is highlighted or not highlighted).

NavigationControl (in the Controls folder)

As mentioned there is some cool stuff going on in the NavigationControl. When you open up the constructor you will see that we are doing some data binding in code to the various properties found in the NavigationLinkInfo class which will be the data context for every instance of this control. (this supplied by an ItemsControl in the MainPage.Xaml file).

The most interesting thing here is the code that exposes the CurrentState to the “outside world” (well outside this class):

  1:publicconststring CurrentStatePropertyName = "CurrentState";
  2:
  3:publicstaticstring GetCurrentState(DependencyObject obj)
  4: {
  5:return (string)obj.GetValue(CurrentStateProperty);
  6: }
  7:publicstaticvoid SetCurrentState(DependencyObject obj, stringvalue)
  8: {
  9:     obj.SetValue(CurrentStateProperty, value);
 10: }
 11:privatestaticvoid TransitionToState(object sender, DependencyPropertyChangedEventArgs args)
 12: {
 13:
 14:     NavigationControl c = sender as NavigationControl;
 15:if (c != null)
 16:     {
 17:         VisualStateManager.GoToState(c.hlink, (string)args.NewValue, true);
 18:     }
 19://else
 20://{
 21://    throw new ArgumentException("CurrentState is only supported on the Control type");
 22://}
 23: }
 24:
 25:publicstaticreadonly DependencyProperty CurrentStateProperty =
 26:            DependencyProperty.RegisterAttached(CurrentStatePropertyName,
 27:typeof(String),
 28:typeof(NavigationControl),
 29:new PropertyMetadata(TransitionToState));

Lines 1-10 and 25 define the CurrentState as a dependency property. I won’t claim to understand everything here, but line 29 will cause the TransitionToState method to be registered as a ProeprtyChanged callback which will fire when the property changes. When this happens the TransitionToState method (in lines 11-23) will set the hyperlink (hlink) control’s VisualState to the appropriate one.

The next result from this and the NavigationLinkInfo class is that all we have to do in our viewmodel is set whether a NavigationLinkInfo Object’s NavigationLinkIsActive property to true or false and the “Binding glue” does the rest.

Here’s what that code looks like in the MainPageModel class:

  1:foreach (var link in AllNavLinks)
  2: {
  3:     link.LinkIsActive =
  4:         link.URIPath.Equals(e.Uri.ToString(), StringComparison.InvariantCultureIgnoreCase);
  5: }

This code is in the routine that fires just after the user clicks a link. e.Uri is the equivalent of the URIPath value. This routine actually resets all the NavigationLinkInfo objects’ LinkIsActive property.

How Do You proceed From Here?

Actually from here to create a new Page simply use the provided Item Template to create a new page in the Views folder, use the provided MVVM Light template for creating a ViewModel in the ViewModel folder, and add the ViewModel info to the ViewModelLocator (using the MVVM Light provided snippet). None of this sounds drop dead simple.. especially if you aren’t really into separation of concerns, but if you do this your XAML files will be “blendable” which means your designers will love you!

The Future

I really do want to make this a little easier. My plan is to create another version of this template with MEF that does away with the need to modify the ViewModelLocator. I might try to see if there is an easier way to provide the Navigation. My ultimate goal is that i want to be able to get MEF to be able to add new Navigations via a downloaded assembly (but whether that gets ina  template or not remains to be seen).


MVVM Light Navigation Template

01 May 2010
Jay Kimble

[WARNING! This is an archived post and as such there may be things broken/missing here.. you have been warned.]

A few days ago on Twitter I mentioned that I had created a template that incorporates MVVM Light and the VS2010/SL4 Navigation Template. Since that point there have been some discussion of whether Codebehind has simply moved to the VM. While I don’t necessarily think so, I start to worry if I may have “overdone” the templates. I’m going to go ahead and publish the templates (no worries there) for a number of reasons. It illustrates some techniques that I have had a hard time digging up (most notably how to bind the visual state of an element and have it change when the property changes). I also think my version has a better story for creating new tabs. I will write about this some more, but right now, I just want to get these out.

BTW, to install this take the zip file and put it into your “My Documents\Visual Studio 2010\Templates\ProjectTemplates\Silverlight\Mvvm” (or some folder under “My Documents\Visual Studio 2010\Templates\ProjectTemplates”).

Enjoy!

PS. I’m noticing some issues with this version of the template. I may need to repost it later once I discover what is happening. Code all seems to be the same, but the template doesn’t work. I will try to post later this weekend,

[Update: I fixed the template this morning! Enjoy]

File Attachment SL4 Nav App With MVVM Light