This article is compatible with the latest version of Silverlight.
Expression Blend 4 comes with some pretty cool pre-built
VisualState transition-animations called
TransitionEffects. A
TransitionEffect is a pixel-based transition from one state to another, in other words it’s a PixelShader that has an animatable Progress property. These effects can be used in various scenarios, they are very developer/designer friendly, and they are an easy way to make your Silverlight application more attractive.
In this article I will show you two situations to use Blend 4’s built-in TransitionEffects:
- Navigating between pages in Navigation Framework and
- switching between Views in an MVVM (Light Toolkit) scenario.
Here are the two samples with the „Pixelate” and the „Slide In” effects, live demo and source codes:
Source code to MVVM Transition Demo
Live demo for Navigation Transition
Source code to Navigation Transition
VisualState and Transition recap
You probably know VisualStates from Silverlight SDK Controls and from custom Controls. The name of the concept is “Parts and State Model”. This means that every control has Parts (named elements within the Control) and States (and State Groups). Between switching from a state to another we can define transitions. VisualStateManager is the one that handles state-changes.
The States panel first appeared in Blend 2 SP1, in Blend 3 there were the pre-setted Easings for the transition storyboards, and now in the fourth version of Blend there are the TransitionEffects.
Here’s the standard Button’s States-view in Blend 4:
It’s good to know that we can use VisualStates and Transitions not just in custom Controls but in UserControls as well.
TransitionEffect in a Navigation Framework Application
To enable one of Blend 4’s new TransitionEffects in a Navigation Framework application, all we need is Blend 4 and zero coding. :)
- If you don’t have an existing navigation application in which you can test, create one using Visual Studio. Create a new project using the Silverlight Navigation Application template.
- Once you’re application is ready, open it in Blend 4. Open the main page (default is MainPage.xaml).
- Expand the LayoutRoot and put the ContentBorder in a Grid. You can do this by right clicking on the ContentBorder and selecting Group Into ->Grid. ContentBorder contains the ContentFrame. We will add the transition effect to the newly created grid.
- Luckily ContentFrame exposes both the Navigating and Navigated events. The first one fires before the navigation, the second one after the navigation is made. We will create two states according to these events, a “NavigatingState” and a “NavigatedState”.
Select the States panel, and add a VisulStateGroup by clicking on the plus icon. Add two VisualStates to the group.
- Notice the “fx” icon next to the Default Transition. Click on it and from the dropdown select the TransitionEffect you would like to use. Some effects have additional options (for “Slide In” there’s the option for slide direction).
- Next to fx icon you can set an easing and the duration of the transition, mine is 1 second long.
- So now there will be an animation between the two states we created. Next step is to wire up the states. On the Assets panel in the Behaviors, there’s a behavior called GoToStateBehavior. In this Behavior we can connect an event with a state. Just what we need. Expand the “ContentFrame” Border element in the Object and Timeline panel. Drag a GoToStateBehavior to the ContentFrame. Then drag another one.
- Using the Behaviors, we can connect the NavigatingState with the Navigating event and the NavigatedState with the Navigated event. We can do this on the Properties panel of each Behavior.
- After the events are connected with the states, we can run the application and voila, the navigation is animated.
Though you may notice that not only the content is animated, but the selected TransitionEffect is applied to the navigation links’ header also. This might be an unwanted side-effect. For a workaround, switch to XAML view, and move the whole VisualStateManager definition just inside the Grid we created in the first step. Something like this:
<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">
<Grid Margin="0,0,0,0">
<!-- VisualStateManager moved from LayoutRoot, just the Content will have the TransitionEffect -->
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<ei:ExtendedVisualStateManager.TransitionEffect>
<ee:SlideInTransitionEffect/>
</ei:ExtendedVisualStateManager.TransitionEffect>
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="NavigatingState"/>
<VisualState x:Name="NavigatedState"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}" Margin="0">
...
Now when we run the application again, the TransitionEffect is only applied to content, the header will stay intact.
For the demo I added the “Slide In” effect to the Cosmopolitan Theme Sample. Notice when there are a lot of different controls on a page the transition might not be very smooth. That’s the case in the demo on the SDK Controls and Toolkit Controls page, where the page contains a lot of different controls. Loading all the controls takes long, it’s not the effect itself that is slow. Well, nothing is perfect.
TransitionEffect in an MVVM application
The second sample for using transitions is an MVVM application. Scenario is simple; we have a few Views and a menu. The user can select between the Views, and one is visible at a time. I really like Laurent Bugnion’s MVVM Light Toolkit for its simplicity and efficiency, so I’ll be using that.
We will create a DataTemplate selector ContentControl, and will bind the Content to the selected ViewModel. Let’s create the Views and ViewModels first.
- First, we can start a new application using the MVVM Light (SL4) template. The MainPage.xaml, the MainPageViewModel and the ViewModelLocator will be automatically created.
- We can add the a few Views and their ViewModels using the MVVM Light Toolkit templates (add new item: MvvmView(SL) and MvvmViewModel (SL)). Let’s call these OneView, TwoView, ThreeView and OneViewModel, TwoViewModel, ThreeViewModel.
- In the ViewModelLocator we must set up properties for the ViewModels. We can do this by using the “mvvmlocatorproperty” snippet. Once this is done for all three ViewModels, we can set up the DataContext bindings in the Views.
DataContext="{Binding OneVM, Source={StaticResource Locator}}"
private static OneViewModel _oneViewModel;
/// <summary>
/// Gets the OneVM property.
/// </summary>
public static OneViewModel OneVMStatic
{
get
{
if (_oneViewModel == null)
{
CreateOneVM();
}
return _oneViewModel;
}
}
/// <summary>
/// Gets the OneVM property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public OneViewModel OneVM
{
get
{
return OneVMStatic;
}
}
/// <summary>
/// Provides a deterministic way to delete the OneVM property.
/// </summary>
public static void ClearOneVM()
{
if (_oneViewModel != null)
{
_oneViewModel.Cleanup();
_oneViewModel = null;
}
}
/// <summary>
/// Provides a deterministic way to create the OneVM property.
/// </summary>
public static void CreateOneVM()
{
if (_oneViewModel == null)
{
_oneViewModel = new OneViewModel();
}
}
The DataTemplate selector
For the DataTemplate selector we must create a custom ContentControl.
- Add a new class (in my case it’s called MvvmDataTemplateSelector) that inherits from ContentControl.
public class MvvmDataTemplateSelector : ContentControl
{
...
}
- Add a property - type of DataTemplate - for each property.
/// <summary>
/// DataTemplate for type OneViewModel
/// </summary>
public DataTemplate OneView { get; set; }
/// <summary>
/// DataTemplate for type TwoViewModel
/// </summary>
public DataTemplate TwoView { get; set; }
/// <summary>
/// DataTemplate for type ThreeViewModel
/// </summary>
public DataTemplate ThreeView { get; set; }
- Override the OnContentChanged method. In this method connect the ViewModel with its View, as we will bind the Content to the selected ViewModel.
protected override void OnContentChanged(object oldContent, object newContent)
{
// select ContentTemplate for ViewModel
if (newContent is OneViewModel)
{
ContentTemplate = OneView;
}
else if (newContent is TwoViewModel)
{
ContentTemplate = TwoView;
}
else if (newContent is ThreeViewModel)
{
ContentTemplate = ThreeView;
}
base.OnContentChanged(oldContent, newContent);
}
- Set up the MainPage and MainPageViewModel. We can add buttons for each view in the xaml, and in the MainPageViewModel we can add Commands. Each Command will set the SelectedViewModel property to a ViewModel.
- Finally we can host our Views in the newly created MvvmDataTemplateSelector. Now when we compile and run the application we can switch between the Views.
<!--DataTemplates -->
<DataTemplate x:Key="oneView">
<Views:OneView />
</DataTemplate>
<DataTemplate x:Key="twoView">
<Views:TwoView />
</DataTemplate>
<DataTemplate x:Key="threeView">
<Views:ThreeView />
</DataTemplate>
...
<Helpers:MvvmDataTemplateSelector
Content="{Binding ActiveViewModel}"
OneView="{StaticResource oneView}"
TwoView="{StaticResource twoView}"
ThreeView="{StaticResource threeView}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
/>
TransitioningContentControl
The easiest way to add transitions to our application is to use the Silverlight Toolkit’s TransitioningContentControl:
<toolkit:TransitioningContentControl
Grid.Column="1"
Content="{Binding ActiveViewModel}"
Style="{StaticResource TransitioningContentControlStyle2}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<toolkit:TransitioningContentControl.ContentTemplate>
<DataTemplate>
<Helpers:MvvmDataTemplateSelector
Content="{Binding }"
OneView="{StaticResource oneView}"
TwoView="{StaticResource twoView}"
ThreeView="{StaticResource threeView}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
/>
</DataTemplate>
</toolkit:TransitioningContentControl.ContentTemplate>
</toolkit:TransitioningContentControl>
There’s one drawback, there are only three transition animations built in the Control. Luckily we can help that by editing its template, thus enabling Blend 4’s TransitionEffect.
- Open up Blend, and on the MainPage edit the TransitioningContentControl’s template (right click, edit template, edit a copy.) It will look like this:
The Control consists of two grids, one for the previous content, and one for the current content, this way enabling a transition between them. Each Transition animation has a State, when you set the TransitionName property, you set one of the upper States. The transition will happen between the Normal state (we cannot delete this) and the selected one.
To add a new animation, you can add a new State, and set-up a Storyboard for it. This time though we will enable one of the built-in TransitionEffects, by setting the Default transition just like in the Navigation Framework example.
- We can add a new State, or just edit the existing ones. I’ve edited the DefaultTransition: deleted the other States (this step is not necessary), deleted the DefaultTransition Storyboard, and in the State I set the PreviousContentPresetationSite to Collapsed, just like in the Normal state, because we will take care of this in the Default transition.
- Select the Default transition (the one with the TransitionEffect), and create a Storyboard, where you hide the PreviousContentPresentationSite halfway through the transition.
At 0:00,000 the PreviousContentPresentationSite is Visible, at 0:00,500 it is Collapsed. The full transition is 1 second long. Of course you can change the timeline, e.g. when you are using a non-linear easing for the TransitionEffect.
Here’s the code for the finished VisualStateManager:
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PresentationStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<ei:ExtendedVisualStateManager.TransitionEffect>
<ee:PixelateTransitionEffect/>
</ei:ExtendedVisualStateManager.TransitionEffect>
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="PreviousContentPresentationSite">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="DefaultTransition">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="PreviousContentPresentationSite">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="PreviousContentPresentationSite">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Summary
I hope I could show you in this short tutorial how easy it is to add a transition-animation to your application. The possible scenarios are endless, I’m sure everybody can find his own best way. You can even create custom TransitionEffects if you are familiar with the HLSL language.