Working with the WPF VSM in an MVVM friendly manner

Lately I have been working a lot with Silverlight. While working on my first Silverlight project I encountered my first problem… There are no Triggers in SL!!! At first I was frustrated and started hitting my self in the head but hey SL is not that bad… SL has the Visual State Manager aka VSM which is a pretty awesome tool I must say. I still think SL should have Triggers for some tasks such as ControlTemplates and other tasks yet with VSM you can still get some cool stuff done :)

What really strikes me with the VSM is the way it is supported in Blend, and YES designers love VSM! With the VSM they can define states in an easy way (and yes these states transition and the designer has full control on the transitions done). They can navigate easily through states in Blend and even see the transitions being done in Blend there and then.

image

Going back to WPF one thing that is missing in WPF is a way how a designer can define animations that get Triggered when something happens… Or let me re phrase that…. There is no easy way, or better not as easy as the VSM :)

Triggers in WPF can invoke Storyboards yet still it feels like the WPF animation system does not have a nice way to dispatch animations… Enter the VSM for WPF.

Yes, VSM is available for WPF as well, and yes it is supported in Blend (you just have to do a little trick that you can find over here)

I created a sample project that leverages the VSM for its animations and yes its all MVVM (in the sample app you’ll see that I am also using design time data for blend and I also included a mini MVVM Libarary that I use to develop my everyday WPF projects :) ).

 

Let’s Get Started

So let’s start by defining a state in Blend. In order to do so (assuming you followed the instructions to enable the VSM for WPF projects) you need to navigate to the States Tab and create a new state. Once the state is created and selected you’ll see that Blend will go to recording mode (just like it does when you are doing a normal WPF animation). Now you can change any property of any element and the Blend will record that and create the appropriate transitions for it.

image

Ok, now that we have the states defined in Blend what is next? well we need a way how to invoke these states for starters!

The Blend team were so kind to give us a Behavior called GoToStateAction. This behavior will basically invoke a state when an event is raised, for example if you have a button that should dispatch a new state, you simple find the GoToStateAction form the Asset tab and drag it on the button

image

Then you can set the properties for this behavior in the properties tab. There you can specify which state to select.

image

By doing these steps (define a state and use the GoToStateAction to invoke the state) you are already good to go. You can press F5 and you can see that when you click the button you get transitioned from one state to another. (P.S it’s important that you set a default transition time span otherwise you end up with no transition since the default is 0s).

 

This is all cool but what about MVVM ??

