Search code examples
wpfwpf-controlswpf-animation

Toggle Button IsMouseOver Animation


I would like to add a fade out animation when a ToggleButton IsChecked=false and IsMouseOver is deactivating.

I have a toggle button with a custom control template.

The following triggers are setup and work great.

  • When IsChecked=true display background opacity=1
  • When IsMouseOver=true display background opacity=1

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/>
                    </Trigger>
    
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/> 
                    </Trigger>
                </ControlTemplate.Triggers>
    

However, now I want to add a fade out animation when the ToggleButton is NOT checked and IsMouseOver is deactivating.

I've created a storyboard animation which changes the opacity value from 1 to 0 in 100ms:

<Storyboard x:Key="OnMouseOut">
      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="innerRectangle">
        <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0"/>
      </DoubleAnimationUsingKeyFrames></Storyboard>

What I've Tried

I added a EnterAction and ExitAction to the IsMouseOver trigger and that performs the intented effect. However, if the ToggleButton is checked I do not want the animation to run.

   <Trigger Property="IsMouseOver" Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard x:Name="OnMouseIn_BeginStoryboard" Storyboard="{StaticResource OnMouseIn}"/>
                                </Trigger.EnterActions>
                                <Trigger.ExitActions>
                                    <BeginStoryboard Storyboard="{StaticResource OnMouseOut}"/>
                                </Trigger.ExitActions>
                                <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/>
                            </Trigger>

What I've Tried Part 2

Using a MultiTrigger doesn't remedy the issue.

 <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="IsChecked" Value="False"/>
                                </MultiTrigger.Conditions>
                                <MultiTrigger.EnterActions>
                                    <BeginStoryboard x:Name="OnMouseIn_BeginStoryboard" Storyboard="{StaticResource OnMouseIn}"/>
                                </MultiTrigger.EnterActions>
                                <MultiTrigger.ExitActions>
                                    <BeginStoryboard Storyboard="{StaticResource OnMouseOut}"/>
                                </MultiTrigger.ExitActions>
                                <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/>
                            </MultiTrigger>

Solution

  • This just looks crazy, but it works. Basically, I created an identical story board with a different name when IsChecked is exiting. On Hover I cancel this secondary storyboard.

     <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="IsChecked" Value="False"/>
                                </MultiTrigger.Conditions>
                                <MultiTrigger.EnterActions>
                                    <StopStoryboard BeginStoryboardName="OnMouseOut_BeginStoryboard2"/>
                                    <BeginStoryboard x:Name="OnMouseIn_BeginStoryboard" Storyboard="{StaticResource OnMouseIn}"/>
                                </MultiTrigger.EnterActions>
                                <MultiTrigger.ExitActions>
                                    <BeginStoryboard x:Name="OnMouseOut_BeginStoryboard" Storyboard="{StaticResource OnMouseOut}"/>
                                </MultiTrigger.ExitActions>
                                <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/>
                            </MultiTrigger>
    
                            <Trigger Property="IsChecked" Value="True">
                                <Trigger.ExitActions>
                                    <StopStoryboard BeginStoryboardName="OnMouseOut_BeginStoryboard"/>
                                    <StopStoryboard BeginStoryboardName="OnMouseIn_BeginStoryboard"/>
                                    <BeginStoryboard x:Name="OnMouseOut_BeginStoryboard2" Storyboard="{StaticResource OnMouseOut}"/>
    
                                </Trigger.ExitActions>
                                <Trigger.EnterActions>
                                    <StopStoryboard BeginStoryboardName="OnMouseOut_BeginStoryboard"/>
                                    <StopStoryboard BeginStoryboardName="OnMouseIn_BeginStoryboard"/>
                                </Trigger.EnterActions>
                                <Setter Property="Opacity" TargetName="innerRectangle" Value="1"/>
                            </Trigger>
    
                        </ControlTemplate.Triggers>