Home > OS >  Storyboard does not work when triggered by IsMouseOver and IsPressed
Storyboard does not work when triggered by IsMouseOver and IsPressed

Time:01-29

I am making a custom control. When mouse over it, the opacity of content will animate from 0.5 to 0.8. When mouse pressed it, the opacity of content will set to 1 immediately.

Here is my code:

<ControlTemplate TargetType="{x:Type local:OpacityButton}">
                    <Border x:Name="B" Background="{TemplateBinding Background}" Opacity="0.5" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                        <ContentPresenter Margin="{TemplateBinding Padding}" TextBlock.FontFamily="{TemplateBinding FontFamily}" Content="{TemplateBinding Content}" TextBlock.Foreground="{TemplateBinding Foreground}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" TextBlock.FontSize="{TemplateBinding FontSize}"></ContentPresenter>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                Storyboard.TargetName="B"
                Storyboard.TargetProperty="Opacity"
                To="0.8" Duration="0:0:0.3" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                Storyboard.TargetName="B"
                Storyboard.TargetProperty="Opacity"
                To="0.5" Duration="0:0:0.3" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Opacity" Value="1" TargetName="B"></Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>

When the button is pressed, the opacity doesn't change any.

Soon I found this solution: Storyboard animation not completing when triggering property changes

I add the RemoveStoryboard to the EnterActions and ExitActions, now when the mouse is over, the opacity does not change any.

It seems because it removes the Storyboard immediately but not after it is complete.

How can I solve this?

CodePudding user response:

This is a better job for VisualStates:

<ControlTemplate TargetType="{x:Type local:OpacityButton}">
    <Border x:Name="B"
            Background="{TemplateBinding Background}"
            Opacity="0.5"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="B"
                                         Storyboard.TargetProperty="Opacity"
                                         To="0.5"
                                         Duration="0:0:0.3" />
                    </Storyboard>
                </VisualState>
                <VisualState Name="MouseOver">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="B"
                                         Storyboard.TargetProperty="Opacity"
                                         To="0.8"
                                         Duration="0:0:0.3" />
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Pressed">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="B"
                                         Storyboard.TargetProperty="Opacity"
                                         To="1"
                                         Duration="0:0:0.3" />
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <ContentPresenter Margin="{TemplateBinding Padding}"
                          TextBlock.FontFamily="{TemplateBinding FontFamily}"
                          Content="{TemplateBinding Content}"
                          TextBlock.Foreground="{TemplateBinding Foreground}"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          TextBlock.FontSize="{TemplateBinding FontSize}"></ContentPresenter>
    </Border>
</ControlTemplate>

The problem with your Triggers is that more than one of them is true at the same time - when the button is pressed, the mouse is also over it, so the triggers are competing and the IsMouseOver one is winning.

The control can only have one VisualState at a time though, so you can define exactly what happens in each state and only that state.

If you're committed to triggers you could use MultiTrigger for the mouse over state, making the conditions IsMouseOver = true and IsPressed = false. Then when IsPressed is true, that trigger will run and the other won't.

But assuming you're subclassing Button here, WPF is already giving you the VisualState's for free, so you might as well use them.

  • Related