Just when I think I understand the VisualStateManager
, something proves me wrong.
I'm using WPF 4 and am trying to simply enlarge an item on mouse over, and shrink it back on mouse leave. I figured I'd just define each state in a VisualStateGroup
and then specify a VisualTransition
with a GeneratedDuration
:
<Border x:Name="PART_Root" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderTransformOrigin="0.5,0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1"/>
</VisualStateGroup.Transitions>
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1.5" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1.5" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.RenderTransform>
<ScaleTransform x:Name="scaleTransform" ScaleX="1" ScaleY="1"/>
</Border.RenderTransform>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
Since I have a catch-all VisualTransition
with a GeneratedDuration
, I was expecting the VSM to generate intermediate animations. That is, mousing over the control should animate the ScaleTransform
properties from 1 to 1.5 over the course of 1 second. Same with mousing off. Instead, there's a delay of 1 second and then the ScaleTransform
properties instantly snap to 1.5 or back to 1.
If I manually specify transitions as follows then I get the desired behavior:
<VisualStateGroup.Transitions>
<VisualTransition From="Normal" To="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1.5" Duration="{StaticResource MouseEnterDuration}"/>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1.5" Duration="{StaticResource MouseEnterDuration}"/>
</Storyboard>
</VisualTransition>
<VisualTransition From="MouseOver" To="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1" Duration="{StaticResource MouseLeaveDuration}"/>
<DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1" Duration="{StaticResource MouseLeaveDuration}"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
But why do I have to do this? I thought the whole point of generated transitions was that the transition would be, you know, generated. What am I misunderstanding here?
UPDATE: As per Rick's answer, Blend generates something that does work. Thus, working backwards I determined that it is indeed the fact that I'm referencing the ScaleTransform
directly rather than through the UIElement
that contains it. I changed my XAML to the following and it works as expected:
<Border x:Name="PART_Root" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderTransformOrigin="0.5,0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="Normal" To="MouseOver" GeneratedDuration="{StaticResource MouseEnterDuration}"/>
<VisualTransition From="MouseOver" To="Normal" GeneratedDuration="{StaticResource MouseLeaveDuration}"/>
</VisualStateGroup.Transitions>
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_Root" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)" To="{StaticResource MouseOverScale}" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="PART_Root" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" To="{StaticResource MouseOverScale}" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.RenderTransform>
<ScaleTransform/>
</Border.RenderTransform>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
Seems ridiculous (and an obvious bug), but it works.
Thanks
(Border.Background).(Brush.Opacity)
fails butBackground.Opacity
works fine. – Antihalation