WPF的EventTrigger用于什么?

3

我有一个 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>
1个回答

3
作为承诺,我重新审视了这个答案并给出了一个示例。我发现我的原始答案无法工作,因为不可能在运行时修改触发器集合。但是有另一种解决方案,我将在这里解释。
更新的答案
既然我们无法在运行时更改触发器集合,我看到两个可能的选项:
1.创建一个智能EventTrigger,它将知道如何处理第一个TargetUpdated事件并忽略它。 2.创建我们自己的新附加事件,使EventTrigger处理它,并仅在我们希望EventTrigger触发时引发它,即每次更新目标除第一次之外。
第一种选项可能可以通过使用Blend SDK和从TriggerBase 继承来实现。我没有尝试过,但这可能是一个不错的选择。
第二个选项相当简单,并在此处提供:
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事件,并且仅在加载完成后添加事件触发器。


听起来有些不必要的复杂,我希望能有一个优雅的解决方案 :) - Max Melcher
2
从编码角度来看,这是一个非常简单的解决方案,可以在5分钟内编写代码。我稍后会尝试用一个例子来更新我的答案。 - Adi Lester
非常不错,没有我想象的那么复杂!谢谢你,Adi。 - Max Melcher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接