wpfxamlanimationmultitrigger

WPF Windows Presentation Foundation Animation using MultiTrigger


I am trying to get the hang of how to do animations using WPF XAML. I'm struggeling to get my animation to trigger based on the right conditions and now I need some help.

Just for laughs I want to make a page with a button that will dodge any attempt to be pressed by moving to a different place on the canvas. Here is an illustration of my desired animations: enter image description here

I want to solve the problem simply using XAML (and no code-behind), but I am open to a solution which uses code if the code is accompanied with an explanation of why this can not be solved simply by declaring the appropriate XAML.

This is what I have so far:

 <Page x:Class="myApp.SecondPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  mc:Ignorable="d" 
  xmlns:c="clr-namespace:myApp"
  d:DesignHeight="300" d:DesignWidth="500"
Title="SecondPage" Height="300" Width="500">
<Page.Resources>        
    <PathGeometry x:Key="AnimationPath" Figures="M 0,0 C -130,-100 -130,-60 -130,-0"/>
    <Style x:Key="secondButton" TargetType="Button">
        <Setter Property="Content" Value="Button"></Setter>
        <Style.Triggers>
            <MultiTrigger >
                <MultiTrigger.Conditions>
                    <Condition  Property="IsMouseOver" Value="True"></Condition>
 <!--Doesn't work --><Condition Property="TranslateTransform.X" Value="0"></Condition>
                </MultiTrigger.Conditions>
                <MultiTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimationUsingPath x:Name="XAnim"
                                   FillBehavior="HoldEnd"
                                   Storyboard.TargetProperty="RenderTransform.X"
                                   PathGeometry="{StaticResource AnimationPath}"
                                   Source="X" 
                                   Duration="0:0:.2"  
                                   />
                            <DoubleAnimationUsingPath x:Name="YAnim"
                                   FillBehavior="HoldEnd"
                                   Storyboard.TargetProperty="RenderTransform.Y"                                       
                                   PathGeometry="{StaticResource AnimationPath}"
                                   Source="Y" 
                                   Duration="0:0:.2"  
                                  />                             
                        </Storyboard>

                    </BeginStoryboard>

                </MultiTrigger.EnterActions>
            </MultiTrigger>
        </Style.Triggers>
    </Style>
</Page.Resources>

    <Grid>
    <Canvas HorizontalAlignment="Stretch" Name="MyCanvas" VerticalAlignment="Stretch" IsManipulationEnabled="True">
      <Button  Style="{StaticResource secondButton}" Canvas.Left="150" Canvas.Top="240" Height="48" x:Name="theButton" Width="115" FontSize="18" ForceCursor="False">
            <Button.RenderTransform>
                <TranslateTransform x:Name="AnimatedTranslateTransform"/>
            </Button.RenderTransform>
        </Button>
    </Canvas>
</Grid>

In my second multitrigger condition I am trying to check where the button is (in order to start the right storyboard). I simply cannot seem to figure out which property and value to use in the condition to evaluate whether the button is on the left or right hand side. The TranslateTransform.X property doesn't work. Any suggestions?


Solution

  • You need to work with MultiDataTrigger here to get through the Transform:

    <Style x:Key="secondButton" TargetType="Button">
        <Style.Triggers>
            <!-- ... -->
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
                            Value="True" />
                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=RenderTransform.X}"
                            Value="0"/>
                </MultiDataTrigger.Conditions>
                <!-- ... -->
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
    

    (In a normal MultiTrigger you can only access immediate properties, Property="TranslateTransform.X" is not only wrong since it should be Property="RenderTransform.X" but it further would be evaluated as an attached property which does not exist)

    Since you named the Transform you could also use a binding using ElementName instead of RelativeSource in the second condition.

    <Condition Binding="{Binding ElementName=AnimatedTranslateTransform, Path=X}"
               Value="0"/>