EventTrigger相信大家都会写,就和下面的东西一样样的。


<EventTrigger RoutedEvent="Mouse.MouseEnter">          
  <EventTrigger.Actions>            
    <BeginStoryboard>              
      <Storyboard>                
        <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="MaxHeight" To="90"/>            </Storyboard>            
    </BeginStoryboard>          
  </EventTrigger.Actions>        
</EventTrigger>

这个例子很简单,检测到MouseEnter事件后,触发一段动画。

那么EventTrigger是怎么实现这一功能的呢?

首先我们知道EventTrigger被存储在一个叫做Triggers的集合里面

那么我们打开referencesource,搜索一下Triggers,发现居然有4种!!

QQ截图20171019105123

这里我们仅研究FrameworkElement的Triggers。

 public TriggerCollection Triggers
        {
            get
            {
                TriggerCollection triggerCollection = EventTrigger.TriggerCollectionField.GetValue(this);
                if (triggerCollection == null)
                {
                    // Give the TriggerCollectiona back-link so that it can update
                    // 'this' on Add/Remove.
                    triggerCollection = new TriggerCollection(this);
 
                    EventTrigger.TriggerCollectionField.SetValue(this, triggerCollection);
                }
 
                return triggerCollection;
            }
        }

第一眼我们发现它实际上只是一个放了 TriggerCollection,但是第二眼我们发现这个TriggerCollection居然来自EventTrigger.TriggerCollectionField。所以我们从实现上了解到了FrameworkElementTriggers仅支持EventTrigger

FrameworkElement在初始化的时候会调用EventTriggerProcessTriggerCollection( FrameworkElement triggersHost )方法,针对集合中的每一个事件,为FrameworkElement添加监听器,而在事件触发时,引发监听器的Handler,使EventTriggerActions集合的每一个TriggerAction得到执行

EventTrigger进化史

文章在之前本应该结束的,但是EventTrigger这个家伙的故事貌似并没有这么简单

执行Actions的实际上是调用它的Invoke方法。这是一个抽象方法,可以通过继承实现自己的逻辑。

比如说在 SoundPlayerAction中就实现了如下工作

internal sealed override void Invoke(FrameworkElement el)
       {
           PlayWhenLoaded();
       }

多么美妙的设计啊,这么棒的东西微软一定为我们实现了很多用法吧!

于是我们打开MSDN。。。

QQ截图20171019112756

什么鬼!只能做动画!根本不够用好吗?于是有这么一段对话(纯属虚构)

B:我要发命令!MS:请用Code Behind

B:我有后台不写代码强迫症!MS:您不能走极端,我们的方案是很合理的实现

B:我叫Blend。 MS:做做做,立刻做,马上做!

于是Blend自己实现了一套新的EventTrigger系统,在System.WIndows.Interactivity.dll

原理都很类似,重点是两个

  1. 提供了InvokeCommandAction类,可以通过EventTrigger触发CommandCanExecuteExecute
  2. 他的TriggerAction传入触发事件的EventArgs作为参数

A:哇,从此可以通过EventTrigger发命令啦!另外有了EventArgs可以做事件,还可以给命令传参,可以。。。

Blend:哦!除了第一个以外,剩下的我都没做。你自己实现咯

P:QQ图片20171019115655

Blend:骗你的,在Microsoft.Expression.Interaction.dll中实现了好多哦

QQ截图20171019115920

P:我要在Command中传EventArgs

Blend:少年郎,这种东西很奇怪咯。你要不试试CallMethodAction,可好用了呢

P:不管我就要(PS:MSDN的原话是Sometimes you need to pass a parameter to the command that comes from the parent trigger,我也不知道具体场景)

Blend:我不做

P(Prism):我自己做

于是Prism在Microsoft.Practice.Prism.Interactivity.dll中提供了默认CommandParamater 为触发事件 EventArgsInvokeCommandAction类。

源码反编译失败了,这里贴一段MVVMLight的EventToCommand的实现,原理基本是一样的

 protected override void Invoke(object parameter)
        {
            if (AssociatedElementIsDisabled() 
                && !AlwaysInvokeCommand)
            {
                return;
            }

            var command = GetCommand();
            var commandParameter = CommandParameterValue;

            if (commandParameter == null
                && PassEventArgsToCommand)
            {
                commandParameter = EventArgsConverter == null
                    ? parameter
                    : EventArgsConverter.Convert(parameter, EventArgsConverterParameter);
            }

            if (command != null
                && command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }

链接

MVVM实现

MVVM进阶

MVVMLight源码EventToCommand


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/EventTrigger.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系