EventTrigger does not work when declared in Window.Resources in WPF

I am new to WPF, so I may miss something substantial, but I experimented and tried to come up with an explanation for the following phenomenon, but to no avail. Basically, the following code works (displays animation):

<Window.Resources> <Storyboard x:Key="LoadStoryBoard" AutoReverse="True" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" Storyboard.TargetProperty="(Button.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> ... <Button x:Name="button1" Grid.Column="0" Grid.Row="1" Style="{StaticResource Load}"> <Button.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" /> </EventTrigger> </Button.Triggers> </Button> 

However, when I try to put an eventrigger in the Load Style in the following, the animation stops appearing:

  <Window.Resources> <Storyboard x:Key="LoadStoryBoard" AutoReverse="True" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" Storyboard.TargetProperty="(Button.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> ... <Style x:Key="Load" TargetType="Button"> ... <Style.Triggers> ... <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" /> </EventTrigger> </Style.Triggers> </Style> 
+4
source share
2 answers

In Style triggers, you cannot use objects with TargetName , such animations. To do this, they are placed in the <ControlTemplate.Triggers> trigger template. Quote from the link :

TargetName is not intended to be used in a collection of style triggers. A style does not have a name tool, so it makes no sense to refer to elements by name. But the template (either DataTemplate or ControlTemplate) has an identifier.

The following works:

 <Window.Resources> <Storyboard x:Key="LoadStoryBoard" AutoReverse="True" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="button1" Storyboard.TargetProperty="(Button.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> <Style x:Key="ButtonStyle" TargetType="{x:Type Button}"> <Setter Property="Background" Value="Green" /> <Setter Property="Foreground" Value="White" /> <Setter Property="FontSize" Value="14" /> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="button1" CornerRadius="0" Background="{TemplateBinding Background}"> <Grid> <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" /> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Orange" /> </Trigger> <EventTrigger RoutedEvent="Button.Loaded"> <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" /> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Button Name="TestButton" Style="{StaticResource ButtonStyle}" Width="100" Height="30" Content="Test" Grid.Column="0" Grid.Row="1" /> </Grid> 

Please note that now TargetName in the template specified in Border : <Border x:Name="button1" .../> .

Note: Or you can simply remove Storyboard.TargetName , since it calls a style that is not supported.

+3
source

You are correct that EventTrigger does not work, but that is not because it was declared in the Resources section. To see this, you can drag your style directly into a Button declaration, where it still doesn't work:

 <Button x:Name="button1" Grid.Column="0" Grid.Row="1"> <Button.Style> <Style TargetType="Button"> <Style.Triggers> <EventTrigger RoutedEvent="Button.Loaded"> <BeginStoryboard Storyboard="{StaticResource LoadStoryBoard}" /> </EventTrigger> </Style.Triggers> </Style> </Button.Style> </Button> 

However, if we move the Animation ad from the Resources section, it will work again:

 <Button x:Name="button1" Grid.Column="0" Grid.Row="1"> <Button.Style> <Style TargetType="Button"> <Style.Triggers> <EventTrigger RoutedEvent="Button.Loaded"> <BeginStoryboard> <Storyboard AutoReverse="True" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Button.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Style.Triggers> </Style> </Button.Style> </Button> 

The problem seems to be related to the Storyboard declared in the Resources section, not ready at the time of the Loaded event. A similar problem is noted in this post .

However, just to make things more confusing, if we put the full announcement for Animation in Style , declared in the Resources section, now Style works:

 <Window.Resources> <Style x:Key="Load" TargetType="Button"> <Style.Triggers> <EventTrigger RoutedEvent="Button.Loaded"> <BeginStoryboard> <Storyboard AutoReverse="True" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Button.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0.4" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <Button x:Name="button1" Grid.Column="0" Grid.Row="1" Style="{StaticResource Load}" /> 

I could have guessed why this is happening, but I assume that there are very few WPF developers who really know why everything is as it is ... I found out that if a certain ad method works, use it, and if not, try other.

Background

There are four places in WPF where we can define Triggers ; Style.Triggers , ControlTemplate.Triggers , DataTemplate.Triggers and FrameworkElement.Triggers (e.g. Button.Triggers ).

Basically, there is a big flaw in the FrameworkElement.Triggers TriggerCollection , since it only accepts triggers of the EventTrigger type. This can be seen on the FrameworkElement.Triggers Property page on MSDN, which indicates the following definition of what this property can accept:

One or more specific EventTrigger elements. Each such trigger is expected to contain valid actions and links to the storyboard. Please note that this collection can only be installed on the root element of the page.

The MSDN property pages for other trigger properties report that they can accept Zero or more TriggerBase objects or one or more TriggerBase objects.

In addition, there are different rules that different triggers follow - a single approach would undoubtedly help WPF newbies. From the FrameworkElement.Triggers Property page:

This property does not allow you to view triggers that exist as part of the styles used in this element. It only reports a collection of triggers that are literally added to the collection, either to markup or code. Elements usually do not have such elements by default (via a template, for example); It is more common for triggers that come from control compositing, which will be set in styles.

From the point of view of behavior (and attempts to establish which effect came from which element the trigger collection is declared), how the trigger condition and the effect of the trigger can be on this element or can be on its children in the logical tree. Please note that if you use ones such as “Downloaded” to get this collection, the child triggers for the elements cannot yet be fully loaded, and the collection will be smaller than it would be at run time.

Note that the set of triggers set only for an element supports EventTrigger, not property triggers (trigger). If you need property triggers, you must put them in a style or template and then assign this style or template to the element either directly through the Style property, or indirectly through an implicit link style.

On the DataTemplate.Triggers Property page on MSDN:

If you create triggers in a data template, setter triggers must set properties that fall within the scope of the data template. Otherwise, there may be more triggers using a style intended for the type containing the data. For example, if you bind a ListBox control, containers ListBoxItem objects. If you use triggers to set properties that are not part of the DataTemplate, then this might be more suitable for creating a ListBoxItem style and creating triggers within that style.

Unfortunately, all this additional information does not actually answer your question about why the animation resource does not work in the Style resource, but hopefully you can now see that the entire Trigger area is a bit complicated, dirty area. Not being an expert myself, I just try to use any Trigger declaration method that works.

I hope this helps.

+3
source

Source: https://habr.com/ru/post/1492858/


All Articles