0

I have a situation where I need to select which animation to play on an event trigger based on a selection property. There are two events which always fire in the order A then B.

The solution I've come up with is to use the EventTrigger for A to set the Tag property of the element to 1, on the EvenTrigger for B I set it back to 0. I then have multiple MultiDataTriggers in the element style which have conditions to match on the Tag property value and the selection property, these trigger the corresponding animation.

This all works fine the first-time round, A triggers the first animation, B the second. However, when event A fires again for a second time the MultiDataTrigger no longer seems to trigger. To add to the confusion the animation for B continues to work. So long as the events alternate between A and B the event B animation will play, but not the event A one.

<Window x:Class="WpfTriggerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTriggerTest"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid x:Name="rootLayout">
        <local:TestElement x:Name="testElement">
            <local:TestElement.Resources>
                <Storyboard x:Key="TriggerA">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Tag" Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames.KeyFrames>
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                <DiscreteObjectKeyFrame.Value>
                                    <sys:Int32>1</sys:Int32>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames.KeyFrames>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
                <Storyboard x:Key="TriggerB">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Tag" Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames.KeyFrames>
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" >
                                <DiscreteObjectKeyFrame.Value>
                                    <sys:Int32>0</sys:Int32>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames.KeyFrames>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
                <Storyboard x:Key="AnimA">
                    <ObjectAnimationUsingKeyFrames Storyboard.Target="{x:Reference rootLayout}" Storyboard.TargetProperty="Background" Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames.KeyFrames>
                            <DiscreteObjectKeyFrame>
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="Red" />
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames.KeyFrames>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
                <Storyboard x:Key="AnimB">
                    <ObjectAnimationUsingKeyFrames Storyboard.Target="{x:Reference rootLayout}" Storyboard.TargetProperty="Background" Duration="0:0:0">
                        <ObjectAnimationUsingKeyFrames.KeyFrames>
                            <DiscreteObjectKeyFrame>
                                <DiscreteObjectKeyFrame.Value>
                                    <SolidColorBrush Color="Green" />
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames.KeyFrames>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </local:TestElement.Resources>
            <local:TestElement.Triggers>
                <EventTrigger RoutedEvent="local:TestElement.TriggerA">
                    <BeginStoryboard Storyboard="{StaticResource TriggerA}" />
                </EventTrigger>
                <EventTrigger RoutedEvent="local:TestElement.TriggerB">
                    <BeginStoryboard Storyboard="{StaticResource TriggerB}" />
                </EventTrigger>
            </local:TestElement.Triggers>
            <local:TestElement.Style>
                <Style TargetType="{x:Type local:TestElement}">
                    <Style.Triggers>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Tag, RelativeSource={RelativeSource Self}}" Value="1" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource AnimA}" />
                            </MultiDataTrigger.EnterActions>
                        </MultiDataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Tag, RelativeSource={RelativeSource Self}}" Value="0" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource AnimB}" />
                            </MultiDataTrigger.EnterActions>
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </local:TestElement.Style>
        </local:TestElement>
        <Button Content="TriggerA" HorizontalAlignment="Left" Margin="51,45,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" />
        <Button Content="TriggerB" HorizontalAlignment="Left" Margin="143,45,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
    </Grid>
</Window>

The code posted has been simplified, I removed the second condition from the MultiDataTrigger as it does not effect the issue at hand.

I've monitored the Tag property value and it is correctly getting set to 1 and 0 when the events fire. Why would the trigger only fire once? Or, is there a better approach to solve this problem?

MrQuery
  • 35
  • 6

2 Answers2

1

I think the reason is that your storyboards keep running. You should stop the storyboard before running the other one. Have a look at this answer: WPF Fade In / Out only runs once

On another matter I would say that using storyboards for setting a simple value is an overkill. Consider using an Attached Behaviour or TriggerAction. See here for details and examples: Setting a property with an EventTrigger

Mohammad
  • 1,930
  • 1
  • 21
  • 31
0

The reason is that Tag's value did not change again. It was '1' and then it was '1' again.

The Dependency Property does not run any call back if the value did not change. I suggest before setting '1' set '0' which will do nothing and then set '1' for the actual Trigger.

Hope this helps

eran otzap
  • 12,293
  • 20
  • 84
  • 139
  • The Tag's value gets set back to 0 when the second event fires though. The events always fire in the order A -> B -> A -> ... so the Tag value should be toggling between 1 and 0 (monitoring it in the live property explorer confirms this). To simplify debugging I moved the trigger of the events onto buttons so I could manually control them. Sorry if I didn't make this clear in the question. – MrQuery Feb 20 '19 at 11:31
  • @MrQuery why is it a MultiDataTrigger and Not a DataTrigger I don't see multiple Conditions only one. And while where on the case. If you'r not binding to any thing in your DataContext why not a Trigger and Not a DataTrigger. I'm making these remarks because from my experience WPF just isn't perfect and you should find the minimal way of doing something in it. for instance i never use a multidatatrigger i would simply make a complex property which groups together all the different properties under one binding which i know works well – eran otzap Feb 20 '19 at 13:49
  • I am trying to switch between which animation plays for the event based on a selection property. To simplify the code posted I removed everything that wasn't required to recreate the issue. Within the full code the MultiDataTrigger has an additional condition. With or without this I still see the same behaviour though. – MrQuery Feb 20 '19 at 13:49
  • does Tier Raise a PropertyChanged event ? – eran otzap Feb 20 '19 at 13:50
  • No. Generally, once set on startup the value of Tier will not change throughout the lifecycle of the application. – MrQuery Feb 20 '19 at 13:53