This may come as no surprise for some, but only today have I discovered a way to get rid of the magic strings when changing states with the VisualStateManager.GoToState method. The way Microsoft and many others were explaining the Visual State Manager is via a sample code like this:
VisualStateManager.GoToState(control, "statename", true);
Unfortunately, this introduces a magic string, namely “statename”. The problem with the magic string here is that it creates a tight contract between the XAML (where the states are usually defined) and the code behind. It is very easy to mistype the name of the state in either place. Also, if the state is renamed by the designer in Blend, you get no compiler warnings or even runtime exceptions to point out the error.
Here is a small XAML sample using the Visual States to move an ellipse to the right of a Grid:
1: <Grid x:Name="LayoutRoot" Background="LightBlue">
2: <VisualStateManager.VisualStateGroups>
3: <VisualStateGroup x:Name="SG1">
4: <VisualStateGroup.Transitions>
5: <VisualTransition GeneratedDuration="00:00:01">
6: <VisualTransition.GeneratedEasingFunction>
7: <ElasticEase EasingMode="EaseOut"/>
8: </VisualTransition.GeneratedEasingFunction>
9: </VisualTransition>
10: </VisualStateGroup.Transitions>
11: <VisualState x:Name="SG1Normal"/>
12: <VisualState x:Name="SG1EllipseRight" >
13: <Storyboard>
14: <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
15: <EasingDoubleKeyFrame KeyTime="00:00:00" Value="320"/>
16: </DoubleAnimationUsingKeyFrames>
17: </Storyboard>
18: </VisualState>
19: </VisualStateGroup>
20: </VisualStateManager.VisualStateGroups>
21: <Ellipse x:Name="ellipse" Fill="Red" Stroke="Black"
22: Height="116" HorizontalAlignment="Left" Margin="50,98,0,0"
23: VerticalAlignment="Top" Width="235" RenderTransformOrigin="0.5,0.5" >
24: <Ellipse.RenderTransform>
25: <TransformGroup>
26: <ScaleTransform/>
27: <SkewTransform/>
28: <RotateTransform/>
29: <TranslateTransform/>
30: </TransformGroup>
31: </Ellipse.RenderTransform>
32: </Ellipse>
33: </Grid>
The key to my “discovery” is realizing that the VisualState elements have an x:Name attribute, and therefore fields get generated for them in the codebehind. So, instead of
VisualStateManager.GoToState(control, "SG1EllipseRight", true);
We can write:
VisualStateManager.GoToState(this, SG1EllipseRight.Name, true);
Now, if the state gets renamed, at least we get a compiler error, and there is no way that you can mistype the state name.
Another advantage of accessing the states by name is that now it is easy to find when a state transition animation ends (like the one above). Just use the VisualState.Storyboard.Completed event:
SG1EllipseRight.Storyboard.Completed += (sender, args) =>
MessageBox.Show("Ellipse move finished!");
That is two big problems that I had with Visual States solved. Now if only I could get the current state somehow…
Here is a little bonus:
You can also create some extension methods on VisualState as it may fit your way of thinking better:
public static class VisualStateHelper
{
public static void Activate(this VisualState state, Control control, bool useTransitions)
{
VisualStateManager.GoToState(control, state.Name, useTransitions);
}
public static void Activate(this VisualState state, Control control)
{
state.Activate(control, true);
}
}
Now, to activate the SG1EllipseRight state above, all you have to write is:
SG1EllipseRight.Activate(this)
That’s all, folks!
Posted
Dec 29 2009, 11:00 PM
by
vbandi