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种!!

这里我们仅研究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。所以我们从实现上了解到了FrameworkElement的Triggers仅支持EventTrigger
FrameworkElement在初始化的时候会调用EventTrigger的ProcessTriggerCollection( FrameworkElement triggersHost )方法,针对集合中的每一个事件,为FrameworkElement添加监听器,而在事件触发时,引发监听器的Handler,使EventTrigger中Actions集合的每一个TriggerAction得到执行
EventTrigger进化史
文章在之前本应该结束的,但是EventTrigger这个家伙的故事貌似并没有这么简单
执行Actions的实际上是调用它的Invoke方法。这是一个抽象方法,可以通过继承实现自己的逻辑。
比如说在 SoundPlayerAction中就实现了如下工作
internal sealed override void Invoke(FrameworkElement el)
{
PlayWhenLoaded();
}
多么美妙的设计啊,这么棒的东西微软一定为我们实现了很多用法吧!
于是我们打开MSDN。。。

什么鬼!只能做动画!根本不够用好吗?于是有这么一段对话(纯属虚构)
B:我要发命令!MS:请用Code Behind
B:我有后台不写代码强迫症!MS:您不能走极端,我们的方案是很合理的实现
B:我叫Blend。 MS:做做做,立刻做,马上做!
于是Blend自己实现了一套新的EventTrigger系统,在System.WIndows.Interactivity.dll中
原理都很类似,重点是两个
- 提供了
InvokeCommandAction类,可以通过EventTrigger触发Command的CanExecute和Execute - 他的
TriggerAction传入触发事件的EventArgs作为参数
A:哇,从此可以通过EventTrigger发命令啦!另外有了EventArgs可以做事件,还可以给命令传参,可以。。。
Blend:哦!除了第一个以外,剩下的我都没做。你自己实现咯
P:
Blend:骗你的,在Microsoft.Expression.Interaction.dll中实现了好多哦

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 为触发事件 EventArgs的InvokeCommandAction类。
源码反编译失败了,这里贴一段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);
}
}
链接
本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/EventTrigger.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接:
https://xinyuehtx.github.io
),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请
与我联系
。