I hear you I hear you…. What if I have a ViewModel which needs to invoke a state change ?? Unfortunately this is not so straight forward… BUT of course there is a solution… I extended the GoToStateAction so that it can be hooked up from a ViewModel. Here is my extended version

   1: public class VisualStateManagerInvoker : GoToStateAction

   2: {

   3:     #region CurrentState

   4:  

   5:     /// <summary>

   6:     /// CurrentState Attached Dependency Property

   7:     /// </summary>

   8:     public static readonly DependencyProperty CurrentStateProperty =

   9:         DependencyProperty.RegisterAttached("CurrentState", typeof(string), typeof(VisualStateManagerInvoker),

  10:                                             new PropertyMetadata((string)null,

  11:                                                                  new PropertyChangedCallback(OnCurrentStateChanged)));

  12:  

  13:     /// <summary>

  14:     /// Gets the CurrentState property.  This dependency property 

  15:     /// indicates ....

  16:     /// </summary>

  17:     public static string GetCurrentState(DependencyObject d)

  18:     {

  19:         return (string)d.GetValue(CurrentStateProperty);

  20:     }

  21:  

  22:     /// <summary>

  23:     /// Sets the CurrentState property.  This dependency property 

  24:     /// indicates ....

  25:     /// </summary>

  26:     public static void SetCurrentState(DependencyObject d, string value)

  27:     {

  28:         d.SetValue(CurrentStateProperty, value);

  29:     }

  30:  

  31:     /// <summary>

  32:     /// Handles changes to the CurrentState property.

  33:     /// </summary>

  34:     private static void OnCurrentStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

  35:     {

  36:         if (GetGoToStateAction(d) == null)

  37:         {

  38:             var command = new RelayCommand(x => //where x is the state name passed as parameter

  39:             {

  40:                 var stateAction = new VisualStateManagerInvoker();

  41:                 stateAction.Attach(d);

  42:                 stateAction.UseTransitions = GetShouldUseTransition(d);

  43:                 stateAction.StateName = (string)x;

  44:                 stateAction.Invoke(null);

  45:             });

  46:             SetGoToStateAction(d, command);

  47:         }

  48:     }

  49:  

  50:     #endregion

  51:  

  52:     #region GoToStateAction

  53:  

  54:     /// <summary>

  55:     /// GoToStateAction Attached Dependency Property

  56:     /// </summary>

  57:     public static readonly DependencyProperty GoToStateActionProperty =

  58:         DependencyProperty.RegisterAttached("GoToStateAction", typeof(ICommand), typeof(VisualStateManagerInvoker),

  59:                                             new PropertyMetadata((ICommand)null));

  60:  

  61:     /// <summary>

  62:     /// Gets the GoToStateAction property.  This dependency property 

  63:     /// indicates ....

  64:     /// </summary>

  65:     public static ICommand GetGoToStateAction(DependencyObject d)

  66:     {

  67:         return (ICommand)d.GetValue(GoToStateActionProperty);

  68:     }

  69:  

  70:     /// <summary>

  71:     /// Sets the GoToStateAction property.  This dependency property 

  72:     /// indicates ....

  73:     /// </summary>

  74:     public static void SetGoToStateAction(DependencyObject d, ICommand value)

  75:     {

  76:         d.SetValue(GoToStateActionProperty, value);

  77:     }

  78:  

  79:     #endregion

  80:  

  81:     #region ShouldUseTransition

  82:  

  83:     /// <summary>

  84:     /// ShouldUseTransition Attached Dependency Property

  85:     /// </summary>

  86:     public static readonly DependencyProperty ShouldUseTransitionProperty =

  87:         DependencyProperty.RegisterAttached("ShouldUseTransition", typeof(bool), typeof(VisualStateManagerInvoker),

  88:                                             new PropertyMetadata((bool)true));

  89:  

  90:     /// <summary>

  91:     /// Gets the ShouldUseTransition property.  This dependency property 

  92:     /// indicates ....

  93:     /// </summary>

  94:     public static bool GetShouldUseTransition(DependencyObject d)

  95:     {

  96:         return (bool)d.GetValue(ShouldUseTransitionProperty);

  97:     }

  98:  

  99:     /// <summary>

 100:     /// Sets the ShouldUseTransition property.  This dependency property 

 101:     /// indicates ....

 102:     /// </summary>

 103:     public static void SetShouldUseTransition(DependencyObject d, bool value)

 104:     {

 105:         d.SetValue(ShouldUseTransitionProperty, value);

 106:     }

 107:  

 108:     #endregion

 109:  

 110: }

This class contains 2 attached properties CurrentState (which is a string) and a GoToStateAction which is an ICommand (It also has another property ShouldUseTransition which instructs the VSM weather to use transitions or just change the properties for the new state).

The CurrentState property will attach itself to the DepenedencyObject which is being decorated with this property and hook to its VSM (please note that the attach will crawl up the Logical Tree to find the first available VSM). This operation creates an RelayCommand that is set in the GoToStateAction attached property.

Now the interesting part happens. I created a base class for ViewModels that want to invoke states called StateBaseViewModel. Here is the code…

   1: /// <summary>

   2: /// Base view model that has a property for state changes

   3: /// </summary>

   4: public class StateBaseViewModel : BaseViewModel

   5: {

   6:     private string currentState;

   7:  

   8:     /// <summary>

   9:     /// Gets or sets the current state of the ViewModel

  10:     /// </summary>

  11:     public string CurrentState

  12:     {

  13:         get { return currentState; }

  14:         set

  15:         {

  16:             currentState = value;

  17:             RaisePropertyChanged("CurrentState");

  18:         }

  19:     }

  20:  

  21:     /// <summary>

  22:     /// Changes the CurrentState property to the spcified state

  23:     /// </summary>

  24:     public ICommand GoToStateCommand { get; protected set; }

  25:  

  26:  

  27:     /// <summary>

  28:     /// Specify a command to be fired when state changes

  29:     /// </summary>

  30:     public ICommand OnGoToState { get; set; }

  31:  

  32:  

  33:     public StateBaseViewModel()

  34:     {

  35:         GoToStateCommand = new RelayCommand(

  36:             x =>

  37:             {

  38:                 CurrentState = (string)x;

  39:                 if (OnGoToState != null && OnGoToState.CanExecute(CurrentState))

  40:                     OnGoToState.Execute(CurrentState);

  41:             },

  42:             x => !String.IsNullOrEmpty((string)x)

  43:             );

  44:     }

  45: }

