1.简介
新发布的 Silverlight 3,有很多很cool的新功能。 我最喜欢的绝对是行为和触发器的支持。 在 WPF 中,触发器是非常强大。 他们允许您以声明方式将操作关联的事件或属性的值。 Silverlight 的早期版本中的一直就是欠缺真正的触发器和行为。 不能像例如将添加对象声明的方式 (如在 WPF) 中的鼠标转移。 您必须编写程序的代码,但代码是被分离的,设计器设计时不必编写它或知道它 这篇文章我会完全集中在 Silverlight 中的新功能,并会向您展示如何创建您自己的自定义的行为和触发器。
您可以在每个节的结尾找到该演示。
篇写行为和触发器需要给您的计算机安装上 Microsoft Expression Blend SDK 。 之后,您需要添加位于 System.Windows.Interactivity.dll 程序集的引用:
\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight {程序文件}
我不知道为什么,但目前此程序集还不是 Silverlight SDK 的一部分。 您可以从 这里 下载 Expression Blend 产品。
目前,您可以使用三种类型的行为: Behavour、 TriggerAction 和 TargetedTriggerAction。 下图显示了类的关系图:

对于简单情况的泛型 Behavior<t> 类是最好的选择,此类具有只有两个可重写的方法,通知当前的行为,当连接对象和将其结束时。 我将创建两个行为: 在单击该图像时,第一次反转图像的着色,而是更复杂的是第二个 — 它添加动画,并在图像的鼠标中创建一个放大镜。
第一件事就是当你创建的行为是一个新的类,该类从 Behavior<t>泛型类继承,当中 T 是一个依赖项对象时,在大多数情况下您将继承自 Behavior<frameworkelement> 或 Behavior<uielement>.
1: public class InverseColorClickBehavior : Behavior<FrameworkElement>
2: {3: public InverseColorClickBehavior() : base()
4: {5: 6: }7: }在个别的情况下,我对鼠标单击事件很感兴趣。 我会附加到与对象关联的 MouseLeftButtonDown 事件到 OnAttached 方法中去。 在 OnDetaching 方法中我将结束该活动。
1: protected override void OnAttached()
2: {3: base.OnAttached();
4: 5: this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler( AssociatedObject_MouseLeftButtonDown );
6: } 7: 8: protected override void OnDetaching()
9: {10: base.OnDetaching();
11: 12: this.AssociatedObject.MouseLeftButtonDown -= new MouseButtonEventHandler( AssociatedObject_MouseLeftButtonDown );
13: }剩下的只是在鼠标单击事件处理程序关联的对象中添加反颜色的效果。
1: private void AssociatedObject_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
2: {3: this.AssociatedObject.Effect =
4: this.AssociatedObject.Effect == null ?
5: this.AssociatedObject.Effect = this.inverseColor :
6: this.AssociatedObject.Effect = null;
7: }我们已经创建了该行后,我们应将其附加到特定对象。
1: <Image Stretch="Fill"
2: Source="/Photos/Image1.jpg">
3: <interactivity:Interaction.Behaviors>
4: <local:InverseColorClickBehavior/>
5: </interactivity:Interaction.Behaviors>
6: </Image>
以下演示, 当第一次您单击时颜色会反转,当第二次单击会还原原来的着色。
更复杂的是第二个特性。 它是常见的方案,您要向其中添加动画,例如鼠标事件。 再次创建与第一个示例那样的新类,该类继承自泛型 Behavior<t> T 于一个 FrameworkElement。
我想当鼠标在该元素上的时候添加一个放大镜和动画效果,当鼠标离开就删除这些效果作用后的元素范围。 我想要处理 MouseEnter、 MouseLeave 事件命令启用和禁用效果。 对 OnAttached 方法中的前一个实例,我将附加到与性能对象关联的 MouseEnter、 MouseLeave 事件中。 并分别在 OnDetaching 方法中我会结束该效果。 鼠标在元素上时我想要跟踪鼠标移动的活动。 这就是为什么我会附加鼠标移动事件,并会当它离开时结束元素效果。
1: protected override void OnAttached()
2: {3: base.OnAttached();
4: 5: this.AssociatedObject.MouseEnter += new MouseEventHandler( AssociatedObject_MouseEnter );
6: this.AssociatedObject.MouseLeave += new MouseEventHandler( AssociatedObject_MouseLeave );
7: } 8: 9: protected override void OnDetaching()
10: {11: base.OnDetaching();
12: 13: this.AssociatedObject.MouseEnter -= new MouseEventHandler( AssociatedObject_MouseEnter );
14: this.AssociatedObject.MouseLeave -= new MouseEventHandler( AssociatedObject_MouseLeave );
15: }16: 17: private void AssociatedObject_MouseLeave( object sender, MouseEventArgs e )
18: {19: this.AssociatedObject.MouseMove -= new MouseEventHandler( AssociatedObject_MouseMove );
20: this.AssociatedObject.Effect = null;
21: }22: 23: private void AssociatedObject_MouseEnter( object sender, MouseEventArgs e )
24: {25: this.AssociatedObject.MouseMove += new MouseEventHandler( AssociatedObject_MouseMove );
26: this.AssociatedObject.Effect = this.magnifier;
27: }整个鼠标移动事件处理程序的工作。
1: private void AssociatedObject_MouseMove( object sender, MouseEventArgs e )
2: {3: ( this.AssociatedObject.Effect as Magnifier ).Center =
4: e.GetPosition( this.AssociatedObject );
5: 6: Point mousePosition = e.GetPosition( this.AssociatedObject );
7: mousePosition.X /= this.AssociatedObject.ActualWidth;
8: mousePosition.Y /= this.AssociatedObject.ActualHeight;
9: this.magnifier.Center = mousePosition;
10: 11: Storyboard zoomInStoryboard = new Storyboard();
12: DoubleAnimation zoomInAnimation = new DoubleAnimation();
13: zoomInAnimation.To = this.magnifier.Magnification;
14: zoomInAnimation.Duration = TimeSpan.FromSeconds( 0.5 );
15: Storyboard.SetTarget( zoomInAnimation, this.AssociatedObject.Effect );
16: Storyboard.SetTargetProperty( zoomInAnimation, new PropertyPath( Magnifier.MagnificationProperty ) );
17: zoomInAnimation.FillBehavior = FillBehavior.HoldEnd;
18: zoomInStoryboard.Children.Add( zoomInAnimation );19: zoomInStoryboard.Begin();20: }可以添加这种特效到XAML中,其方式与第一种相同。 这是第二个演示 (其中包括也许最常用的动画、 影响等事件)。 只是将您的鼠标光标移到形象上。
若要汇总之前,我认为触发器非常类似于 C# 中的扩展方法特性。 唯一的区别是行为是一个组件。 它封装了一些功能,可以附加到另一个组件来扩展其内置的功能.
简单的情况下 Behavior<t> 类是足够用的。 TriggerAction<t> 类可用于更为常见的情况下,比起简单的 Behavior<t>. TriggerAction<t> 提供了调用方法,一旦事件触发器被激发时,当您使用Behavior<t> 负责所需行为, 使用TriggerAction<t> 您可以指定事件触发XAML中的操作,您将会看到以下一个演示。
我将创建将应用动画的单击事件触发器。 第一步是创建一个新的类,该类继承自泛型 TriggerAction<t> 类。
1: public class WaveTrigger : TriggerAction<FrameworkElement>
2: {3: protected override void Invoke(object parameter)
4: {5: this.waveStoryboard.Begin();
6: }7: }正如您所看到的 Invoke 方法是唯一的方法,但必须在您的触发器中。 但通常在实践中您将需要重写此类的其他几种方法。 在我们的触发器的构造函数中,我需要配置动画和过程的摘要。
1: public WaveTrigger() :
2: base()
3: {4: this.waveAnimation = new DoubleAnimation();
5: this.waveStoryboard = new Storyboard();
6: this.waveEffect = new WaveEffect();
7: this.InitializeTrigger();
8: this.waveAnimation.AutoReverse = false;
9: this.waveStoryboard.Children.Add( this.waveAnimation );
10: }11: 12: private void InitializeTrigger()
13: {14: this.waveAnimation.From = -this.WaveFrequency;
15: this.waveAnimation.To = this.WaveFrequency;
16: this.waveAnimation.Duration = this.WaveDuration;
17: }要设置该动画的对象,以及该动画的属性,您需要重写OnAttached 方法。 如果您试图这样做,例如在构造函数中你是不会成功的,因为关联的对象仍是未知对象。
1: protected override void OnAttached()
2: {3: base.OnAttached();
4: 5: this.AssociatedObject.Effect = this.waveEffect;
6: Storyboard.SetTarget( this.waveAnimation, this.AssociatedObject.Effect );
7: Storyboard.SetTargetProperty( this.waveAnimation, new PropertyPath( WaveEffect.WavinessProperty ) );
8: }您需要做的 Invoke 方法是启动动画的方法。
1: protected override void Invoke( object parameter )
2: {3: this.waveStoryboard.Begin();
4: }为了使用我会加上 WaveTrigger, 它可配置 (持续时间和频率)的几个依赖属性。 我们的触发器的最后一步是应用到 XAML 中。
1: <Image Stretch="Fill"
2: Source="Photos/Image3.jpg">
3: <interactivity:Interaction.Triggers>
4: <interactivity:EventTrigger EventName="MouseLeftButtonUp">
5: <local:WaveTrigger WaveFrequency="1.9" WaveDuration="00:00:05"/>
6: </interactivity:EventTrigger>
7: </interactivity:Interaction.Triggers>
8: </Image>
在这里,您可以看到我们的触发器的结果 只需单击该图像,该触发器将被执行。
第三个类型的行为是由泛型 TargetedTriggerAction<t>提供的类。 它表示操作时可以面向一个完全不同的对象,而不是其关联的对象的影响。 它允许您将触发器关联从一个对象到另一个对象,操作完全不同的元素。 例如在跟着下来的演示,我将用一个的按钮关联目标的触发器,它将被翻转, 第一步要创建一个新的类,该类继承自 TargetedTriggerAction<t>是一个 image.Again 类。
1: public class TurnImageTargetedTrigger : TargetedTriggerAction<FrameworkElement>
2: { 3: protected override void Invoke(object parameter)
4: { 5: 6: } 7: protected override void OnAttached()
8: {9: base.OnAttached();
10: (this.AssociatedObject as FrameworkElement).Loaded += new RoutedEventHandler(TurnImageTargetedTrigger_Loaded);
11: } 12: protected override void OnDetaching()
13: {14: base.OnDetaching();
15: (this.AssociatedObject as FrameworkElement).Loaded -= new RoutedEventHandler(TurnImageTargetedTrigger_Loaded);
16: } 17: private void TurnImageTargetedTrigger_Loaded(object sender, RoutedEventArgs e)
18: {19: 20: }21: }在这里目标对象可以访问关联的对象在加载的时候,这就是为什么我要附加 Loaded 事件到相关联对象。 此外必须定义一个 Invoke 方法,启动动画的方法。触发器已准备就绪后,我们要在 XAML 中使用它。 在以下代码片段中,您可以查看。
1: <Image x:Name="imgToFlip" Stretch="Fill"
2: Source="Photos/Image4.jpg"/>
3: <Button Content="TurnImage" FontSize="14" FontFamily="{StaticResource PrimaryFontFamily}"
4: Grid.Row="1" Width="100" Margin="0,5,0,0">
5: <interactivity:Interaction.Triggers>
6: <interactivity:EventTrigger EventName="Click">
7: <local:TurnImageTargetedTrigger TargetName="imgToFlip"/>
8: </interactivity:EventTrigger>
9: </interactivity:Interaction.Triggers>
10: </Button>
下图可以看到该演示。
我们的行为 (触发器) 可以直接使用在 XAML 中或通过 Expression Blend 3 添加它。

