Trigger是XAML中另一个非常有趣的特性,它就像是在XAML中定义了一个if语句。但是随着后期微软XAML框架的演变,Trigger却逐渐被弱化,让我们先看看Trigger在当前最新XAML框架中的支持状况:
WPF 4.5 | Silverlight 5/WP8 | WinRT(Windows 8) | |
FrameworkElement.Triggers (注意这里只支持EventTrigger) | 支持 | 支持 | 支持 |
Style.Triggers | 支持 | 不支持 | 不支持 |
DataTempate.Triggers | 支持 | 不支持 | 不支持 |
ControlTemplate.Triggers | 支持 | 不支持 | 不支持 |
Trigger类型 | 支持 | 不支持 | 不支持 |
EventTrigger类型 | 支持 | 支持 | 支持 |
DataTrigger类型 | 支持 | 不支持 | 不支持 |
TriggerBase.Enter/ExitActions | 支持 | 不支持 | 不支持 |
BeginStoryboard TriggerAction | 支持 | 支持 | 支持 |
看到了吧,Trigger在后期微软XAML框架中已经被大大削减,说实话只有WPF才能算上是支持Trigger的,而Silverlight/WinRT也就仅仅是在FrameworkElement的Trigges属性中支持BeginStoryboard这个TriggerAction罢了。
注意:
Microsoft Expression Blend SDK可以提供额外的对Trigger相关类型的支持。比如众所周知的System.Windows.Interactivity.dll。
2. 为什么是Trigger?
OK,那么为什么Trigger在后期XAML框架混的这么惨呢?
我个人认为两点:
1:Trigger本身不是100%必须的,而后期的XAML框架显然很重视性能和体积,因此某些不是100%需要的东西就可能被剪掉。
比如模板中的Trigger可以通过定义Converter甚至是DataTemplateSelector代替,而Style中的Trigger通常比较少见,即便有需求也可以直接写在背后控件属性逻辑上,可以不放在XAML中。
2:Trigger多应用在动画定义上,在这一点上。从上面可以看到,EventTrigger和BeginStoryboard都被现有XAML框架所支持。不过在控件模板这种对动画定义需求比较高的情况下,BeginStoryboard很力不从心,而ViewStateManager在这里则完胜Trigger。
3. ViewStateManager的支持
注意:
XAML中的ViewState和ASP.NET中的ViewState概念不一样,ASP.NET中的ViewState主要是指数据在无状态协议中的保留问题。而XAML中的ViewState指控件在不同状态下的外表定义。
ViewStateManager只有在某些旧的XAML框架中不支持,新的XAML框架都是很好地支持它的。
不支持ViewStateManager的XAML框架:
WPF 3.0
WPF 3.5
Silverlight 1
Silverlight 2
支持ViewStateManager的XAML框架:
WPF 4 +
Silverlight 3 +
Windows Phone 7.0 +
WinRT
可以看到,VisualStateManager基本上已经是任何XAML框架定义控件模板的标准了。
4. Trigger和ViewStateManager的具体比较
接下来我们就来直接来比较一下Trigger和ViewStateManager的风格。我们就用最常见的操作:“控件模板”定义做比较。这也是他们的直接冲突领域。现在我们就定义一个最简单的Button的控件模板,具体规格如下:
1. 颜色默认是蓝色,鼠标指向时变成绿色,点击后变成灰色。
2. 要求点击后动画时长0.1秒,其他统一0.5秒。
注意:
下方代码统一以WPF环境做示例。因为WPF是唯一的既完全支持Trigger也支持ViewStateManager的XAML框架。
使用ViewStateManager方式:
1. 定义ViewStateGroup。
2. 定义具体ViewState
3. 添加Storyboard
4. 需要的话,定义Transition。
XAML:
< Button.Template>
< ControlTemplate TargetType= "Button">
< Border Name= "border"
Background= "LightBlue">
< ContentPresenter Content= "{TemplateBinding Content}" />
< VisualStateManager.VisualStateGroups>
<!-- 普通状态 -->
< VisualStateGroup x:Name= "CommonStates">
<!-- 定义时常 -->
< VisualStateGroup.Transitions>
< VisualTransition GeneratedDuration= "0:0:0.5" />
< VisualTransition GeneratedDuration= "0:0:0.1" To= "Pressed" />
< /VisualStateGroup.Transitions>
<!-- 定义三种状态的Storyboard -->
< VisualState x:Name= "Normal" />
< VisualState x:Name= "MouseOver">
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
Duration= "0"
To= "Green" />
< /Storyboard>
< /VisualState>
< VisualState x:Name= "Pressed">
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
Duration= "0"
To= "Gray" />
< /Storyboard>
< /VisualState>
< /VisualStateGroup>
< /VisualStateManager.VisualStateGroups>
< /Border>
< /ControlTemplate>
< /Button.Template>
< /Button>
而使用Trigger的方式:
注意:
尽管WPF对Trigger和ViewStateManager都完全支持。但是在WPF 4后,还是推荐使用ViewStateManager的方式定义模板。
使用Trigger定义,我们不得不面对如下麻烦:
1. 选择Trigger类型和对应属性,比如我们使用普通Trigger对象,和Button的IsPressed属性和IsMouseOver来手动判断状态。
2. 根据Trigger类型执行操作,如果使用Trigger.Setters,是具有自动还原功能的,如果使用EnterActions,如果想进行还原操作,必须设置ExitActions。对于EventTrigger,只能使用Actions属性。
3. 使用BeginStoryboard执行Storyboard,这里没有VisualStateManager模式中的GeneratedDuration,一切都得自己定义。
XAML:
< Button.Template>
< ControlTemplate TargetType= "Button">
< Border Name= "border" Background= "LightBlue">
< ContentPresenter/>
< /Border>
<!-- 定义Trigger -->
< ControlTemplate.Triggers>
<!-- 鼠标进入的设置和还原动画 -->
< Trigger Property= "IsMouseOver" Value= "True">
< Trigger.EnterActions>
< BeginStoryboard>
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
To= "Green"
Duration= "0:0:0.5" />
< /Storyboard>
< /BeginStoryboard>
< /Trigger.EnterActions>
< Trigger.ExitActions>
< BeginStoryboard>
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
Duration= "0:0:0.5" />
< /Storyboard>
< /BeginStoryboard>
< /Trigger.ExitActions>
< /Trigger>
<!-- 鼠标按下的设置和还原动画 -->
< Trigger Property= "IsPressed" Value= "True">
< Trigger.EnterActions>
< BeginStoryboard>
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
To= "Gray"
Duration= "0:0:0.1" />
< /Storyboard>
< /BeginStoryboard>
< /Trigger.EnterActions>
< Trigger.ExitActions>
< BeginStoryboard>
< Storyboard>
< ColorAnimation Storyboard.TargetName= "border"
Storyboard.TargetProperty= "Background.Color"
Duration= "0:0:0.5" />
< /Storyboard>
< /BeginStoryboard>
< /Trigger.ExitActions>
< /Trigger>
< /ControlTemplate.Triggers>
< /ControlTemplate>
< /Button.Template>
< /Button>
结果很明显,使用Trigger和ViewStateManager相比,Trigger的方式更原始,可能会依靠多种Trigger的多种执行方式来完成设置操作,动画定义较繁琐。而ViewStateManager,则是为了克服如上缺点而生。
当然,ViewStateManager也有其不好的一面,有些时候,特别是不需要动画的时候,ViewStateManager只能还是使用动画的模式硬生生地进行非动画操作。比如我们创建一个简单的CheckBox的控件模板。当选中后显示“真”,未选时显示“假”。
使用ViewStateManager方式。我们还得定义Storyboard,只不过是用ObjectAnimationUsingKeyFrames。XAML:
< CheckBox.Template>
< ControlTemplate TargetType= "CheckBox">
< Border>
< TextBlock Text= "真" Name= "textBlock" />
< VisualStateManager.VisualStateGroups>
< VisualStateGroup Name= "CheckStates">
< VisualState Name= "Checked" />
< VisualState Name= "Unchecked">
< Storyboard>
< ObjectAnimationUsingKeyFrames Duration= "0"
Storyboard.TargetName= "textBlock"
Storyboard.TargetProperty= "Text">
< DiscreteObjectKeyFrame KeyTime= "0"
Value= "假" />
< /ObjectAnimationUsingKeyFrames>
< /Storyboard>
< /VisualState>
< /VisualStateGroup>
< /VisualStateManager.VisualStateGroups>
< /Border>
< /ControlTemplate>
< /CheckBox.Template>
< /CheckBox>
这个时候,我们就该想念Trigger,一个再简单不过的Trigger和一个Setter就实现了上述功能,既简单可读性还高,XAML:
< CheckBox.Template>
< ControlTemplate TargetType= "CheckBox">
< Border>
< TextBlock Text= "真" Name= "textBlock" />
< /Border>
< ControlTemplate.Triggers>
< Trigger Property= "IsChecked" Value= "False">
< Setter TargetName= "textBlock"
Property= "Text"
Value= "假" />
< /Trigger>
< /ControlTemplate.Triggers>
< /ControlTemplate>
< /CheckBox.Template>
< /CheckBox>
当然,多数情况下VisualStateManager还是完胜Trigger方式的,毕竟有动画的控件模板更受欢迎!不过如果Trigger被加入到Silverlight和WinRT中也不错,因为在某些时候,开发者又有一种新的选择!
本文来自刘圆圆的博客,原文地址:http://www.cnblogs.com/mgen/archive/2012/12/07/2807821.html