【问题标题】:WPF EventTrigger forWPF 事件触发器
【发布时间】:2014-05-14 13:49:29
【问题描述】:

我有一个 WPF Grid,我想在值更改时突出显示一个单元格。这确实适用于下面的EventTrigger,但在加载窗口时会突出显示每个单元格。是否有“更好”的 WPF 方式仅在设置初始值或加载窗口后触发?

<Style TargetType="DataGridCell" x:Key="FlashStyle">
    <Style.Triggers>
        <EventTrigger RoutedEvent="Binding.TargetUpdated">
                <BeginStoryboard>
                    <Storyboard x:Name="Blink" AutoReverse="True">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
                            <EasingColorKeyFrame KeyTime="00:00:01" Value="Red" />
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
        </EventTrigger>
    </Style.Triggers>
</Style>

【问题讨论】:

    标签: c# wpf xaml triggers eventtrigger


    【解决方案1】:

    正如承诺的那样,我已经重新审视了这个答案来举个例子。我发现我的原始答案不起作用,因为无法在运行时修改触发器集合。还有另一种解决方案,我将在这里解释。

    更新答案

    由于我们无法在运行时更改触发器集合,我看到了两个可能的选项:

    1. 制作一个智能的 EventTrigger,它将知道如何处理第一个 TargetUpdated 事件并忽略它。
    2. 创建我们自己的新附加事件,让 EventTrigger 处理它,并仅在我们希望 EventTrigger 触发时引发它,即每次更新目标时,除了第一次。

    第一个选项可以使用Blend SDK and inheriting from TriggerBase&lt;T&gt;。我还没有尝试过,但这可能是一个不错的方法。

    第二个选项相当简单,在这里提供:

    public class AfterLoadBehavior
    {
        #region TargetUpdatedAfterLoad Attached Event
    
        public static readonly RoutedEvent TargetUpdatedAfterLoadEvent = EventManager.RegisterRoutedEvent(
            "TargetUpdatedAfterLoad", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AfterLoadBehavior));
    
        public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
        {
            UIElement uiElement = d as UIElement;
            if (uiElement != null)
            {
                uiElement.AddHandler(TargetUpdatedAfterLoadEvent, handler);
            }
        }
    
        public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
        {
            UIElement uiElement = d as UIElement;
            if (uiElement != null)
            {
                uiElement.RemoveHandler(TargetUpdatedAfterLoadEvent, handler);
            }
        }
    
        #endregion
    
        #region RaiseTargetUpdatedAfterLoadEvent Attached Property
    
        public static bool GetRaiseTargetUpdatedAfterLoadEvent(FrameworkElement obj)
        {
            return (bool)obj.GetValue(RaiseTargetUpdatedAfterLoadEventProperty);
        }
    
        public static void SetRaiseTargetUpdatedAfterLoadEvent(FrameworkElement obj, bool value)
        {
            obj.SetValue(RaiseTargetUpdatedAfterLoadEventProperty, value);
        }
    
        public static readonly DependencyProperty RaiseTargetUpdatedAfterLoadEventProperty =
            DependencyProperty.RegisterAttached("RaiseTargetUpdatedAfterLoadEvent", typeof(bool), typeof(AfterLoadBehavior),
            new PropertyMetadata(false, OnRaiseTargetUpdatedAfterLoadEventChanged));
    
        private static void OnRaiseTargetUpdatedAfterLoadEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var frameworkElement = (FrameworkElement)d;
            frameworkElement.AddHandler(Binding.TargetUpdatedEvent, new RoutedEventHandler((sender, args) =>
                {
                    if (GetIsFirstValueChange(frameworkElement))
                    {
                        SetIsFirstValueChange(frameworkElement, false);
                        return;
                    }
    
                    frameworkElement.RaiseEvent(new RoutedEventArgs(AfterLoadBehavior.TargetUpdatedAfterLoadEvent));
                }));
        }
    
        #endregion
    
        #region IsFirstValueChange Attached Property
    
        public static bool GetIsFirstValueChange(FrameworkElement obj)
        {
            return (bool)obj.GetValue(IsFirstValueChangeProperty);
        }
    
        public static void SetIsFirstValueChange(FrameworkElement obj, bool value)
        {
            obj.SetValue(IsFirstValueChangeProperty, value);
        }
    
        public static readonly DependencyProperty IsFirstValueChangeProperty =
            DependencyProperty.RegisterAttached("IsFirstValueChange", typeof(bool), typeof(AfterLoadBehavior),
            new PropertyMetadata(true));
    
        #endregion
    }
    

    使用这个,你需要像这样修改你的风格:

    <Style TargetType="DataGridCell" x:Key="FlashStyle">
        <Setter Property="local:AfterLoadBehavior.RaiseTargetUpdatedAfterLoadEvent" Value="True" />
        <Style.Triggers>
            <EventTrigger RoutedEvent="{x:Static local:AfterLoadBehavior.TargetUpdatedAfterLoadEvent}">
                    <BeginStoryboard>
                        <Storyboard x:Name="Blink" AutoReverse="True">
                            <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
                                <EasingColorKeyFrame KeyTime="00:00:01" Value="Red" />
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
            </EventTrigger>
        </Style.Triggers>
    </Style>
    

    此行为的作用是注册Binding.TargetUpdated 事件并在第一次调用时忽略它。其他时候,它会引发新创建的TargetUpdatedAfterLoadEvent,由您的EventTrigger 处理。

    结果证明它比我最初预期的要复杂一些,但是这里并没有发生什么疯狂的事情,一旦你将这个行为添加到你的代码中,它就非常容易使用。 希望这会有所帮助。


    原始答案(不起作用)

    我不知道有一个简单的仅限 XAML 的解决方案,但您可以编写一个附加属性,该属性将注册到 DataGridCell 的 Loaded 事件,并且仅在加载后添加事件触发器。

    【讨论】:

    • 这听起来不必要的复杂,我希望有一个优雅的解决方案:)
    • 代码方面,这是一个非常简单的解决方案,可以在 5 分钟内完成编码。我稍后会尝试用一个例子来更新我的答案。
    • 非常好,没有我想象的那么复杂!感谢那个阿迪
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    相关资源
    最近更新 更多