NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger?
I think I need to combine an EventTrigger
and a DataTrigger
to achieve what I'm after:
Currently I have a DataTemplate that looks like this:
<DataTemplate DataType="{x:Type Notifications:NotificationViewModel}">
<Grid HorizontalAlignment="Stretch">
<Border Name="Background" CornerRadius="8" Background="#80c0c0c0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Border Name="Highlight" CornerRadius="8" Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<!-- snip actual visual stuff -->
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation x:Name="LoadedAnimation"
Storyboard.TargetName="Highlight"
Storyboard.TargetProperty="Opacity"
From="0" To="1"
RepeatBehavior="5x"
Duration="0:00:0.2"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Grid.Triggers>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsCritical}" Value="True">
<Setter TargetName="LoadedAnimation" Property="RepeatBehavior" Value="5.5x" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
The idea is that an EventTrigger animates the Highlight
border's opacity between 0 and 1 and back again repeatedly when the item is first loaded, drawing the user's attention to it. The DataTrigger
determines the number of times to animate. If the view model reports that the item IsCritical
then the animation occurs 5.5 times (such that it ends at opacity 1), otherwise it occurs 5 times (ending at opacity 0.)
However the above XAML doesn't work because the DataTrigger's setter fails with:
Child with Name 'LoadedAnimation' not found in VisualTree.
Fair enough. So, shy of using a custom value converter or putting the animation count on the view model and binding to it, what are my options?
If you have access to the Blend SDK (you should if you're using VS2012+), you should be able to accomplish this entirely in XAML, with something like this (disclaimer: untested):
<Grid HorizontalAlignment="Stretch">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="NotificationStates">
<VisualState x:Name="Flashing">
<Storyboard>
<DoubleAnimation x:Name="LoadedAnimation"
Storyboard.TargetName="Highlight"
Storyboard.TargetProperty="Opacity"
From="0" To="1"
RepeatBehavior="5x"
Duration="0:00:0.2"
AutoReverse="True" />
</Storyboard>
</VisualState>
<VisualState x:Name="Normal" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Name="Background" CornerRadius="8" Background="#80c0c0c0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Border Name="Highlight" CornerRadius="8" Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<!-- snip actual visual stuff -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ic:GoToStateAction StateName="Flashing"/>
</i:EventTrigger>
<ie:DataTrigger Binding="{Binding Path=IsCritical}" Value="True">
<ic:GoToStateAction StateName="Flashing"/>
</ie:DataTrigger>
</i:Interaction.Triggers>
</Grid>
Extract your Storyboard to a VisualState, then use the expression library to switch states within the XAML. You'll need Microsoft.Expression.Interactions library, see also WPF/Silverlight States - Activate from XAML?