Wouter
Wouter

Reputation: 2262

Skip normal button state in VSM between mouseover and pressed state

When a button is pressed the state transitions are as follows:

Normal -> MouseOver -> Normal -> Pressed -> Normal -> MouseOver

The problem I have is that the MouseOver and the Pressed state have a lot in common visually and the transition to normal between these messes up the animation. As a more practical example I have a button that by default has a black border and it has a green border in the MouseOver and Pressed state. When I press it it first changes from green to black and then to green, which is not desirable. Is there any way to either skip the Normal state in this situation or make it look bad?

enter image description here

This is the template of the button:

<Button Command="{Binding PlayScene}" Width="220">
    <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
            <Border x:Name="border"
                    BorderThickness="3"
                    Margin="2 0"
                    BorderBrush="{DynamicResource ButtonBorderBrush}"
                    RenderTransformOrigin="0.5,0.5">
                <Border.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Border.RenderTransform>
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualStateGroup.Transitions>
                            <VisualTransition GeneratedDuration="0:0:0.2"/>
                            <VisualTransition GeneratedDuration="0:0:0.2" To="Pressed">
                                <VisualTransition.GeneratedEasingFunction>
                                    <BackEase EasingMode="EaseOut"/>
                                </VisualTransition.GeneratedEasingFunction>
                            </VisualTransition>
                            <VisualTransition GeneratedDuration="0:0:0.1" To="MouseOver"/>
                        </VisualStateGroup.Transitions>
                        <VisualState x:Name="Normal"/>
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource HighlightBrush}"/>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Pressed">
                            <Storyboard>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(Brush.RelativeTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="border">
                                    <EasingDoubleKeyFrame KeyTime="0" Value="1.1"/>
                                </DoubleAnimationUsingKeyFrames>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(Brush.RelativeTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="border">
                                    <EasingDoubleKeyFrame KeyTime="0" Value="1.1"/>
                                </DoubleAnimationUsingKeyFrames>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="border">
                                    <EasingDoubleKeyFrame KeyTime="0" Value="0.95"/>
                                </DoubleAnimationUsingKeyFrames>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="border">
                                    <EasingDoubleKeyFrame KeyTime="0" Value="0.95"/>
                                </DoubleAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource HighlightDarkBrush}"/>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Disabled"/>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>

                <Border.Background>
                    <ImageBrush ImageSource="{Binding Image}" Stretch="UniformToFill">
                        <ImageBrush.RelativeTransform>
                            <TransformGroup>
                                <ScaleTransform CenterY="0.5" CenterX="0.5"/>
                                <SkewTransform CenterY="0.5" CenterX="0.5"/>
                                <RotateTransform CenterY="0.5" CenterX="0.5"/>
                                <TranslateTransform/>
                            </TransformGroup>
                        </ImageBrush.RelativeTransform>
                    </ImageBrush>
                </Border.Background>

                <ContentPresenter x:Name="contentPresenter" />
            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>

Upvotes: 3

Views: 196

Answers (1)

AXG1010
AXG1010

Reputation: 1962

The Visual State Transitions are run prior to the entering the actual state. Since you have not set a brush in the transitions, they are defaulting back to the base value (in this case ButtonBorderBrush). I'm not entirely sure of your desired outcome, but my example assumes you want to immediately change color from HighlightBrush (in the MouseOver state) to HighlightDarkBrush (in the Pressed state), obviously if a fade from one color to another is desired the storyboard animation animation can be changed to implement that.

<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.2"/>
<VisualTransition GeneratedDuration="0:0:0.2" To="Pressed">
    <VisualTransition.GeneratedEasingFunction>
        <BackEase EasingMode="EaseOut"/>
    </VisualTransition.GeneratedEasingFunction>
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource HighlightDarkBrush}"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualTransition>
<VisualTransition GeneratedDuration="0:0:0.1" To="MouseOver"/>

Essentially you need to add the Storyboard to both the Pressed state and to=Pressed transition. If you only add it to the transition, the color will revert back once it enters the State.

Also note, that while it may look as if the transitioning sequence is

MouseOver -> Normal-> Pressed

that is in fact not the case. You can see this for yourself if you gave the Normal state some value (currently it is using the base values) like this:

<VisualState x:Name="Normal">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush)" Storyboard.TargetName="border">
            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TestBrush}"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

If (for example) TestBrush is a red brush, the border will only be red in when the mouse is not over. In between states, it will be black (ButtonBorderBrush)

Upvotes: 3

Related Questions