前言
这段时间的开发不会用到Blend,到年底才会大量用到,本来打算到时候在写Blend相关的笔记,不过看到一些朋友还比较感兴趣,所以这里提前整理了一下。
首先,我希望你记住下面几点:
1、 Blend并不完全是为Designer设计的,玩得最好的一定是Programmer
2、必须了解Behavior,Blend很多特性基于Behavior
Expression Blend 的技术发展历史
2007
Blend的第一个版本就可以进行基本的动画设计。它通过提供一个Object and Timeine面板来进行动画的设计,这和Flash有些类似。在程序中,一段动画就是一个Timeline对象。
那么TimeLine对象是怎样实现动画效果的呢?我们首先看一下抽象类TimeLine的定义:
{
public abstract class Timeline : DependencyObject
{
public bool AutoReverse { get ; set ; }
public TimeSpan ? BeginTime { get ; set ; }
public Duration Duration { get ; set ; }
public FillBehavior FillBehavior { get ; set ; }
public RepeatBehavior RepeatBehavior { get ; set ; }
public double SpeedRatio { get ; set ; }
public event EventHandler Completed;
}
}
我们查看MS文档的描述:时间线表示时间段。它提供的属性可以让您指定该时间段的长度(Duration),开始时间(BeginTime),重复次数(RepeatBehavior),该时间段内时间进度的快慢(SpeedRatio)等等。从TimeLine派生的类可以提供动画功能(例如DoubleAnimation,ColorAnimation等)。一些时间线类:
System.Windows..::..DependencyObject
System.Windows.Media.Animation.Timeline
System.Windows.Media.Animation.ColorAnimation
System.Windows.Media.Animation.ColorAnimationUsingKeyFrames
System.Windows.Media.Animation.DoubleAnimation
System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames
System.Windows.Media.Animation.ObjectAnimationUsingKeyFrames
System.Windows.Media.Animation.PointAnimation
System.Windows.Media.Animation.PointAnimationUsingKeyFrames
System.Windows.Media.Animation.Storyboard
{
public sealed class Storyboard : Timeline
{
public static readonly DependencyProperty TargetNameProperty;
public static readonly DependencyProperty TargetPropertyProperty;
public TimelineCollection Children { get ; }
public void Begin();
public void Pause();
public void Resume();
public void Stop();
}
}
这里看虽然StoryBoard同样继承与TimeLine,但却比其他兄弟类多了一些属性:Begin,Pause,Resume,Stop,还有一个TimeLine的集合属性,以及两个附加属性TargetName和TargetProperty。
原来,StoryBoard才是真正和UI线程打交道的类,它的原理大概就是这样:一个StoryBoard对象包含一些TimeLine元素,每个TimeLine对象如DoubleAnimation定义动画,并用StoryBoard的两个附加属性来描述作用于哪个元素的哪个属性,当调用StoryBoard对象的Begin()方法时,UI线程会根据TimeLine的描述,找到对应元素的属性,时间线内不断修改其属性值,这样就实现了动画。
值得注意的是,StoryBoard本身也是继承自TimeLine。有人不明白,StoryBoard主要用于实现动画而不是定义动画,为什么它也要继承自TimeLine呢?其实这是一种很好的设计模式(后面会写一篇架构和设计模式的文章,标题暂定《谁是架构师?》),从某种程度上我们可以把StoryBoard视为一些特定动画的集合,UI线程将这一些动画一起执行,然而我们可以把这个动画集合也视为一个特定的动画,所以StoryBoard也继承自TimeLine,这样你可以把一个StoryBoard对象作为另一个StoryBoard对象的子元素。
Blend的Objects and TimeLine面板实现的就是上述功能。在面板上点击新建就会在XAML中新增一个StoryBoard对象,选择不同的时间之后没改变一个元素的某个属性,都会增加对应属性类型的TimeLine对象,这就实现了用Blend对动画的编辑。
然而一个控件通常有许多状态,在不同的状态下会有不同的动画组,在第一版本中Blend几乎是无能为力的,所幸的是微软的Silverlight以及Blend的技术发展非常快!
2008
如前面描述,一个控件有非常复杂的状态,如果全靠这样一个一个修改属性值,软件开发将会变得非常痛苦而低效。在Blend SP1中,Blend Team革命性的提出了VSM(Visual State Manager)试图状态管理器,一个控件可以有很多状态,一个状态到另一个状态有很多属性值需要发生改变,这样就需要启动一个StoryBoard过渡不同的状态。而VSM则管理不同的状态,所以他们有以下关系:
给自己的控件添加状态非常简单,在Blend中有一个State面板:
状态分为状态组VisualStateGroup以及状态VisualState,一个VisualStateGroup包含多个VisualState,其理解的很好的一个例子是看Button控件:
CommonStates(VisualStateGroup)
Normal
MouseOver
Pressed
Disabled
FocusStates(VisualStateGroup)
Unfocused
Fouces
状态统一由VisualStateManager管理,VisualManager类有一个附加属性VisualStateManager.VisualStateGroups,我们看Button默认模板的定义:
< Grid >
< VisualStateManager.VisualStateGroups >
< VisualStateGroup x:Name ="CommonStates" >
< VisualStateGroup.Transitions >
< VisualTransition To ="MouseOver" GeneratedDuration ="0:0:0.5" />
</ VisualStateGroup.Transitions >
< VisualState x:Name ="Normal" />
< VisualState x:Name ="MouseOver" >
< Storyboard >
< DoubleAnimation Duration ="0" To ="1"
Storyboard.TargetProperty ="Opacity"
Storyboard.TargetName ="BackgroundAnimation" />
< ColorAnimation Duration ="0" To ="#F2FFFFFF"
Storyboard.TargetProperty ="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)"
Storyboard.TargetName ="BackgroundGradient" />
< ColorAnimation Duration ="0" To ="#CCFFFFFF"
Storyboard.TargetProperty ="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)"
Storyboard.TargetName ="BackgroundGradient" />
< ColorAnimation Duration ="0" To ="#7FFFFFFF"
Storyboard.TargetProperty ="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)"
Storyboard.TargetName ="BackgroundGradient" />
</ Storyboard >
</ VisualState >
.......
其中VisualStateGroup还包含一个Transitions属性,Transitions是VisualTransition的集合,VisualTransition表示控件从一个状态过渡到另一个状态时发生的可视行为,其包含一个StoryBoard对象,当控件在VisualStateGroup中定义的各种状态之间过渡时将应用Transitions中的VisualTransition对象。
通过上面的分析我们不难分析出VSM的整个逻辑:
附加属性VisualStateManager.VisualStateGroups可以被放置于任何控件;可以通过VisualStateGroup来设置控件的状态组,每个状态组之间的过渡可视行为通过设置VisualStateGroup.Transitions来实现,Transitions实际上就是一些StoryBoard的集合,可以在定义各个子控件的属性改变;每个VisualStateGroup包含一些VisualState,VisualState包含StoryBoard属性,当过渡到这个VisualState时会触发这段动画。
状态之间的过渡控制我们可以通过VisualStateManager.GotoState()方法来控制。所以,VSM能让你实现很复杂的动画处理,可以说有了VSM之后,Blend才开始变得强大。
2009
Blend的强大,得益于两个东西:第一是VSM,第二是Behavior。2009年,Blend进行了4个方面的增强:
EasingFunction
GoToStateBehavior
FluidMoveBehavior
FluidLayout
EasingFunction(缓动函数)是个很重要的增强。以前的StoryBoard在运行时,只能根据属性的初始值和结束值进行线性改变,这样的动画有时候不够生动,比如我们可能希望在前半段时间内改变快一点,或者在某个时间段回退一下,这些都可以通过EasingFunction来实现。它本质上根据一个二次方程决定属性值的渐变,这使得我们的动画生动得多。
每一个TimeLine的实现类几乎都定义了EasingFunction属性,微软也提供了一些基本的EasingFunction,如:
-
BackEase:在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动。
-
BounceEase:创建弹回效果。
-
CircleEase:创建使用循环函数加速和/或减速的动画。
-
CubicEase:创建使用公式 f(t) = t3 加速和/或减速的动画。
-
ElasticEase:创建表示弹簧在停止前来回振荡的动画。
-
ExponentialEase:创建使用指数公式加速和/或减速的动画。
-
PowerEase:创建使用公式 f(t) = tp(其中,p 等于 Power 属性)加速和/或减速的动画。
-
QuadraticEase:创建使用公式 f(t) = t2 加速和/或减速的动画。
-
QuarticEase:创建使用公式 f(t) = t4 加速和/或减速的动画。
-
QuinticEase:创建使用公式 f(t) = t5 加速和/或减速的动画。
-
SineEase:创建使用正弦公式加速和/或减速的动画。
我们看一个例子:
Storyboard.TargetName ="myRectangle"
Storyboard.TargetProperty ="Height" >
< DoubleAnimation.EasingFunction >
< BounceEase Bounces ="2" EasingMode ="EaseOut" Bounciness ="2" />
</ DoubleAnimation.EasingFunction >
</ DoubleAnimation >
在Blend中设置EasingFunction的图示:
GotoStateBehavior是一个Behavior,它用于控制我们前面VSM描述的状态的转换,在Asset面板的Behaviors中,即可以看到GotoStateBehavior控件,可以将其选择拖动到任何一个控件,即可利用Blend实现状态转换的逻辑。在Properties面板进行设置,设置触发事件,要转换的状态等:
如我们拖动到Button控件上,会产生如下代码:
< i:Interaction.Triggers >
< i:EventTrigger EventName ="Click" >
< ei:GoToStateAction StateName ="VisualState" />
</ i:EventTrigger >
</ i:Interaction.Triggers >
</ Button >
GoToStateAction本身是继承自IAttachedObject,这和我们前面讲的Behavior的原理是一样,这也是Blend利用Behavior的一个例子。
FluidMoveBehavior:StoryBoard能够做到属性值改变过程的过渡。然而还有一种场景,那就是Silverlight中控件可以随着界面大小的调整而重新布局,这是通过控件的MeasureOverride和ArrangeOverride方法来实现。一般情况下,到界面重新布局时,控件瞬间被安排到新的位置,然而有时候我们希望看到这个重新排列的过程。
我们可以自己写逻辑实现这一功能,但是非常复杂。Behavior的用途是什么,就是重用,所以Blend小组开发了这个Behavior。
FluidMoveBehavior也位于Asset面板中的Behaviors部分,拖动FluidMoveBehavior到一个WPF WarpPanel控件,配置的属性即可: 可以设置AppliesTo属性为Children,这一当Grid的尺寸大小发生改变时,可以看到起内部元素重新排列的过程,你还可以设置缓动函数来设置这段动画的变化效果。如下图:
最后要介绍的是FluidLayout,它通常用于在两种状态之间过渡的时候,控件属性的更改没有应用动画效果,而又希望平滑过渡的效果。例如,当控件从一种状态变为另一种状态时,Grid.Row有1变为2,并且Grid.Rowspan由1变为2,这一只要开启FluidLayout就能看到变换过程。你可以在States面板中设置FluidLayout:
很简单,只要点击"Turn on FluidLayout"就能开启状态之间的过渡效果,当然同样可以设置缓动函数EasingFunction。
2010
.......
暂停
2010 Blend 4发布,这自然包含一些新特性,由于时间及篇幅关系,我打算到下一篇续写Blend 4的一些功能。
Blend 4基本上没有新增知识点,主要都是围绕Behavior提供的一些很酷的功能。
所以,对于Blend,只要掌握了VSM和Behavior,我想其他的都不是问题了。
预告后面的内容是,一个Blend Team据说花了4年时间研究的功能 FluidMoveTagSetBehavior,这是一个很有意思的特性。相信你和我一样会非常感兴趣,还有PathListBox,它能实现更酷的效果,当然还有其他东东,到时候再揭晓了。
Blend并不难,而且我们看到它并不是为Designer设计的,它的许多概念都需要Programer的思维。所以其实我们是很容易去学习和利用的。多摸几次就熟悉了。
这里主要是讲理论的,后面可能会再写一篇操作的,看时间了,相信有了理论之后,再看一面操作,就可以了。
这里打一个广告,笔者现在辞职全职创业,手头有一个计划是和Silverlight以及Windows Azure有关的,我的QQ群(6183299)会大力分享这些方面的技术,包括ASP.NET,Silverlight,Windows Azure,WCF,Expression Blend,WCF RIA Service等等新技术,因为这些都是我们会用到的技术,希望在交流学习中能找到志同道合者一起创业。欢迎加入...