一旦添加了,就可以在任何元素行为中使用到对象的内部。

您可以调整行为的相关东西(设置属性,激发事件,等等)在 属性 窗口中。

如果您只创建了行为到当前的解决方案中, Expression Blend 的行为资料库本身已有几个其他行为, 如果您希望自己的自定义行为也列出到所有 Blend 项目中,您需要在注册表中注册程序集。 您需要使用 注册表编辑器 中去到以下路径进行编辑:
HKEY_CURRENT_USER (或 HKEY_LOCAL_MACHINE) 的 \Software\Microsoft\Expression\Blend\v3.0\Toolbox\Silverlight\v3.0
8.使用 DefaultTriggerAttribute
在默认情况下创建一个新的 TriggerAction 时 Expression Blend 将其关联 MouseLeftButtonDown 事件,如果 MouseLeftButtonDown 不可用的时候,它会在 Loaded 事件中. 但有时您可能希望与不同的默认事件作自定义操作。 那么您应使用 DefaultTriggerAttibute:
1: [DefaultTrigger(typeof(UIElement), typeof(EventTrigger), "MouseLeftButtonDown")]
2: [DefaultTrigger(typeof(ButtonBase), typeof(EventTrigger), "Click")]
3: public class WaveAction : TriggerAction<UIElement> { }
如果把 WaveAction 拖放到一个 Button上,操作将创建一个 单击 EventTrigger。 但是,如果把 WaveAction 拖放到任何其他 UIElement 上,将会创建 MouseLeftButtonDown 触发器。
9.使用 TypeConstraintAttribute
您可以使用 TypeConstraintAttribute 指定对 TargetedTriggerAction 及 EventTriggerBase AssociatedObject的类型约束。
结束语
添加 Silverlight 3 中的行为的目的有两个。 首先,行为是因为在 Silverlight 中缺少触发器的解决方法。 它们允许减少 WPF 和 Silverlight 之间的差异。 第二,它们允许无需编写任何代码就可以添加交互性设计。 我喜欢行为,肯定会使用它们在我未来的任何项目 这篇文章介绍了一些基础。 我希望对您有所帮助。 Expression Blend 的 官方站点 中,您可以找到更多可供使用的许多特效。 也请参阅 参考资料 部分的详细信息。
欢迎您参与更多关于此话题的讨论,本文原创地址为:http://funsl.com admin博客