该专利文档的一部分公开内容包含受版权保护的材料。版权所有人不反对任何人对该专利文档或专利公开内容进行复制,按照其在专利和商标局的专利文件或记录中的形式,但版权所有人保留其它所有版权的权利。
发明的详细描述
示范操作环境
图1示出了一合适的计算系统环境100的示例,在该环境中可实现本发明。计算系统环境100只是一合适的计算环境的一例,而不是要提出对本发明使用范围或功能性进行任何限制。计算环境100也不应解释成对于在示范操作环境100中所示出的任一组件或其组合有任何依赖或要求。
本发明用多个其它通用或专用计算系统环境或配置是可运行的。可以适用于该本发明的公知的计算系统、环境、和/或配置的示例包括,但不局限于,个人计算机、服务器计算机、手持设备或膝上型设备、写字板设备、多处理器系统、基于微处理器的系统、机顶盒、可编程消费者电子设备、网络PC、小型机、大型计算机、包括任何诸如以上系统或设备的分布式计算环境等。
本发明可以在正由计算机执行的诸如程序模块的计算机可执行指令的一般上下文中被描述。一般地,程序模块包括完成特殊任务或执行特殊抽象数据类型的例行程序、程序、对象、组件、数据结构等。本发明也可以在分布的计算环境中实践,在此由通过通信网络而连接的远程处理设备来执行任务。在分布式计算环境中,程序模块可以定位于包括记忆体储存设备的本地和远程计算机存储介质。
参见图1,对于实现本发明的示范的系统包括一以计算机110形式出现的通用计算设备。计算机110的组件包括,但不局限于,处理单元120、系统存储器130、以及将包括该系统存储器在内的各种系统组件耦合至处理单元120的系统总线121。上述系统总线121可以是几种总线结构类型中的任何一种,包括存储总线或存储控制器、外围总线和使用各种总线体系结构的任一种的局域总线。举例来说,而非限制,此类体系结构包括工业标准体系结构(ISA)总线、微通道体系结构(MCA)总线、增强型ISA(EISA)总线、视频电子标准技术协会(VESA)局域总线、加速图形端口(AGP)总线和也被称为Mezzanine总线的外围部件互连(PCI)总线。
计算机110通常包括各种计算机可读介质。计算机可读介质可以是任何计算机110能够访问的可用介质,包括易失性的和非易失性的介质、可移动的和不可移动的介质。举例来说,而非限制,计算机可读介质可以包含计算机存储介质和通信介质。计算机存储介质包括能以任何方法或技术实现的易失性的和非易失性的、可移动的和不可移动的介质,用于存储诸如计算机可读指令、数据结构、程序模块或其它数据等信息。计算机存储介质包括,但不局限于,RAM、ROM、EEPROM、闪存或其它存储技术,CD-ROM、数字化多功能光盘(DVD)或其它光盘存储、盒式磁带、磁带、磁盘存储器或其它磁存储设备,或任何其它可以被用来存储想要的信息并且可以被计算机110访问的介质。通信介质通常具体体现了诸如载波或其它传送机制的已调制数据信号中的计算机可读指令、数据结构、程序模块或其它数据,也包括任何信息传递介质。术语“已调制数据信号”是指在该信号中以编码信息的方式来设置或改变其一个或多个特征的信号。举例来说,而非限制,通信介质包括诸如有线网或直线连接的有线介质,和诸如声音、射频、红外线和其它无线介质的无线介质。任何以上所述的组合也应该包括在计算机可读介质的范围之内。
系统存储器130包括以诸如只读存储器(ROM)131和随机存取存储器(RAM)132的易失性和/或非易失性存储器的形式的计算机存储介质。包含如在启动期间帮助在计算机110内各元件之间传送信息的基本例行程序的基本输入/输出系统133(BIOS),通常存储在ROM 131中。RAM 132通常包含可以被处理单元120立即访问和/或当前操作的数据和/或程序模块。举例来说,而非限制,图1示出了操作系统134、应用程序135、其它程序模块136和程序数据137。
计算机110还可以包括其它可移动/不可移动、易失性/非易失性的计算机存储介质。仅作为示例,图1示出了从不可移动、非易失性磁性介质读出或写入不可移动、非易失性磁性介质的硬盘驱动器141、从可移动、非易失性磁盘152读出或写入可移动、非易失性磁盘152的磁盘驱动器151、以及从诸如CDROM或其它光学介质的可移动、非易失性光盘156读出或写入可移动、非易失性光盘156的光盘驱动器155。其它可以使用在示例的操作环境中的可移动/不可移动、易失性/非易失性计算机存储介质包括,但不局限于,盒式磁带、闪存卡、数字多功能光盘、数字录像带、固态RAM、固态ROM等。硬盘驱动器141通常通过诸如接口140的不可移动存储接口连接到系统总线121,磁盘驱动器151和光盘驱动器155通常通过诸如接口150的可移动存储接口连接到系统总线121。
以上讨论并且在图1中示出的驱动器及其它们相关的计算机存储介质为计算机110提供了计算机可读指令、数据结构、程序模块和其它数据的存储。在图1中,例如,示出硬盘驱动器141存储操作系统144、应用程序145、其它程序模块146、和程序数据147。需要注意的是这些组件可以和操作系统134、应用程序135、其它程序模块136和程序数据137相同,也可以和它们不同。在此对操作系统144、应用程序145、其它程序模块146和程序数据147给出了不同的标号来说明至少它们是不同的副本。用户可以通过诸如写字板(电子数字转换器)164、麦克风163、键盘162和定位设备161的输入设备把指令和信息输入到计算机110中,定位设备161通常指如鼠标、跟踪球或触摸板。其它输入设备(图1中未示)可以包括操纵杆、游戏垫、圆盘式卫星天线、扫描仪等等。这些和其它输入设备通常由用户输入接口160连接到处理单元120,上述输入接口160和系统总线121相耦合。但是上述和其它输入设备也可以由其它接口和总线结构连接到处理单元120,诸如,并行端口、游戏端口或通用串行总线(USB)。监视器191或其它类型显示设备也可以通过诸如视频接口190的接口连接到系统总线121。监视器191也可以和触摸屏画板193等集成在一起。该触摸屏面板193等可以经由接口(例如触摸屏接口192)将诸如笔迹的数字化输入到计算机系统110。需要注意的是监视器和/或触摸屏画板可以物理地耦合到包含了计算机110,诸如写字板式个人计算机的一外壳,其中触摸屏面板193实质上用为写字板164。另外,计算机(例如,计算设备110)也包括其它外围输出设备,诸如可以通过一输出外围设备接口194等连接的扬声器195和打印机196。
计算机110可以在网络化的环境中运行,该环境使用与一台或多台诸如远程计算机180的远程计算机的逻辑连接。远程计算机180可以是个人计算机、服务器、路由器、网络个人计算机、对等设备或其它共用网络节点,通常包括以上描述的和计算机110相关的多个或全部元件,尽管在图1中只示出了记忆存储设备181。在图1中描绘的逻辑连接包括局域网(LAN)171和广域网(WAN)173,但是也可以包括其它网络。这样的网络环境在办公室、企业范围的计算机网络、内联网和因特网中是普遍的。
当在LAN网络环境中使用时,计算机110通过网络接口或适配器170连接到LAN171。当在WAN网络环境中使用时,计算机110通常包括调制解调器172或通过诸如因特网的WAN173建立通信的其他装置。调制解调器172可以是内置的或外置的,可以通过用户输入接口160或其它适当的机制连接到系统总线121。在一网络化的环境中,所描述的和计算机110相关的程序模块或其中的各个部分可以存储在远程记忆体存储设备内。举例说明,但非限制,图1示出了驻留在存储设备181上的远程应用程序185。可以理解的是所示的网络连接是示例的,也可以使用在计算机间建立通信链路的其他装置。
示例架构层
本发明的一方面一般涉及在计算机系统上提供流畅、复杂的动画和/或媒体。为此,如图2中所一般表现的,提供媒体整合层架构200。应用程序、控制或其它类似的较高级程序代码(例如,操作系统组件的用户接口)202通过一组应用编程接口(API)204等访问媒体整合层架构200以访问(读或写)图形信息。需要注意的是虽然在此描述的例子中的多个例子涉及与API连接的应用程序,但可以理解的是其它较高级程序代码和组件(例如,操作系统的用户接口)也可用以与在此描述的较低级组件连接口。如此,任何涉及这样的较高级程序代码,不管是否涉及应用程序、用户接口等,都应该被同等考虑。
在一实现中,媒体整合层架构200包括高级合成和动画引擎206、定时和动画组件208、以及低级合成和动画引擎210。如在此使用的,术语“高级”和“低级”类似于那些在其它计算情况中使用的,其中一般地,相对于较高级组件而言越低级的软件组件是越靠近硬件的组件。如此,例如,发送自高级合成和动画引擎206的图形信息可在低级合成和动画引擎210处被接收,在210处信息被用以发送图形数据至包括硬件的图形子系统。
一般地,高级合成和动画引擎(在此也称为高级合成器和动画制作者或高级引擎或组件)206构建显示元素树以表现由应用程序202提供的图形场景,而定时和动画组件提供说明性的(或其它)动画和定时控制。低级合成和动画引擎(在此也称为低级合成器和动画制作者或低级引擎或组件)210组成用以多个应用场景的呈现,并利用呈现组件来实现对于屏幕真实的图形呈现。注意,在较高层上依然可能做消耗时间的或特定应用的呈现,并发送涉及位图等至较低层。
高级合成和动画引擎206构建元素树结构并遍历该结构,创建被发送至低级合成和动画引擎210的呈现指令和简单动画时间间隔。由高级合成器生成的呈现指令可包括定时和动画信息。低级合成和动画引擎210采用呈现指令和动画时间间隔并管理以后提供给图形子系统(举例来说,图形软件和硬件)212的场景的动画制作、呈现和编写。除了本地显示的输出以外,高级合成和动画引擎206(或此外一类似的)可以合适的格式提供呈现和动画指令给较低级打印代码220以发送固定的图形数据至打印机222等,和/或可以合适的格式提供呈现指令和简单动画时间间隔给较低级终端传送服务器226以传送至远程机器228。注意大量的信息也可通过网络发送,举例来说,在没有任何网络流通量的情况下,具有本地的远程机器处理鼠标滚动影响是理想的。
在该实现中,媒体整合层架构200把图形处理分割成多个层次,并且这些层次中的每个层次执行某些智能图形处理,这些处理一起允许应用程序的用户接口202等输出具有流畅动画的图形、合成这些图形和其它应用图形、以及与视频帧一起处理。动画和/或合成也可和音频输出同步。例如,通过同步音频与在低级组件上的帧速率,音频的定时实质上可与视频或图形的定时一致,并且不依赖于任务调度、复杂预处理的能力以保持刷新速率。
图3表现了一实现,其中诸如基于XAML的代码的标记代码302可由句法分析器/翻译器304解释。一般地,句法分析器/翻译器304添加元素至元素树/特性系统314;元素是实行自己布局的可视对象。此外,注意标记代码的某些或所有可被按需编译而不是解释,从而提高效率。
一般地,元素是在参与特性系统、触发和布局/表现系统的元素层内的对象。句法分析器304查找标记并且确定是否那些标记帮助定义元素或资源对象。在VisualBrush的特殊情况中,例如,相同的标记可被解释成元素或也被解释成资源对象,这取决于那些标记出现地方的上下文,例如,如在美国专利申请序号为10/401,717中描述的那样,取决于是否在复杂特性句法中出现。
除了在标记内的内联表现以外,资源实例也可定位在其它任何位置(例如,在标记内或文件内,该文件可以是在本地或在远程网络上并能适当地下载),并能由名称所引用,(例如,文本名称、引用或其它合适的标识符)。通过这种方式,场景设计者可在整个场景内重用元素树中的元素,包括由复杂特性句法描述的元素。
句法分析器304通过按必要访问类型转换器308来处理在复杂特性句法内的标记,并且也通过把特定的参数与对象特性匹配来处理在复杂特性句法内的标记,从而为场景设计者处理复杂性。如此,句法分析器304不仅仅建立对象,也设置对象上的特性。由于相同的呈现模型在元素层次和API层次之间共享,对象中的多个对象实质上是相同的。这使得句法分析器/翻译具有高效率,并且也允许不同类型的编程语言(例如,类似C#等语言)可容易地从标记转换成自己的句法,并且反之亦然。注意的是如图3中所表现的,另一个这样的编程语言310(可包括编译过的标记)能添加元素至元素树208,或能直接与可视API层316连接。
还如图3中所表现的,相同的标记302可被用以在一元素层次和资源层次上编程。一般地,元素层次给场景设计者全部的可编程性、提供继承的特性系统的使用(例如,如特征的式样表)、以及触发(例如,由此元素可具有附加的代码以响应用户输入事件或动作来更改它的外观、位置等)。然而,本发明还提供资源层机制,场景设计者实质上可由此简化元素树以及直接对可视API层编程。对于不需要元素层次特性的许多类型的静态形状、图形等,这提供一种更有效率和轻便的方式输出适当的对象。
为了控制动画和媒体输出,如以下参照图4和图5描述的那样,包括时钟的定时树也被维持。一般地,高级合成器和动画制作者引擎206执行复杂处理(有时指编译),该复杂处理大大简化处理量并且大大减少较低级需要处理的数据量以呈现正确的输入。然而,需要的注意的是,由较高级执行的处理的数量和类型在很大程度上可取决于较低级的装载、配置和能力。例如,如果高能力图形硬件存在,较高级可进行较少数量的处理,反之亦然。高级和低级层被适应于这些因素。
一般地,动画通过高级合成器和动画引擎206以及低级合成器和动画引擎210完成。在一实现中,高级引擎206遍历场景并且用关于稍后的插补的时间间隔更新动画参数,以及把这些简化的数据结构封装至发送到较低级引擎210的指令中。这可以同步和/或异步的方式完成。时间间隔数据可被考虑成包括定时端点(开始和结束定时数据)和用于呈现指令的参数化的值。注意的是高级引擎204可执行请求的插补中的某些插补或所有插补,例如,如果一插补或其它运动函数对于较低级引擎210而言太复杂而不能处理,或较低级不能跟上放置其中的处理需要,较高级引擎可执行计算中的某些计算或所有计算并提供给较低级简化的数据、指令、格局等以实现理想的结果。
在典型的情况下,当较低级执行插补时,对于动画的每个帧,低级引擎210插补参数时间间隔以获得即时值,并解码指令为由图形设备执行的呈现命令。图形设备通过添加任何可在场景中出现的视频帧以合成最终的场景。其它数据也可被添加,诸如由数字化版权管理保护的内容。
如此,高级引擎206遍历场景数据结构、计算描述一段时间内每个动画的参数的时间间隔、以及发送这些时间间隔和简化的参数化的画图指令至低级引擎210。参数数据包括开始时间、结束时间、插补者和插补数据。作为示例,高级合成器和动画引擎206可指示低级合成器和动画引擎210关于图形应该随时间的推移如何改变,例如,开始坐标、结束坐标、图形应该在坐标之间移动的时间量(时间间隔)、以及诸如线性的运动函数(注意运动不是动画所要求的,如固定对象可通过更改例如它的颜色特性而被动画制作),而不是擦除和重画图像以使它看上去移动。低级合成器和动画引擎210将插补以确定帧之间新的位置,把这些转换成图形设备可理解的画图指令,并发送命令至图形设备。高级引擎206的每个发送较好地提供足够数据给低级引擎210以执行随着几个帧的推移的流畅的动画。
低级(例如,快速记号)引擎210是从高级引擎206中分离的任务。低级引擎210从高级引擎206接收简化的参数化的画图指令和描述场景的参数时间间隔。低级引擎维持和遍历这些数据结构直到新的数据结构由高级引擎206提供。低级引擎可服务多个高级引擎206,对于每个高级引擎维持独自的数据结构。在低级引擎210和高级引擎206之间一对多的关系允许系统同步地流畅地动画制作多个场景。
低级引擎210实质上基于高级引擎的提供的时间间隔来插补即时动画参数,更新画图指令和呈现每一帧的场景。低级引擎210任务在系统上以高优先级运行以确保帧准备好显示,诸如在图形硬件屏幕刷新速率。由低级引擎210执行的插补一般因此限制于简单、快速的函数,诸如线性、分段线性、三次样条以及类似速度的那些函数。
关于动画和媒体,一般如图4和图5中显示的,诸如应用程序202的程序结合定时信息(被称为时钟或时钟特性)对高级组件206指定动画特性值。如以下描述的,实质上任何独立动画或媒体(例如,诸如视频和音频的线性媒体)以及协调指定的动画的故事板具有对于其在高级组件上维持的时钟。一般地,作者指定例示至时钟以合适地保持它们同步的时间线数据。
时钟特性包括定义给定的时钟和定时结构的其它部分之间初始同步关系的定时特性。如图5所示,高级组件206可调用高级动画函数520H(例如,以管理的代码书写,该代码以元数据的形式提供信息,该元数据允许通用语言运行时间,其上可运行元数据以控制它的操作)以确定动画特性的当前值。在快速帧速率计算期间,低级组件210调用类似(或相同)的具有由引擎414计算的进程的动画函数520L以确定动画的当前特性值。注意的是在可供选择的实现中,动画函数可构建进例如较低级组件中。
一般地,动画和线性媒体与一组时钟关联,该组时钟通过同步图元和规则相互涉及。时钟可被分级地安排,例如,应用程序具有父时钟,而应用程序的动画的对象是子程序,其依次具有其它子时钟。当时钟的特性被定义或修改时,该时钟的任何子时钟可被影响。例如,暂停父时钟可暂停其子时钟的任何一个,以及加倍父时钟的速度可加倍子时钟的任何一个的速度。
这些时钟特性可由包括由应用程序在运行时间启动的交互的控制事件的资源事件所修改。如此,时钟是交互的,通过这种方式每个时钟可各自地由应用程序在任意时间,例如,响应于用户输入,开始、暂停、继续执行以及停止。此外,新的时钟可被添加至定时结构,并且现有的时钟可被移除。
如在上述美国专利申请10/693,822中描述的,高级定时组件可基于存储的事件表(开始、暂停等)和关联的同步图元对于每个时钟生成时间间隔表。激活时间间隔是描述在真实世界时间内的不同点上给定的时钟表示的时间的直接的、非重叠片断。
如图4中所示的,时钟特性中的至少某些特性可在时钟的定时树402内被分级地关联。三个这样的时钟与它们的特性在图4中示出,即时钟特性4041-4043,然而,可以理解的是在给定的情况下可存在许多更多的时钟和可供选择的定时树。对每个时钟,例如可相对应显示的动画对象(或故事板协调的对象组),高级组件206包括状态机,称为可从时钟特性生产生事件表(例如,4061)的事件表生成器408。一般地,事件表生成器408把由特定的时钟特性初始安排的事件与任何诸如暂停和继续执行接收到的关于动画的请求的清晰的交互事件分组成一个事件表。时钟对于每个动画(或如以下描述的通过故事板的动画组)维持,并且这样就存在相应于每个独立的动画/动画组的事件表以及存在一个对于每个独立线性媒体的事件表。
高级组件包括对于每个动画或媒体使用事件表(例如,4063)的时间间隔生成器410以计算发送至低级组件210的相应的时间间隔表(例如,4123)。依次地,低级组件210包括基于关于时间间隔数据的当前时间来控制输出的低级计算引擎414,该引擎通过诸如基于对于该对象的时间间隔数据和当前时间提供进程值至确定动画对象的变化特性的当前值的低级动画功能子系统520t来控制输出。例如,对于任何给定的帧,低级计算引擎414基于时间间隔数据和当前时间(可以是系统时间或相对时间)来插补动画对象的位置。注意的是如图4和图5中所示,高级定时组件包括定时树402和事件表4061-4063,而低级定时组件210包括时间间隔表4121-4123,然而这些数据结构实质上可维持在存储器,一般在高速随机存取存储器内的任何位置。
总而言之,高级定时组件206查看同步规则和图元所叙述(例如,分级地)的时钟的树402。本发明在高级定时组件206内使用时钟以编译由低级定时引擎消耗的活动时间间隔412表。低级定时引擎查看表现独立(例如,每一动画对象或每一线性媒体)时钟的时间间隔表412。如图5中所示,存在提供一致的时间给给高级定时组件和低级定时组件的诸如系统时钟524的时钟以使多个级保持同步。
响应于从应用程序202(或某些其它源)接收到的交互更改,高级定时组件需要更新时钟的状态,该状态直接涉及该更改,和任何如同步图元所指定的那样间接涉及的。注意的是对于单时钟的更改可影响在定时结构内其它时钟是可能的。例如,幻灯片的陈述者可暂停整个显示,由此当前在显示的任何动画和线性媒体需要被暂停。如以下描述的,根据本发明的一个方面,故事板允许动画和/或媒体被一起分组并且以这种理想的方式协调。
一般地,任何时候发生关于对象的交互作用,事件表按需重新生成并且时间间隔表重新计算并被发送至较低级定时组件。较高级组件执行这些操作(例如,以每秒大约6到10次的频率)以使较低级组件(例如,每秒操作60次)只需要处理对于每个动画的对象当前的时间间隔表。
除了对象的该时间间隔信息和开始以及结束值,额外的参数可被发送至较高级组件并导致对于时间间隔表的更改。例如,应用程序可及时寻找对应于一事件的时间段。该应用程序指定寻找何时将要发生、以及到什么程度。象倒带或快进媒体,通过在动画上的寻找,应用程序可及时前跳或回跳至特定点。倒转事件是应用程序可指定的另一类型的计划的或交互的事件。当倒转事件在表中时,进程自动倒转,例如,从百分之百至百分之零。速度更改也被支持,例如,低于某个时钟的每个事物可被设置成运行速度高于或低于实际时间。其它属性也可被指定,诸如对于特定时钟表明是否允许重新开始,和/或如果当前不运行是否再开始,例如,在一秒处启动,在10秒处再开始,在100秒处再开始等,但是如果当前运行,重启或不重启是可选择的。
用于协调动画和媒体的故事板
本发明在其它事情之中涉及故事板,该故事板包括允许对于元素(例如,诸如窗体、面板的FrameworkElement(框架元素),诸如按钮等的控制器)特定时间线树的对象。时间线设置元素的特性值,并且控制元素和它子树的动画。故事板实质上对应于可被开始、结束或暂停、或用以寻找特定类型的时间线对象,从而控制与该故事板关联的动画元素和/或媒体行为的外观。
可以理解的是,本发明的一个方面是故事板包括可与元素关联并且能独立开始的最高级时间线集合。例如,如以下描述的,模型允许作者在同一故事板集合内的元素上具有鼠标浮过动画和鼠标点击动画。鼠标浮过和鼠标点击动画时间线可被独立地触发,但是它们包括在同一单元内。故事板也在定时树和一元素树之间提供连接点,该元素树允许在故事板内的动画相对于故事板附加于的元素解析它们的目标。故事板也可包括触发器集合,该触发器可由状态更改、事件、或其它动作所激活并包括当触发器激活时将要被执行的一组可扩展的动作(例如,时间线的开始或停止)。故事板是基础对象模型的一部分,该对象模型允许作者从代码中在故事板内交互地开始和停止时间线。
一般地,在故事板内的时间线表现可被应用于各种子树、元素和其它对象(例如,文本字母、单词、线、段落等)的动画模板。故事板可直接框架元素上被设定,或通过式样(如以下描述的,包括一个或多个数据值的资源,该数据值可被应用于诸如按钮的颜色、大小和其它特性的元素)应用于框架元素。故事板也可被用以把动画应用于对象,该对象支持复制,诸如可变性(例如,画笔、转换、几何等,其中可变性可用以设置FrameworkElement(框架元素)的特性,如在美国专利申请序号为10/693,438的申请中描述的)。例如,画笔是可变的,其通过复制原始背景信息然后在复制的信息上操作来设置按钮或面板的背景,以使原始信息被恢复。如此,故事板可被用以动画制作设置FrameworkElement(框架元素)特性的可变性。换句话说,存在两种涉及故事板的对象,称为参与故事板的元素(诸如FrameworkElement(框架元素))和那些没有参与故事板的元素(例如,以上描述的可变性)。故事板的算法/数据结构是那些参与特定任务的,甚至那些只要支持复制的没有参与特定任务的。
根据本发明的一个方面,故事板(例如,通过API访问的对象)允许程序作者通过对时间线内的动画分组(成为元素特性)来安排协调的动画组。换句话说,故事板把定时树和元素树整合,通过相同的时间线以协调的方式提供能力以操作对于不同动画的不同特性的定时。如此,故事板提供一种方法以将其它影响各种对象和特性的独立的时间线组成单一时间线树,使程序作者以直接的方式组织和控制复杂定时事件组。例如,作为故事板一部分的时间线下的动画分组允许通过仅仅重启故事板来立即重启所有动画,而不是必须寻找和重启单独的动画。此外,定时树和元素树的分离允许用户使用定时树及时做出处理,诸如顺序运行一组动画、或循环复杂的定时结构。
故事板可在动画/视频/音频的特定实例上使用,或被用以特定式样的动画。
作为对比,考虑以下在没有故事板情况下写的示例代码,以及如此需要后台编码以添加交互响应(在这个例子中,两个独自按钮在鼠标点击后理想的同时动画)。代码的作者需要确定对于每个按钮的动画设定相同的开始时间,这使由于这些时间需要在每个按钮的时间线上各自声明。注意的是尽管该代码目的在于同时动画制作两个按钮,由于它们由两个独自的(开始)调用所启动,实际上在第一动画和第二动画之间将存在一些滞后:
<Canvas Background=″#ffffffff″><Button Opacity=″0″Click=”StartAnimations”><Button.Opacity><Timeline def:ID=”_timeline1”BeginTime=″5″><DoubleAnimation To=″1″Duration=″1″/></Timeline></Button.Opacity>Hello</Button><Button Canvas.Left=″100″Canvas.Top=″0″><Canvas.Top><Timeline def:ID=”_timeline2”BeginTime=″5″><LengthAnimation To=″300″Duration=″5″/></Timeline></Canvas.Top>Hello 2</Button></Canvas>public void StartAnimations(object sender,RoutedEventArgs args){_timelinel.InteractiveController.Begin();_timeline2.InteractiveController.Begin();} |
根据本发明的各个方面,在一实现中,为了动画而使用故事板,程序作者添加时间线、关联机制(例如,SetterTimeline或附加的故事板特性)、动画对象至FrameworkElement(框架元素)的故事板特性中。故事板可被直接应用于特定元素,或在式样内被定义(如以下所描述的)。在该实现中,故事板具有至少一个用于链接一个或多个时间线至特定对象的特定特性的SetterTimeline。除了以上描述的时间线的基本定时特性外,SetterTimeline有三个使作者指定要动画制作的对象和特性的唯一特性,并且从那里进行动画制作的基值称为TargetID,该TargetID指向对象动画;PropertyPath(在此与“特性”或“路径”同义),指向要动画制作的特性;以及值,该值(如果存在的话)是指定指向的特性的基值的选择特性。需要注意的是在作者使用SetterTimeline指向对象和特性后,作者添加时间线和动画作为它的子时间线和动画应用到指向的特性。
以下示例代码说明故事板如何相对于先前的示例代码简化编码和提高功能性(注意的是该示例使用SetterTimeline示例实现而不是通过在Storyboard(故事板)类中附加的特性操作的一可供选择的实现,如以下描述的):
<Canvas Background=″#ffffffff″><Canvas.Storyboards><Timeline def:ID=”myStoryboard”BeginTime=″5″><SetterTimeline TargetID=″myButton″><DoubleAnimation To=″1″Duration=″1″/></SetterTimeline><SetterTimeline TargetID=″myButton2″PropertyPath=″(Canvas.Top)″Value=″0″><LengthAnimation To=″300″Duration=″5″/></SetterTimeline></Timeline></Canvas.Storyboards><Canvas.Triggers><EventTrigger TargetID=”myButton”Event=”Click”><TimelineBeginAction TargetID=”myStoryboard”/></EventTrigger></Canvas.Triggers><Button ID=″myButton″Opacity=″0″>Hello</Button><Button ID=″myButton2″Canvas.Left=″100″>Hello 2</Button></Canvas> |
作为选择,该第二个示例XAML代码组使用故事板启动点击事件后两个按钮上的同时动画。为此,在图层的单一故事板时间线内,存在一把一元素特性(myButton元素的暗度特性)连接到该故事板时间线的SetterTimeline以及另一把另一元素特性(myButton2元素的位置特性)连接到该相同故事板时间线的SetterTimeline。一单一事件(点击myButton)触发故事板开始一协调的动画,该动画随时间的推移更改myButton的暗度以及myButton2的位置。
如可从对比代码示例中看出,故事板的使用减少代码复杂度并在动画的开始时间之间产生真正的协调。在该例子中,由于没有独自对于开始的调用,两个动画真正地同时开始。此外,没有在XAML后台的代码被需要实现交互性,所以作者可完全用XAML编写代码。此外,结合故事板,作者只需要在一处,也就是说在故事板的时间线上,设定协调的开始时间。
如此,故事板使作者能组织和控制动画组为一组而不是个别地定义随着时间的推移的每个动画的改变,从而使同步的动画和顺序动画的直接创建成为可能(以下描述)。考虑以下的例子,其对应于具有三根不同宽度和颜色的矩形指针的时钟,有黑色的小时指针、绿色的分钟指针以及红色的秒指针,该时钟通过设定角度的旋转变换的使用来自动显示当前系统时间。在例子中,一单一故事板时间线被用以协调三根指针的动画:
<Canvas xmlns=″http://schemas.microsoft.com/xaml/2005″Height=″100%″Width=″100%″Background=″White″ID=″Canvasl″><!--We initialize the clock hands to stand at zero degrees at midnight.The hands appear at the current global time when displayed because they have theoretically)continued rotating ever since midnight.--><Canvas.Storyboards><Timeline Begin=″@12:00:00 AM″>//Set the zero-time position for the clock hands.<SetterTimeline TargetID=″hourhand″Property=″RenderTransform″SubPropertyPath=″RotateTranform.Angle″><DoubleAnimation From=″0″To=″360″Duration=″12:0:0″Repeat=″Indefinite″/></SetterTimeline><SetterTimeline TargetID=″minutehand″Property=″RenderTransform″SubPropertyPath=″RotateTranform.Angle″><DoubleAnimation From=″0″To=″360″Duration=″1:0:0″Repeat=″Indefinite″/></SetterTimeline><SetterTimeline TargetID=″secondhand″Property=″RenderTrahsform″SubPropertyPath=″RotateTranform.Angle″><DoubleAnimation From=″0″To=″360″Duration=″0:1:0″Repeat=″Indefinite″/></SetterTimeline></Timeline></Canvas.Storyboards><Rectangle ID=″secondhand″Fill=″Red″Top=″0″Left=″50″Height=″50″Width=″2″><Rectangle.RenderTrahsform><RotateTransform Center=″0 50″/></Rectangle.RenderTransform></Rectangle><Rectangle ID=″minutehand″Fill=″Green″Top=″0″Left=″50″Height=″50″Width=″3″><Rectangle.RenderTransform><RotateTransform Center=″0 50″/></Rectangle.RenderTransform></Rectangle><Rectangle ID=″hourhand″Fill=″Black″Top=″0″Left=″50″Height=″50″Width=″4″><Rectangle.RenderTransform><RotateTransform Center=″0 50″/></Rectangle.RenderTransform></Rectangle></Canvas> |
运行完以上的代码后,通过呈现转换特性和旋转转换角度子特性,时间管理器基于当前时间自动计算每个指针的旋转量,(由于在公元0年的00:00:00时,指针被认为在角度为0度处)以及当呈现时使用的更新角度。每个新的动画计算更新指针的角度位置,例如,每帧一次或以某个其它的速率。所有三个指针一起启动并且如果,例如,某个事件(在以上示例代码中未示出)停止时钟,所有三个指针一起停止。
可以被容易地理解的是,元素特性的动画复杂性可以是重要的,如例如,多个SetterTimelines可以在单一故事板时间线下被分组,每个SetterTimeLines依次涉及不同的目标元素以及那些元素的特性。故事板可结合;例如,一故事板可把转换与元素关联并且另一个故事板可对该转换设定值,(虽然代码内的排序需要正确以使转换在设定值之前存在)。尽管如此,故事板的使用使作者能直接设计理想的元素、特性和动画行为。
图6是这种代码可如何被安排的一般表现,该表现结合具有故事板组(例如,故事板1-故事板n)的图层,组中的每个故事板(例如,故事板1)包括具有可能多个SetterTimelines(例如,SetterTimelin1-SetterTimelinm)(或以下描述的,具有诸如结合特性的ParalleTimeline的等效元素树联合机制)的时间线。图层也可包括也在以下描述的事件触发器信息,以及有关与故事板关联的元素和资源(作为目标具有动画的特性的目标)的信息(诸如式样和文件标识符)。
可以被容易地理解的是,各种实现本发明各个方面的选择性方式是可行的。例如,不使用SetterTimeline,一可选择的联合机制通过附加的特性在故事板类上指定类似联合信息。注意的是该方法促进媒体和故事板的结合。对于该选择性方法的示例XAML如以下所示:
<Canvas.Storyboards><MediaTimeline Source=“my.wmv”Storyboards.TargetID=“myvideo”><ParallelTimeline Storyboard.TargetID=”myButton”Storyboard.Property=”Width”Storyboard.Value=...><LengthAnimation.../>//no SB.TargetID on nestedtargets</ParallelTimeline><LengthAnimation Storyboard.Property=”...”Storyboard.</Canvas.Storyboards<Video ID=...//etc. |
注意的是在以上的例子中,存在用于将某个媒体文件(my.wmv)与故事板关联的MediaTimeline,以及在其中设定故事板特性的ParallelTimeline(具有一些用于简明的省略号)。注意的是“ParallelTimeline”实际上是先前理解的故事板时间线,使用不同的名称以将其从其它类型的时间线中区分,包括MediaTimeline(以上描述的)以及SequentialTimeLine(以下描述)。然而,ParallelTimeline也通过故事板特性的使用,例如,Storyboard.Property、Storyboard.Value以及Storyboard.Target(而不是SetterTimeline的TargetID、PropertyPath以及Value),消除对于SetterTimeline的需要,虽然故事板特性的使用使附加的特性变长并且不很容易被发现。一般地,故事板特性可被附加于任何时间线,只要对于每个故事板,存在某个目标特性(例如,TargetID),其它故事板特性在与TargetID相同的时间线上,并且没有嵌套的Targets(目标)。注意的是如果没有TargetID被指定,可以假设目标是操办故事板的元素,例如,在先前例子中的图层。对于媒体而言,MediaTimelines不指定Storyboard.Property或Storyboard.Value。
以下的表格表述示例性的Storyboard(故事板)类以及它的对象特性和方法:
Public static class Storyboard{//Dependency Propertiespublic static readonly DependencyProperty TargetIDProperty;public static readonly DependencyProperty ValueProperty;public static readonly DependencyProperty PropertyPathProperty;public static readonly DependencyProperty SubPropertyPathProperty;//Static Methodspublic static void SetTargetID(DependencyObject d,StringtargetID);public static string GetTargetID(DependencyObject d);public static void SetValue(DependencyObject d,StringtargetID);public static string GetValue(DependencyObject d);public static void SetPropertyPath(DependencyObject d,StringtargetID);public static string GetPropertyPath(DependencyObject d);public static void SetSubPropertyPath(DependencyObject d,String targetID);public static string GetSubPropertyPath(DependencyObject d);} |
其它用于把元素树与故事板的时间线树关联的可供选择的方式在以下的XAML示例中示出:
<Canvas.Storyboard><SetterTimeline><SetterTimeline.Value><MediaTimeline></SetterTimeline.Value></SetterTimeline><SetterTimeline Storyboard.Target=″myButton″Property=″Width″Value=...><LengthAnimation..../></SetterTimeline></Canvas.Storyboards><Video ID=...//etc. |
注意的是在以上的选择性方式中,MediaTimeline不是SetterTimeline的子类,但应该是时间线树的一部分,而不是时间线上的值。如此,这个方法一般是较不理想的句法。
<Canvas.Storyboard><MediaSetterTimeline><MediaTimeline Source=″my.wmv″TargetID=″myvideo″></MediaSetterTimeline><SetterTimeline TargetID=″myButton″Property=″Width″Value=...><LengthAnimation..../></SetterTimeline></Canvas.Storyboards<Video ID=...//etc. |
注意以上的选择性方式添加另一个类,MediaSetterLine。
<Canvas.Storyboard><SetterTimeline TargetID=″myvideo″Property=″Source″><MediaTimeline Source=″my.wmv″></SetterTimeline><SetterTimeline TargetID=″myButton″Property=″Width″Value=...><LengthAnimation..../></SetterTimeline></Canvas.Storyboards><Video ID=...//etc. |
以上的选择性方式遵循动画模式,但由于值和子类要成为同一特性的值而会产生混淆,并且还由于其允许MediaTimeline在宽度上放置。
尽管有多个可能的可供选择的方式,为了解释,在此其它的示例一般将通过使用SetterTiemline被描述。
在一实现中,故事板可以是在FrameworkElement上的时间线集合特性;当第一次应用到元素时,时间线集合被例示至元素上的时钟。在Storyboards(故事板)特性内的值是时间线实例,例如,具有每个是SetterTimeline(在一实现内用以设定特性的基值)等的时间线层级的叶子的IList<Timeline>,或在该实现中具有SetterTimeline下的动画的动画实例(用以修改基值)。SetterTimeline是一时间线,并且可具有子类,然而在一实现中SetterTimeline唯一合法的子类是Animation(动画)。
注意的是Storyboards(故事板)特性不是依赖特性。在故事板特性中的时间线实例的每个实例潜在地表现分层结构。这些时间线分层结构被例示至TimelineClcok分层结构,并且内部地存储在主FrameworkElement上。然后这些单独的时钟实例可通过使用采用Timeline作为输入(需要是来自故事板的时间线中的一个)的FindStoryboardClock API被获取,并且使用其以寻找关联的时钟。
在当前的实现中,当ISupportInitialize.EndInit在主框架元素上被调用时,故事板被处理。一旦故事板已经被处理,FrameworkElement.Storyboards可被设定成只读以帮助反映“更改不被处理”的事实。
如以上描述的,SetterTimeline是用于指定特性的时间线,该特性可以隐含地指向主FrameworkElement或如果特定的,指向目标FrameworkElement,以及任意的值。如果特定的,TargetID是在主FrameworkElement的派生内的FrameworkElement ID的引用。在SetterTimeline上的Path(在此与PropertyPath同义)特性描述哪个特性正经由故事板被动画制作,并且被请求。
SetterTimeline的Value特性也可以是任意的。如果特定的,它指示当Timeline活动时,特性应该采用的基值。当没有动画或当动画需要暗示的值时,使用该基值。基值可相符地来自本地值(直接在元素上设定)、继承、式样等的值相等。例如,以下动画制作了从值10至20的特性:
...<SetterTimeline PropertyPath=″...″Value=″10″><LengthAnimation To=″20″/></SetterTimeline>... |
注意的是如果以上的LengthAnimation指定From和To值,那么基值(SetterTimeline.Value)可以被忽略。
在一实现中,目标需要在与Storyboards特性的主FrameworkElement相同的树中成为FrameworkElement的引用。PropertyPath的第一组件需要成为依赖特性支持的特性。例如,由于Window.WindowAutoLocation不是依赖特性,所以它不是故事板可实现的。
特性可以是在索引的集合内的项。在SetterTimeline上的PropertyPath(或Path)属性可使用附加的特性在括号中包括的特殊情况“记录”至对象中。例如,如果“画笔”是Brush,以下的特性路径在线性梯度画笔的停止的颜色上访问阿尔法:
brush.Stops[1].Color.A而以下的路径访问在元素上附加的特性的驳接:
button.(DockPanel.Dock)
注意的是以上的特性路径是在附加的特性句法内,但实际上不是附加的特性。而是,BackgroundProperty在Button本身上使用按钮上的通用语言运行时间特性存取程序被定义,而不是使用具有静态Set/Get方法存取程序的传统附加的特性。这个是必要的,由于在编译期间,TargetID不能被解析为特定类型,所以不能知道“Background″特性解析哪个类。
PropertyPath如以下定义:
public sealed class PropertyPath{public PropertyPath(string path,params DependencyProperty[]pathParameters);public PropertyPath(DependencyProperty dependencyProperty);public string Path{get;set;}public DependencyProperty[]PathParameters{get;set;}} |
以下描述复杂特性路径,也就是那些具有一个以上特定组件的路径。
根据本发明的一个方面,除了将定时树和元素树整合外,故事板允许特性触发器和事件触发器的使用以控制它们的操作(例如,开始、暂停、寻找和停止动作)。一般地,特性触发器对应于诸如真或假的某个状态/值而事件启动(以及如此不具有假值)。例如,当鼠标在元素上方盘旋时,特性触发器可具有真值。当鼠标不在元素上方盘旋时,具有假值。每个真或假状态可被用以在一故事板内(或多个故事板内)分离地开始、停止、暂停、和/或寻找。诸如鼠标点击元素的事件启动并且不具有可变的状态,但是同样地可控制故事板的操作。通过这种方式,动画通过与显示的元素树表现的用户交互作用是可控制的。
触发器是在FrameworkElement上的集合特性。触发器可指定包括SetAction和TimelineAction的动作。SetAction指定特性的值。TimelineAction在Storyboards特性内操作时间线。
作为例子,以下给按钮在鼠标浮过的特定背景:
<Window.Triggers><PropertyTrigger TargetID=”button”Property=”IsMouseOver”Value=”true”><SetAction Path=”(Button.Background)”Value=”red”/></PropertyTrigger></Window.Triggers><Button ID=”button”>OK</Button></Window><Window> |
TriggerAction和SetAction被定义为:
abstract class TriggerAction{}class SetAction:TriggerAction{String TargetID{get;set;}DependencyProperty Property{get;set;}PropertyPath Path{get;set;}Object Value{get;set;}} |
注意SetAction遵循SetterTimeline的模式,但SetAction不是Timeline(时间线)。
TimelineAction是抽象类,具有用于在TimelineClock上每个动词方法的子类:
abstract class TimelineAction:TriggerAction{Timeline TargetID{get;set;}}class BeginInAction:TimelineAction{int Offset{get;set;}}... |
TimelineAction的TargetID是字符串,字符串在当前范围内被解析并且解析成时间线。
如以上描述的,除了特性触发器之外还存在事件触发器,其中事件触发器采用当该事件在主元素上启动时导致动作发生的事件作为它的源。注意的是,不象特性触发器,事件触发器是没有范围的;它执行一操作然后该操作确定它自己的生存期。例如,一事件可开始时间线,并且该时间线可运行直到它的持续时间结束,或直到另一事件触发器等停止它。
以下一般地描述一触发器:
class Trigger{TriggerActionCollection TriggerActions{get;}}class PropertyTrigger:Trigger{String TargetID{get;set;}DependencyProperty Property{get;set;}PropertyPath Path{get;set;}Object Value{get;set;}}class EventTrigger:Trigger{String SourceID{get;set;}RoutedEventID Event{get;set;}} |
结合关联的时间线使用事件触发器,以下示例代码示出了被“on(开)”和“off(关)”按钮操作的时间线受控内容:
<Window><Window.Storyboards><SequentialTimeline ID=”myTimeline”><SetterTimeline TargetID=”foo”.../><SetterTimeline TargetID=”bar”.../>...</SequentialTimeline></Window.Storyboards><Window.Triggers><EventTrigger SourceID=”b1”Event=”Click”><BeginInAction TargetID=”myTimeline”Offset=”0”/></EventTrigger><EventTrigger SourceID=”b2”Event=”Click”><EndInAction TargetID=”myTimeline”Offset=”0”/></EventTrigger></Window.Triggers><DockPanel><DockPanel DockPanel.Dock=”Top”>...(content)...</DockPanel><Button ID=”b1”DockPanel.Dock=”Left”>Start</Button><Button ID=”b2”DockPanel.Dock=”Right”>Stop</Button></DockPanel></Window> |
注意以上代码使用顺序的时间线而不是独立时间线。顺序时间线有它被顺序触发的子时间线,例如,某个对应于“bar”(未示出)的故事板在“foo″结束之后被触发并且不是在任何程序指定的时间数据上。
除了事件触发器以外,特性进入\退出触发器也被用在触发时间线内,如一般在以下示例中显示的:
<PropertyTrigger Property=”MouseOver”Value=”True”><PropertyTrigger.Enter><BeginInAction Offset=”0”TargetId=”myTimeline”/></PropertyTrigger.Enter><PropertyTrigger.Exit><EndInAction Offset=”0”TargetId=”myTimeline”/></PropertyTrigger.Exit</PropertyTrigger> |
根据本发明的另一方面,故事板和触发器可被式样化的(虽然不完全象依赖特性,由于Storyboard本身不是依赖特性)。换句话说,如以上描述的,Style(式样)允许作者把表现一个或多个特性的数据分在一起,然后可作为一组应用到元素的相应的特性;这些Style内的特性可被动画制作和/或与触发器对应。例如,应用以下的Style可式样化在页面上的按钮以在红色和蓝色之间顺序地闪烁:
<DockPanel><DockPanel.Resources><Style><Button/><Style.Storyboards><SequentialTimeline RepeatDuration=”Indefinite”><SetterTimelinePath=”(Button.Background)”Value=”Red”Duration=”1”/><SetterTimelinePath=”(Button.Background)”Value=”Blue”Duration=”1”/></SequentialTimeline></Style.Storyboards></Style></DockPanel.Resources> ...</DockPanel> |
如被理解的,使用控制来自包括触发器、时间线、故事板、在标记内本体设定的值以及其它的不同机制的元素特性值的能力,特性值之间的冲突解析需要被处理以确定应该是什么特性值。例如,设置为Storyboards特性一部分的值覆盖在元素上本地设置的值。如此,存在于特定FrameworkElement上特性值的优先,使用Triggers(触发器)特性开始的作为最高优先权。注意的是如果多个触发器尝试设定相同的特性,应用“最后写入者赢”的规则,例如,在标记的情况下,在标记内最后发生的触发器让它的值被应用。
Storyboards(故事板)特性有下一个最高优先权,如果多个故事板设置者尝试设置相同的特性,则再次利用“最后写入者赢”的解决方案。最后写入者通过使用时间线树的深度优先途径被解释。
在元素上本地设置的值是下一个,(例如,FraneworkElement.SetValue)。式样一般使用在式样内相同的优先次序跟随下一个,就是说,Style(式样)触发器(如果在相同的层次处有冲突,则再次利用“最后写入者赢”,接着Style Storyboards(式样故事板),接着Style(式样)目标类型值。
参考复杂特性路径的解释,设置者不仅仅修改在目标上的特性,而且在某些情况下修改在目标的子特性中的一个是可能的。一般地,复杂特性通过首先建立特性值对于元素而言是什么,然后记录至该特性用以动画来加以处理。例如,以下的代码示例了一红色矩形,该矩形通过动画制作暗度来反复地和逐渐地淡入和淡出:
<Window><Window.Storyboards><SetterTimeline TargetID=″rectangle″Begin=″0″RepeatDuration=″Indefinite″PropertyPath=″(Rectangle.Fill).(SolidColorBrush.Opacity)”/><DoubleAnimation From=″0″To=″1″Begin=″0″Duration=″5″AutoReverse=″true″/></SetterTimeline></Window.Storyboards><Rectangle ID=″rectangle″RectangleWidth=″100″RectangleHeight=″100″Fill=″Red″/></Window> |
在一个实现中,特性的第一组件被要求是依赖特性(例如,在以上示例中正被填充的Background(背景))。由于复制副本以使复制的副本可被修改的需要,该依赖特性的类型也被要求是可变的;(选择性方式包括深度复制解决方案,或保留其为模板)。注意的是由于式样和故事板/触发器依然根本上是基于依赖特性,该方案是可行的。如果任何涉及Button.Background的事物更改,整个背景值被重新计算。
如此,在以上的例子中,首先做出纯色画笔的副本并且存储在矩形实例中。内部地,由于当触发器不活动时值需要被保存,本地值实际上没有被替换。其次,触发器在该副本上执行,如重放脚本。
如此,特性无效算法在两个传递中发生,即触发特性无效的第一传递,正如没有复杂特性路径存在,以及在其内触发器使用复杂特性路径(也就是说,在其内使用点)仅仅使基本依赖特性无效的第二传递。
为了动画制作,特性值计算算法使用标准搜索算法寻找基本特性值,而忽略任何在其上具有复杂特性路径的任何值。其次,算法使用复杂特性路径寻找可应用的触发器和设置者,并且把它们应用于来自上述的基值。这个可需要做出可变性的副本,该副本的做出可通过无论何时,只要可能的共享值被优化;副本仅仅在必要时做出。
使用复杂特性路径需要在特性重建中维持一致的时钟。例如,以下的标记代码显示获得来自故事板的线性梯度画笔背景的按钮,在此第一停止由一特性触发器修改(当鼠标在按钮上方时),以及第二停止由第二触发器修改(当按钮被按下时):
<Window><Window.Resources><Style><Button/><Style.Storyboards><SetterTimeline Start=″0″Duration=″60″Path=″(Button.Background)″><SetterTimeline.Value><LinearGradientBrush><GradientStop Color=″#FF000000″/><GradientStop Color=″#00FF0000″/></LinearGradientBrush></SetterTimeline.Value></SetterTimeline></Style.Storyboards><Style.VisualTriggers><PropertyTrigger Property=″IsMouseOver″Value=″true″><SetActionPath=″(Button.Background).Stops[0]″Value=″#80000000″/></PropertyTrigger><PropertyTrigger Property=″IsPressed″Value=″true″><SetAction path=″(Button.Background).Stops[1]″Value=″#00800000″/></PropertyTrigger></style.VisualTriggers></Style></Window.Resources><Button>OK</Button></Window> |
在该例子中,在第一分钟期间,按钮的背景是线性梯度的,从红色至绿色。当鼠标在按钮上方时,画笔更改为从粉红色至绿色的线性梯度。当鼠标按钮被按下时,颜色线性地从粉红色变为淡绿色,直到当鼠标按钮被释放时,在那个时候背景更改回从粉红色至绿色的线性梯度画笔。当鼠标离开按钮时,背景返回红色至绿色的线性梯度。注意的是在SetAction对象中具有复杂特性路径是可行的。
适应该情况的第一步是当式样被应用时(也就是说,在OnStyleInvalidated期间),在式样化的父元素上存储时钟。这个允许当特性被重建时,相同的时钟被重用。第二步是忽略特性路径错误;也就是,在第一分钟后,当时间线不活动时,按钮不再具有线性梯度画笔。在该点,修改停止的触发器不再有意义,并且(与数据绑定一致)被沉默地忽略。
来自Storyboard(故事板)特性,每个Timeline(时间线)树被例示至TimelineClock(时间线时钟)树,内部地存储在操办Storyborads(故事板)的相同FrameworkElement上。对于每个设置者和动画时钟,指向的元素也被给予对于时钟的引用。对于每个设置者时钟,指向的元素听时钟上的活动/不活动事件,其中的每个事件触发特性的无效。对于每个动画时钟,时钟被合并至用于特性的目标元素的动画时钟表内。
图7提供当故事板在元素上被设定和处理时,步骤和数据结构更新的例子。一般地,图7显示了用于以下简化SetterTimeline例子的对象和引用:
<DockPanel><DockPanel.Storyboards><SetterTimeline TargetID=″_rectangle″Path=″(Rectangle.RectangleWidth)″Value=″10″Start=″0″End=″10″/></DockPanel.Storyboards><Rectangle ID=″_rectangle″RectangleHeight=″10″/></DockPanel> |
最初,一个或多个Timeline(时间线)实例被添加至DockPanel的Storyboards(故事板)特性中,例如,通过调用DockPanel.Storyboards.Add(new SetterTimeline(...))。
当DockPanel的ISupportInitialize.EndInit被调用时,故事板(例如,相应于图7中DockPanel702的SetterTimeline700)被处理以使在Storyboards(故事板)中的每个时间线都通过TimelineClock.CreateFromTimeline被示例至时钟(例如,图7中标识为704),并且私密地存储在FrameworkElement(例如,图7钟的矩形706)上。
每个设置者的目标被给予对于与影响的依赖特性关联的时钟的引用。目标也在诸如(已开始和已结束)的时钟边界事件上设置收听者。在设置者下的任何动画被添加至目标的动画表(通过FrameworkElement.GetAnimationClocks)。
当式样在FrameworkElement(每个FrameworkElement)上设置时,时钟从在Style.Storyboards集合中的Timeline分层中被例示。对于指向式样化父类的设置者,FrameworkElement维持对时钟的引用(与关联的依赖特性联系),并且式样化的父类在时钟的已开始/已结束事件上设置收听者。对于指向在Style.VisualTree内元素的设置者,当FrameworkElement例示元素时,类似的引用/收听者被设置。
注意的是当时间线被处理时,具有单简单特性路径(单组件)的设置者时间线从具有复杂路径的设置者时间线中分离。这个允许如以上描述的,对于特性值的双通道寻找。
结论
如从以上详细的描述中可看出,提供了以故事板的形式的机制,其中其优势是消除作者在不同时间线上手动同步各种在动画期间发生的操作(例如,开始和停止时间或对事件的响应)需要。此外,通过使用式样化系统,故事板的可用性消除必须被个别地应用于每个动画或媒体元素的定时信息的需要。
尽管本发明允许各种修改和可供选择的构造,其中某些示例的实施例在附图中显示并在以上被具体描述。然而,可以理解的是,没有限制本发明于公开的特定形式的意图,而相反,是为了涵盖在本发明的精神和范围内的所有的修改、可供选择的结构以及同等物。
在此引用的每个专利申请被转让给本发明的受让人,并在此并入作为引用。