在WPF中,如何覆盖事件触发器?

时间:2016-04-06 10:18:14

标签: wpf xaml

我有一个带有多个按钮的StackPanel。我希望除了一个按钮之外的所有按钮在用户点击时触发动画,所以在StackPanel.Triggers我添加了这个代码:

<StackPanel.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
        <BeginStoryboard Storyboard="{StaticResource animationName}" />
    </EventTrigger>
</StackPanel.Triggers>

在特定按钮中,我添加了以下代码:

<Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
        <BeginStoryboard Storyboard="{StaticResource anotherAnimation}" />
    </EventTrigger>
</Button.Triggers>

单击按钮时,两个动画都会开始,因此第二个EventTrigger似乎只会添加到第一个EventTrigger而不会覆盖它。

如何覆盖第一个<Storyboard x:Key="animationName"> <DoubleAnimation Storyboard.TargetName="PageFrame" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.25" /> </Storyboard> ,以便在点击该特定按钮时只会触发第二个?

注意:我需要答案是在纯XAML中,而不涉及任何代码隐藏。

编辑:以下是故事板:

{{1}}

2 个答案:

答案 0 :(得分:3)

只需使用x:Key属性即可获得必要的按钮。例如:

<Window>
    <Window.Resources>
       <Style x:Key="myStyle" TargetType="Button">
          <Setter Property="Background" Value="Green"/>
          <Style.Triggers>
             <EventTrigger RoutedEvent="Button.Click">
    <BeginStoryboard Storyboard="{StaticResource animationName}" />
             </EventTrigger>
           </Style.Triggers>
       </Style>
    </Window.Resources>

    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
       <Button Style="{StaticResource myStyle}">Styles are cool!</Button>
       <Button>No Animation:)</Button>
       <Button Style="{StaticResource myStyle}">Yes to animation!</Button>
    </StackPanel>
</Window>

<强>更新

如果您想避免仅为几个按钮使用Style,只需为所有Button控件创建样式,并将Style="{x:Null}"设置为控制您想要避免动画的位置。请参阅以下示例:

<Window>
    <Window.Resources>
       <!--This style will be applied to all Buttons, except where Style="{x:Null}"-->
       <Style TargetType="Button">
          <Style.Resources>
            <Storyboard x:Key="animationName">
                <DoubleAnimation Storyboard.TargetProperty="Opacity"
    To="0" Duration="0:0:0.25" />
            </Storyboard>
        </Style.Resources> 
          <Setter Property="Background" Value="Green"/>
          <Style.Triggers>
             <EventTrigger RoutedEvent="Button.Click">
    <BeginStoryboard Storyboard="{StaticResource animationName}" />
             </EventTrigger>
           </Style.Triggers>
       </Style>
    </Window.Resources>

    <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
      <Button Content="Yes to Animation"/>
      <Button Content="No Animation:)" Style="{x:Null}"/>
      <Button Content="Yes to Animation"/>
    </StackPanel>
</Window>

更新1:

您已删除了TargetName,但我确实需要设置它以便将动画应用于正确的元素。

由于样式可以在WPF应用程序的多个位置重用,因此我们无法在样式中引用UIElement。这种行为是设计使然。

答案 1 :(得分:2)

正如所承诺的那样,我从这个link接受了@RayBurns的回答并对其进行了修改,以回答你的问题。 ConditionalEventTrigger现在看起来像这样:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Markup;

namespace Trigger
{
[ContentProperty("Actions")]
public class ConditionalEventTrigger : FrameworkContentElement
{
    private static readonly RoutedEvent TriggerActionsEvent = EventManager.RegisterRoutedEvent("", RoutingStrategy.Direct, typeof(EventHandler), typeof(ConditionalEventTrigger));
    public RoutedEvent RoutedEvent { get; set; }

    public static readonly DependencyProperty ExcludedSourceNamesProperty = DependencyProperty.Register(
        "ExcludedSourceNames", typeof (List<string>), typeof (ConditionalEventTrigger), new PropertyMetadata(new List<string>()));

    public List<string> ExcludedSourceNames
    {
        get { return (List<string>) GetValue(ExcludedSourceNamesProperty); }
        set { SetValue(ExcludedSourceNamesProperty, value); }
    }

    public static readonly DependencyProperty ActionsProperty = DependencyProperty.Register(
        "Actions", typeof (List<TriggerAction>), typeof (ConditionalEventTrigger), new PropertyMetadata(new List<TriggerAction>()));

    public List<TriggerAction> Actions
    {
        get { return (List<TriggerAction>) GetValue(ActionsProperty); }
        set { SetValue(ActionsProperty, value); }
    }

