“流畅 UI”这个词最近常被用来形容一种 UI 设计技术,这种技术能够避免 让可视化对象突然进入视野或者从一个位置跳到另一个位置。流畅的可视化对象 在进入视野和变换位置时更加优雅,有时就像从雾中浮现或者滑入视野。
我在本专栏的前两篇文章中介绍过一些您自己实现流畅 UI 的技术,当时的 部分灵感就来源于 Silverlight 4 中即将推出的流畅 UI 功能。现在, Silverlight 4 已经正式发布,本文就为您介绍其功能。Silverlight 4 中流畅 UI 的使用范围很窄,只用于加载和卸载 ListBox 中的项,但是却能给我们一些 重要的启发,告诉我们如何在自己的实现中扩展流畅 UI 技术。Expression Blend 4 中具备更多的流畅 UI 行为。
模板和 VSM
如果您不知道新的流畅 UI 功能究竟在 Silverlight 4 的什么地方,您可能 需要花几个小时去寻找。它不是类;不是属性;不是方法;也不是事件。实际上 ,它是 ListBoxItem 类上的三个新的视觉状态。图 1 显示了这个类的文档,其 中的 TemplateVisualState 属性项进行了微调,以符合组的名称。
图 1 ListBoxItem 类文档
[TemplateVisualStateAttribute(Name = "Normal", GroupName =
"CommonStates")]
[TemplateVisualStateAttribute(Name = "MouseOver", GroupName =
"CommonStates")]
[TemplateVisualStateAttribute(Name = "Disabled", GroupName =
"CommonStates")]
[TemplateVisualStateAttribute(Name = "Unselected", GroupName =
"SelectionStates")]
[TemplateVisualStateAttribute(Name = "Selected", GroupName =
"SelectionStates")]
[TemplateVisualStateAttribute(Name = "SelectedUnfocused", GroupName =
"SelectionStates")]
[TemplateVisualStateAttribute(Name = "Unfocused", GroupName =
"FocusStates")]
[TemplateVisualStateAttribute(Name = "Focused", GroupName =
"FocusStates")]
[TemplateVisualStateAttribute(Name = "BeforeLoaded", GroupName =
"LayoutStates")]
[TemplateVisualStateAttribute(Name = "AfterLoaded", GroupName =
"LayoutStates")]
[TemplateVisualStateAttribute(Name = "BeforeUnloaded", GroupName =
"LayoutStates")]
public class ListBoxItem : ContentControl
视觉状态管理器 (VSM) 是 Silverlight 中最重要的更改之一,它改编自 Windows Presentation Foundation。在 WPF 中,样式或模板(几乎总是用 XAML 定义)可以包含名为触发器 的元素。这些触发器被定义为检测属性更改或 检测事件,然后启动一段动画或更改另一个属性。
例如,一个控件的样式定义可以包含一个针对 IsMouseOver 属性的触发器, 当该属性为 True 时,触发器将控件的背景设置为蓝色画笔。也可以定义针对 MouseEnter 和 MouseLeave 事件的触发器,当这些事件发生时可以启动几段简 短的动画。
在 Silverlight 中,大部分触发器都被弃用,取而代之的是 VSM。这么做的 部分原因是希望提供更加结构化的方法,以便在运行时动态更改控件的特征;还 有部分原因是避免在定义多个触发器后,处理各种可能的组合。 VSM 被认为是 对触发器的极大改进,因此包含在 Microsoft .NET Framework 4 的 WPF 中。
您从图 1 可以看到,ListBoxItem 控件支持 11 种视觉状态,这些状态被分 为四组。在每一组中,任意时刻只能有一种视觉状态是活动的。这一简单的规则 极大地减少了可能的组合数量。例如,您无需去思考当鼠标悬停在已选定但未聚 焦的项上时,ListBoxItem 该如何显示;而且每个组都可以独立于其他组进行处 理。
ListBoxItem 的代码部分通过调用静态的 VisualStateManager.GoToState 方法,来负责更改视觉状态。ListBoxItem 的控件模板负责响应这些视觉状态。 模板使用一个情节提要来响应特定的视觉状态,该情节提要中包含一个或多个以 可视化树中的元素为目标的动画。如果您希望控件立即响应视觉状态的更改而不 使用动画,只需将动画的持续时间定义为 0 即可。但为什么要这么麻烦呢?可 以像使用动画一样,轻松地让控件的视觉效果更流畅。
支持流畅 UI 的新视觉状态分别是 BeforeLoaded、AfterLoaded 和 BeforeUnloaded,它们都包含在 LayoutStates 组中。通过将动画关联到这些视 觉状态,您可以让 ListBox 中的项在第一次加入 ListBox 时淡入或滑入视野, 而从 ListBox 删除时呈现一些别的效果。