A recurring problem with MVVM is how to incorporate animations in a nice MVVM way. The problem is that StoryBoards need to be started from the view, since they are usually configured in XAML. This means that the ViewModel needs a reference to the view, in order to tell it when the animation should start. Keeping a reference from the ViewModel to the View is obviously “not done” in the MVVM pattern. In this post I’m going to show how you can use the VisualStateManager to start animations and incorporate it in the MVVM pattern, without keeping a reference from the ViewModel to the View.
When using animations in business applications, it’s usually to tell your user that the application is going from one state to another. For example, going from the “NotLoggedIn” state to the “LoggedIn” state. States in Silverlight are handled by the VisualStateManager. The VisualStateManager is probably well known under Silverlight developers; it’s used to go from state to state in controls. What a lot of developers don’t know, is that the VisualStateManager can also be used in other situations than custom control development or templating controls. For example, you can use the VisualStateManager to manually add states to a UserControl, let Blend record the user interface in the different states and use the “VisualStateManager.GoToState()” method to let your UserControl switch states. This way, you can declaratively tell in XAML, how your UserControl should look in each state. Blend handles the animations and the XAML part, all you have to do is tell the VisualStateManager when to switch states. I’m going to demonstrate this with a very simple application:
The idea is that when the user moves the mouse over the image, it becomes slightly enlarged, using an animation in the process. Obviously, MVVM is overkill for this very simple application, but it demonstrates the point quite well. We can define two states in this application, namely “Enlarged” and “Normal”.
In order to make this work in a MVVM way, we have to do a couple of things:
- Create a ViewModel, named “MainPageViewModel”.
- Use a command for the “MouseEnter” event of the image, to switch to the “Enlarged” state.
- Use a command for the “MouseLeave” event of the image, to switch to the “Normal” state.
- Bind a “CurrentState” property of the main UserControl, to a “CurrentState” property of the ViewModel. Every time the “CurrentState” property of the ViewModel changes, the View should switch states. The two commands for the “MouseLeave” and “MouseEnter” events should modify the “CurrentState” property of the ViewModel and the View should update itself through data binding. This way, the ViewModel doesn’t need to have a reference to the View.
When implementing the scenario outlined above you’ll encounter a couple of problems:
- The Image component doesn’t have support for any ICommand objects.
- A UserControl class doesn’t have a “CurrentState” property which you can bind to the ViewModel.
- There isn’t a generic ICommand implementation in Silverlight.
To tackle these problems I’ve created a separate reusable Silverlight class library, named “MVVMSupport”.
The MVVMSupport Library
This library contains three classes, one to fix each problem outlined above. First up is the DelegateCommand<T> class:
This is a slightly modified implementation of the DelegateCommand class wandering around on the Internet. It’s a generic ICommand implementation with two constructors, one which supports a condition that determines whether the command can be executed at the current time (to let a Button disable itself for example), the other supplies a condition that always returns true, so the command can always be executed. Because the implementation of the action and the condition to execute is supplied by Action and Func delegates coming from outside this class, the outside (usually the ViewModel) must also determine when the command can be executed. This means that the ViewModel is responsible for firing the “CanExecuteChanged” event. This is where the “FireCanExecuteChanged” method comes in. I don’t use these features in the sample application above, but a generic ICommand implementation should support them. This class tackled the “No generic command implementation in Silverlight” problem outlined above.
Next up is the static “Commands” class:
I’m not covering every bit of code in this class. The idea is that the lack of ICommand support of most controls in Silverlight, can easily be solved by using attached properties. I define two attached properties in the class above, a “MouseEnterCommand” and a “MouseLeaveCommand”. When set, this class registers event listeners for the “MouseEnter” and “MouseLeave” events, when these events occur, the correct command is executed, supplying the MouseEvent for extra information to the command. For every command you miss in Silverlight, you can use an attached property to provide support for it. This class obviously tackles the “No command support” problem outlined above.
And finally, the static “VisualStates” class:
This class provides an attached property for every Control. when this property is set, the control transitions to the new state. Because transitioning to states can now be done by setting properties instead of calling the “VisualStateManager.GoToState()” method, you can easily bind this property to a property in the ViewModel.
Let’s take look at the ViewModel for the sample application:
The ViewModel is actually quite small. This is because all it has to do is, set the current state to the correct state when the “ToLargeState” command or the “ToNormalState” command is executed. The ViewModel uses the DelegateCommand<T> class covered earlier. The commands are bound to the “MouseEnterCommand” and the “MouseLeaveCommand” attached properties (also covered earlier) in the view, that’s why the lambda’s accept a “MouseEventArgs”. When the “CurrentState” property in the ViewModel changes, the View is automatically notified because of the INotifyPropertyChanged implementation.
The whole View is implemented in XAML:
I’ll cover the different parts:
- Line 11: “The DataContext” property is bound to the ViewModel, as usual with MVVM.
- Line 12: The attached “CurrentState” property is bound to the “CurrentState” property of the ViewModel. This means that when the “CurrentState” property in the ViewModel changes, the UserControl also transitions to another state.
- Line 16 – 36: This XAML is all generated by Blend 4, and tells the VisualStateManager how the control should look in each state. You can generate this XAML in Blend by first opening your UserControl and then switching to the “States” panel:
You can add states as much as you like and define transitions and animations by clicking on a state and then simply modify the properties in the designer. You only have to make sure that the added states have the same names as the states in the ViewModel.
- Line 40 – 41: Here the attached “MouseEnterCommand” and the “MouseLeaveCommand” properties are bound to the commands in the ViewModel. They’ll execute when the mouse enters or leaves the image.
That’s it! This is all that is needed to wire everything up. By splitting the application up in states, using attached properties for the missing commands and using the VisualStateManager to transition between states in a declarative way, all responsibilities are neatly where they belong, without needing a reference from the ViewModel to the View. The ViewModel determines to which state is switched and remains testable and the View knows how it should look in each state. The beauty of the View is that all that it needs to know is in XAML, and you can easily tweak the appearance of the different states with Blend, without ever touching the ViewModel. The solution above also works for DataTemplates in an ItemsControl, DataTemplates also support the manually adding of states in Blend. You can download the whole working solution here. Creating nice interactive states with Blend is easy, especially with Blend’s fluid layout features. Those features have been expanded in Blend 4. Enjoy!