    // "Triggers" attached property
    public static ConditionalEventTriggerCollection GetTriggers(DependencyObject obj) { return (ConditionalEventTriggerCollection)obj.GetValue(TriggersProperty); }
    public static void SetTriggers(DependencyObject obj, ConditionalEventTriggerCollection value) { obj.SetValue(TriggersProperty, value); }
    public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached("Triggers", typeof(ConditionalEventTriggerCollection), typeof(ConditionalEventTrigger), new PropertyMetadata
    {
        PropertyChangedCallback = (obj, e) =>
        {
            // When "Triggers" is set, register handlers for each trigger in the list 
            var element = (FrameworkElement)obj;
            var triggers = (List<ConditionalEventTrigger>)e.NewValue;
            foreach (var trigger in triggers)
                element.AddHandler(trigger.RoutedEvent, new RoutedEventHandler((obj2, e2) =>
                  trigger.OnRoutedEvent(element, e2)));
        }
    });



    // When an event fires, check the condition and if it is true fire the actions 
    void OnRoutedEvent(FrameworkElement element, RoutedEventArgs args)
    {
        var originalSender = args.OriginalSource as FrameworkElement;
        if(originalSender == null) return;
        DataContext = element.DataContext;  // Allow data binding to access element properties
        if (!ExcludedSourceNames.Any(x=>x.Equals(originalSender.Name)))
        {
            // Construct an EventTrigger containing the actions, then trigger it 
            var dummyTrigger = new EventTrigger { RoutedEvent = TriggerActionsEvent };
            foreach (var action in Actions)
                dummyTrigger.Actions.Add(action);

            element.Triggers.Add(dummyTrigger);
            try
            {
                element.RaiseEvent(new RoutedEventArgs(TriggerActionsEvent));
            }
            finally
            {
                element.Triggers.Remove(dummyTrigger);
            }
        }
    }
}

public class ConditionalEventTriggerCollection: List<ConditionalEventTrigger>{}
}

它可以在你的XAML中使用。请注意,在执行您的操作时,您不希望识别的所有SourceNames都在ExcludedSourceNames部分内。:

        <trigger:ConditionalEventTrigger.Triggers>
        <trigger:ConditionalEventTriggerCollection>
            <trigger:ConditionalEventTrigger RoutedEvent="Button.Click">
                <trigger:ConditionalEventTrigger.ExcludedSourceNames>
                    <system:String>buttonTriggeringAnotherAnimation</system:String>
                </trigger:ConditionalEventTrigger.ExcludedSourceNames>
                <BeginStoryboard Storyboard="{StaticResource Storyboard1}"></BeginStoryboard>
            </trigger:ConditionalEventTrigger>
        </trigger:ConditionalEventTriggerCollection>
    </trigger:ConditionalEventTrigger.Triggers>

为了让您准备好开始示例,这里有一个窗口:

<Window x:Class="ConditionalEventTriggerExample.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:ConditionalEventTriggerExample"
    xmlns:trigger="clr-namespace:Trigger;assembly=Trigger"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <Storyboard x:Key="Storyboard1">
        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle">
            <EasingColorKeyFrame KeyTime="0:0:1" Value="#FF5151FD"/>
        </ColorAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="Storyboard2">
        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle1">
            <EasingColorKeyFrame KeyTime="0:0:1" Value="#FFFF7400"/>
        </ColorAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
<StackPanel>
    <StackPanel.Triggers>
        <EventTrigger RoutedEvent="Button.Click" SourceName="buttonTriggeringAnotherAnimation">
            <BeginStoryboard Storyboard="{StaticResource Storyboard2}"/>
        </EventTrigger>
    </StackPanel.Triggers>
    <trigger:ConditionalEventTrigger.Triggers>
        <trigger:ConditionalEventTriggerCollection>
            <trigger:ConditionalEventTrigger RoutedEvent="Button.Click">
                <trigger:ConditionalEventTrigger.ExcludedSourceNames>
                    <system:String>buttonTriggeringAnotherAnimation</system:String>
                </trigger:ConditionalEventTrigger.ExcludedSourceNames>
                <BeginStoryboard Storyboard="{StaticResource Storyboard1}"></BeginStoryboard>
            </trigger:ConditionalEventTrigger>
        </trigger:ConditionalEventTriggerCollection>
    </trigger:ConditionalEventTrigger.Triggers>
    <Button x:Name="button" Content="Button"/>
    <Button x:Name="button1" Content="Button"/>
    <Button x:Name="buttonTriggeringAnotherAnimation" Content="triggering another animation"/>
    <Button x:Name="button3" Content="Button"/>
    <Button x:Name="button4" Content="Button"/>
    <Button x:Name="button5" Content="Button"/>
    <Rectangle x:Name="rectangle" Fill="#FFF4F4F5" Height="100" Stroke="Black"/>
    <Rectangle x:Name="rectangle1" Fill="#FFF4F4F5" Height="100" Stroke="Black"/>
</StackPanel>

如果你没有让它工作,我可以在GitHub上传解决方案。