This question is a follow-up to my previous question as well as this related question about how VisualStates
work in WPF.
Currently, my understanding is that animating the same property within different VisualStateGroups
can cause problems (see the linked questions).
To resolve these problems, it requires loop-holes to be taken advantage of. (Perhaps loop-hole isn't the correct term, but it appears that the solution isn't what the WPF designers intended.)
I'm wondering what is the correct way to animate the same property in multiple VisualStateGroups
without causing adverse side-effects. If it is not possible, what is the correct route for achieving the same visual behavior for a control?
I was able to find some related documentation at MSDN:
The control is always in exactly one state per group. For example, a Button can have focus even when the mouse pointer is not over it, so a Button in the Focused state can be in the MouseOver, Pressed, or Normal state.
This leads me to a second question...
How can you provide a visual behavior that should only occur when two specific VisualStates
are active?
Take for example a ToggleButton
:
- If the button is Checked, I would like to display Behavior 1.
- If the button is Disabled, I would like to display Behavior 2.
- Finally, if the button is Checked and Disabled, I would like to display Behavior 3.
In the above example, how would you go about rendering the third visual behavior?
For the first part of your question, you'll want each state to interact with a different individual object for each instead of hitting the same one with each VisualState, as example;
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DisabledState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckedState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- Each state interacts with its own object in your ControlTemplate ideally -->
<Border x:Name="CheckedState" Visibility="Collapsed"
Background="Green"/>
<Border x:Name="DisabledState" Visibility="Collapsed"
Background="White" Opacity=".5"/>
Instead of one object sharing a property change like;
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="White" />
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="Green" />
</Storyboard>
</VisualState>
<Border x:Name="Background" Background="Blue"/>
As per your second question, A VisualState
is going to act as a bool in that it either is, or it isn't in that state. To share a declaration of a state you'd have to add a little more finesse with a MultiTrigger or a converter somewhere or something.
Hope this helps. Cheers
EDIT ADDITION:
So you also have available to you VisualTransition
you could employ like;
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="Normal"
GeneratedDuration="0:0:0.2"
To="Checked">
<VisualTransition.GeneratedEasingFunction>
<ExponentialEase EasingMode="EaseIn" Exponent="7" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
<VisualTransition From="Checked"
GeneratedDuration="0:0:0.2"
To="Normal">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<!-- etc, etc, etc -->
So you can play with your different eases, your generated duration times, etc.