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 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。