This base view model has the same 2 properties of the VisualStateManagerInvoker. We can make these 2 in sync together by using standard TwoWay databinding. By doing so the VisualStateManagerInvoker will feed the View Model with a command that is hooked up to a UI element VSM and when invoked this will ask the VSM for that element to transition to the state passed as parameter.

Here is the binding for this

   1: <local:Checkout  

   2:     mvvmStateManagement:VisualStateManagerInvoker.CurrentState="{Binding CurrentState}"

   3:     mvvmStateManagement:VisualStateManagerInvoker.GoToStateAction="{Binding OnGoToState, Mode=TwoWay}" />

Assuming that the element local:Checkout has a datacontext set to a view model that is inheriting from StateBaseViewModel, you can see how the VisualStateManagerInvoker is feeding this View model a GoToStateAction by simple two way databinding.

Since this command is now ready to be used from our ViewModel we can invoke a state by simple executing the command like so.

   1: CancelCommand = new RelayCommand(x =>

   2:                 {

   3:                     GoToStateCommand.Execute("CancelState");

   4:                 });

This is super awesome isn’t it :)

Another cool thing about this is that you can use this trick so that you can invoke a state from a child to its parent. Lets say that you have a Window that contains a usercontrol (which has a StateBaseViewModel set as its datacontext) and a button. You want the button to show the UserControl. Ok easy Just drag and drop a GoToStateAction behavior and set its state to the state which makes the user control visible. But now lets say you have a button inside the user control and this button needs to invoke a state defined in the Window. The VSM works with namescopes thus you cannot invoke a state that is out of your namescope. BUT with the approach of uses VisualStateManagerInvoker we can do this since we can set the binding in the same XAML line that declares the usercontrol in the Window thus being in the same namescope as the Window :) We can then make a normal command that executes the GoToStateCommand in out StateBaseViewModel (this example is in the sample application to Cancel the check out of the shopping list and to show the thank you message when someone checks out the shopping cart)

Please note that the same code works for Silverlight.

Conclusion

I think that by using VSM one makes the Developer Designer workflow a happier thing. Designers love VSM and I learnt to love it as well. Now that I can invoke states easily from my ViewModel. Besides that since the implementation gets the VSM instance in an attached behavior and then feeds it to the ViewModel via databinding, the ViewModel never touches the VSM thus unit testing is much easier like this.

I found this way of working with the VSM awesome… OK I still need to make it cooler but as it is right now you can just grab the code and start working without any problems…. hope it helps :D

 

Download sample application (P.S Open this in Blend 3 to get maximum pleasure :) )

(Sample application contains MVVMHelper a small library I built for MVVM. It contains loads of goodies… have a look and enjoy)

Some Astoria tips I learnt today

First of all let me start by saying that if you are doing Silverlight, you must learn Astoria, or as they call it nowadays, ADO.NET Data Services..

http://msmvps.com/blogs/theproblemsolver/archive/2009/01/06/consuming-an-ado-net-data-service-from-silverlight.aspx

In a matter of minutes you can expose data from a database in a restful service that can be consumed by Silverlight :)

Now here are some things I learnt today…

The first tip is How can I have the Service on 1 server and the Silverlight app on another without having Cross Domain issues…

The answer to this is this url

http://blogs.msdn.com/astoriateam/archive/2009/09/03/using-the-ado-net-data-services-silverlight-client-in-x-domain-and-out-of-browser-scenarios-i.aspx

Its important that you install the v1.5 CTP2 since by default this is not the version that comes with .NET 3.5 SP1 (Please note you also have to follow the rest of the steps… )

Another useful tip I learnt today is how to Expand a Property of a Property inside an entity (common when you have many to many relations). Example you have a Venue table that is linked to Sports with a table called VenueSportMapping. You can use XPath like syntax to load the property value.

   1: var query = from x in context.Venue.Expand("VenueSportMapping/Sport")

   2:                         select x;

Thats all I have for today… hope you find these 2 tips handy :)

happy coding :)