WPF中的动画

WPF中的动画
                                                                                  周银辉

动画无疑是WPF中最吸引人的特色之一,其可以像Flash一样平滑地播放并与程序逻辑进行很好的交互。这里我们讨论一下故事板。

在WPF中我们采用Storyboard(故事板)的方式来编写动画,为了对Storyboard有个大概的印象,你可以粘贴以下代码到XamlPad来查看效果:
None.gif<!-- This example shows how to animate with a storyboard.-->
None.gif
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
None.gif  xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
None.gif 
None.gif  WindowTitle
="Storyboards Example">
None.gif  
<StackPanel Margin="20">
None.gif    
None.gif    
<Rectangle Name="MyRectangle"
None.gif      Width
="100"
None.gif      Height
="100">
None.gif      
<Rectangle.Fill>
None.gif        
<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
None.gif      
</Rectangle.Fill>
None.gif      
<Rectangle.Triggers>
None.gif        
<EventTrigger RoutedEvent="Page.Loaded">
None.gif          
<BeginStoryboard>
None.gif            
<Storyboard RepeatBehavior="Forever" AutoReverse="True">
None.gif              
<DoubleAnimation 
None.gif                
Storyboard.TargetName="MyRectangle"
None.gif                Storyboard.TargetProperty
="Width"
None.gif                From
="100" To="200" Duration="0:0:1" />              
None.gif            
</Storyboard>
None.gif          
</BeginStoryboard>
None.gif        
</EventTrigger>
None.gif      
</Rectangle.Triggers>
None.gif    
</Rectangle> 
None.gif  
</StackPanel>
None.gif
</Page>
None.gif
None.gif

在介绍Storyboard之前应该先了解Animation
Animation提供一种简单的“渐变”动画,我们为一个Animation指定开始值和一个结束值,并指定由开始值到达结束值所需的时间,便可形成一个简单的动画。比如我们指定长方形的宽度由100变化到200,所需时间为1秒,很容易想像这样的动画是什么样的,而它对应的Xaml代码如下:
None.gif<DoubleAnimation 
None.gif                
Storyboard.TargetName="MyRectangle"
None.gif                Storyboard.TargetProperty
="Width"
None.gif                From
="100" To="200" Duration="0:0:1" />       
将它翻译成C#代码则如下:
None.gif DoubleAnimation myDoubleAnimation = new DoubleAnimation();
None.gif            myDoubleAnimation.From 
= 100;
None.gif            myDoubleAnimation.To 
= 200;
None.gif            myDoubleAnimation.Duration 
= new Duration(TimeSpan.FromSeconds(1));
None.gif            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
None.gif            Storyboard.SetTargetProperty(myDoubleAnimation, 
new PropertyPath(Rectangle.WidthProperty));

代码里我们定义了一个DoubleAnimation,并指定了它的开始值和结束值以及它由开始值到达结束值所需的时间。至于后面两句,它们是用来将Aniamtion与指定的对象和指定的属性相关联,等会我们将介绍。
注意到,这里我们使用的是DoubleAnimation,因为我们所要变化的是数值。那么如果我们要变化颜色是不是就用ColorAnimation了呢,对,其实出了这些之外还有PointAnimation等等,并且你可以实现IAnimatable接口来实现自定义版本的Animation。关于这些你可以参见System.Windows.MediaAniamtion名字空间.

但值得注意的是并非每个属性都能够使用Animation,它必须满足以下条件:
1,它必须是Dependency Property
2,它所在类必须继承于DependencyObject,必须实现了IAnimatable接口.
3,必须有类型一致的Animation Type(即Color类型使用ColorAniamtion,Point类型使用PointAnimation等)

一个简单的Animation定义了一个简单的动画,很容易想到的是,如果若干个Animation同时作用于一个对象,那么这个对象不就可以表现复杂的动画了吗,对,这就是Storyboard

Storyboard可以看做是Animation的容器,它包含了若干的简单动画来完成一个复杂动画。
参考以下代码:

None.gif<!-- This example shows how to animate with a storyboard.-->
None.gif
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
None.gif  xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
None.gif 
None.gif  WindowTitle
="Storyboards Example">
None.gif  
<StackPanel Margin="20">
None.gif    
None.gif    
<Rectangle Name="MyRectangle"
None.gif      Width
="100"
None.gif      Height
="100">
None.gif      
<Rectangle.Fill>
None.gif        
<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
None.gif      
</Rectangle.Fill>
None.gif      
<Rectangle.Triggers>
None.gif        
<EventTrigger RoutedEvent="Page.Loaded">
None.gif          
<BeginStoryboard>
None.gif            
<Storyboard RepeatBehavior="Forever" AutoReverse="True">
None.gif              
<DoubleAnimation 
None.gif                
Storyboard.TargetName="MyRectangle"
None.gif                Storyboard.TargetProperty
="Width"
None.gif                From
="100" To="200" Duration="0:0:1" />    
None.gif              
<ColorAnimation 
None.gif                
Storyboard.TargetName="MySolidColorBrush"
None.gif                Storyboard.TargetProperty
="Color"
None.gif                From
="Blue" To="Red" Duration="0:0:1" />            
None.gif            
</Storyboard>
None.gif          
</BeginStoryboard>
None.gif        
</EventTrigger>
None.gif      
</Rectangle.Triggers>
None.gif    
</Rectangle> 
None.gif  
</StackPanel>
None.gif
</Page>
None.gif
None.gif

这里我们的Storyboard定义了DoubleAnimation来变化矩形的宽度,并定义了ColorAnimation来变化矩形的颜色。

至此,你已经可以编写绚丽的WPF动画了,并推荐你下载Expression Blend来制作WPF动画.



但你会发现使用XAML标记的方式来编写动画虽然很简单,但缺乏了C#等程序设计语言的灵活性,比如我们的矩形动画中矩形的宽度是由后台逻辑计算出来的变量值,我们的动画将如何编写呢,这时我更喜欢使用C#的方式来编写动画,虽然这所需的代码量更大.
以下重点介绍如何用C#编写动画,并且这更助于你理解Storyboard是如何工作的。

参考以下代码:

            this.Name = "PageMain";
            myRectangle.Name 
= "MyRectangle";  
            
            NameScope.SetNameScope(
thisnew NameScope());                      
            
this.RegisterName(myRectangle.Name, myRectangle);

            DoubleAnimation myDoubleAnimation 
= new DoubleAnimation();
            myDoubleAnimation.From 
= 100;
            myDoubleAnimation.To 
= 200;
            myDoubleAnimation.Duration 
= new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, 
new PropertyPath(Rectangle.WidthProperty));
            
            
            Storyboard myStoryboard 
= new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);

            
this.Loaded += delegate(object sender, MouseEventArgs e)
            {
                myStoryboard.Begin(
this);
            };
其中:

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From 
= 100;
            myDoubleAnimation.To 
= 200;
            myDoubleAnimation.Duration 
= new Duration(TimeSpan.FromSeconds(1));
定义了一个DoubleAniamtion,并指定了它的开始值和结束值以及所需的时间.
Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);设置myDoubleAniamtion的作用对象是"myRectangle",注意到传入的第二个参数是一个字符串myRectangle.Name,那么我们的程序怎么知道"myRectangle"这个字符串就是指我们的矩形对象myRectangle呢,这里存在一个名称与对象的映射,即我们的"myRectangle"映射到矩形对象myRectangle,为了构造这个映射我们涉及到了NameScope(名字域)这个概念.
            NameScope.SetNameScope(thisnew NameScope());                      
            
this.RegisterName(myRectangle.Name, myRectangle);
上面的代码中,this设置了一个名字域,myRectagle向这个名字域注册了自己的名字,这样我们的程序就可以通过this的名字域来查找到myRectangle与"myRectangle"之间的映射关系了,关于NameScope可以参见MSDN
WPF Namescopes主题.
为了让myDoubleAnimation知道它所作用的属性是谁,我们使用
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.WidthProperty));语句来将Aniamtion与属性关联起来,其中PropertyPath中指定要作用的对象所对应的DependencyProperty.
然后我们将定义好的myDoubleAniamtion添加到myStoryboard的Children中去.最后就可以通过调用Storyboard的Begin(FrameworkElement)方法来开始我们的动画.

Begin方法的另一个重载形式是
public Begin (containingObject,isControllable),第二个参数表明我们的storyboard是否是可控的,如果可控的话,我们可以像控制播放器一样控制来控制storyboard,关于控制Storyboard请参考Storyboard类中的Pause,Seek等方法.

至此也许我们会认为这些知识足以应付简单的动画了,现在让我们一起设计一个简单的动画,也许会发现些问题.

假设我们的界面中存在一个Button对象button1,我们设计一个简单的动画让它在窗口中的x坐标从0连续变化到100,然后在从100变化到0,如此重复.也许我们会编写如下的代码:

            this.button1.Name = "button1";
            
this.Name = "window1";
            NameScope.SetNameScope(
thisnew NameScope());
            
this.RegisterName(this.button1.Name, this.button1);

            DoubleAnimation xAnimation 
= new DoubleAnimation();
            xAnimation.From 
= 0;
            xAnimation.To 
= 100;
            xAnimation.Duration 
= new Duration(TimeSpan.FromSeconds(1));

            Storyboard story 
= new Storyboard();
            story.AutoReverse 
= true;
            story.RepeatBehavior 
= RepeatBehavior.Forever;
            story.Children.Add(xAnimation);

            Storyboard.SetTargetName(xAnimation, 
this.button1.Name);
            Storyboard.SetTargetProperty(xAnimation, 
???);
但当我们编写到Storyboard.SetTargetProperty(xAnimation, ???);时发现似乎不知道将我们的xAniamtion关联到哪个属性.似乎Button中没有用来控制X坐标的DependencyProperty.但通过研究后发现(你可以通过ExpressionBlend自动生成的XAML代码来发现这些信息),如果我们将button1的RenderTransform设置为TranslateTransform,然后可以通过TranslateTransform的XProperty属性来更改button1的X坐标.注意到,我们并不是像以前一样直接关联到Button的某个属性(比如先前的WidthProperty),而是通过其RenderTransformProperty属性的XProperty来间接关联的,这中方式叫做"属性链"(PropertyChain).
参考下面的代码:
            DependencyProperty[] propertyChain = new DependencyProperty[]
            {
                Button.RenderTransformProperty,
                TranslateTransform.XProperty
            };

            Storyboard.SetTargetProperty(xAnimation, 
new PropertyPath("(0).(1)", propertyChain));
为了构造PropertyChain,我们先定义一个DependencyProperty的数组,注意数组的元素是怎么来的,它按照属性链的"链条"关系依次书写,直到到达我们最终要修改的属性,(由于我们是通过将RenderTransformProperty设置为TranslateTransform类型,所以第二个元素是TranslateTransform.XProperty),简单地说就是(类型1.属性1,类型2.属性2,....类型n.属性n),其中类型i是属性i-1的类型或可以与之转换的类型.
这样我们的代码就演化如下:
            this.button1.RenderTransform = new TranslateTransform();

            
this.button1.Name = "button1";
            
this.Name = "window1";
            NameScope.SetNameScope(
thisnew NameScope());
            
this.RegisterName(this.button1.Name, this.button1);

            DoubleAnimation xAnimation 
= new DoubleAnimation();
            xAnimation.From 
= 0;
            xAnimation.To 
= 100;
            xAnimation.Duration 
= new Duration(TimeSpan.FromSeconds(1));

            DependencyProperty[] propertyChain 
= new DependencyProperty[]
            {
                Button.RenderTransformProperty,
                TranslateTransform.XProperty
            };

            Storyboard story 
= new Storyboard();
            story.AutoReverse 
= true;
            story.RepeatBehavior 
= RepeatBehavior.Forever;
            story.Children.Add(xAnimation);

            Storyboard.SetTargetName(xAnimation, 
this.button1.Name);
            Storyboard.SetTargetProperty(xAnimation, 
new PropertyPath("(0).(1)", propertyChain));

            story.Begin(
this);
注意:如果你收到关于PropertyChain的运行时错误或动画没有效果,那么你应该初始化button的RenderTransform属性,所以我们添加了this.button1.RenderTransform = new TranslateTransform();语句.

更多的,请参见Windows SDK "Storyboard Overview"
示例程序下载










本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/407973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[访问系统] Api_Win32_Mac类工具包 (转载)

点击下载 Api_Win32_Mac.zip using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices;namespace BaseFunction {class API{[DllImport("kernel32")]//内存public static extern void GlobalM…

constexpr 函数

constexpr 函数 — C 20 constexpr double pi 3.14;constexpr允许你在编译时使用典型的C函数语法进行编程,但这并不意味之constexpr只和编译期有关 constexpr函数可以在编译期运行,也可以在运行时运行 但在以下情况constexpr函数必须在编译期运行: constexpr函数在编译的上…

constexpr和consteval --- C++ 20

constexpr和consteval — C 20 标准库容器和算法库对constexpr 的应用 C20 中大量的算法和容器可以使用constexpr,这意味着你甚至可以再编译期vector<int>进行排序 Algorithms library - cppreference.com 如下: #include <iostream> #include <ranges>…

函数模板(参考《C++ Templates 英文版第二版》)

函数编程(参考《C Templates 英文版第二版》) Chapter 1 函数模板 1.1 一窥函数模板 template<class T> T max(T a, T b) {return b < a ? a : b; }#include "max1.hpp" #include <iostream> #include <string> #include <format>int…

Curiously Recurring Template Pattern奇怪的模板递归 --- C++20

Curiously Recurring Template Pattern 奇怪的模板递归 — C20 我们都知道C有静态多态和动态多态,动态多态通过虚函数表实现,他的缺点就是对效率产生一点点影响 可以用CRTP解决这个问题 我们先举一个动态多态的例子: #include <iostream> using namespace std;class …

PROJECT #0 - C++ PRIMER [CMU 15-445645]笔记

PROJECT #0 - C PRIMER [CMU 15-445/645]笔记 这是数据库领域的一门课程, 由卡内基梅隆大学副教授Andy Pavlo授课, 目前在网上有授课视频资料、实验以及配套的在线测评环境 (限时开放至2021年12月31日) 环境: wsl2 Clion Project #0 - C Primer 还是很简单的,主要目的是让…

简单JS实现对表的行的增删

这段代码非常的简单&#xff0c;仅仅作为自己的一个小小的记录&#xff01; ok&#xff0c;先上一个简单的图例&#xff0c;效果如下&#xff08;注意&#xff1a;这只是一个简单的例子&#xff0c;不过可以根据这个简单的例子&#xff0c;变化出更为复杂的效果&#xff09;&am…

Yii权限管理工具Srbac使用小结

一、关于Srbac Srbac是基于Yii 框架的 RBAC&#xff08;基于角色的访问控制&#xff09; 插件模块&#xff0c;用于帮助Yii开发人员更方便地进行权限控制&#xff0c;在实际应用过程中也比较方便。 二、安装配置Srbac 在Yii的官方网站的Extensions中已经收录了Srbac插件&#x…

概念concept和requires ---C++ 20

概念concept和requires —C 20 concept concept简化了模板编程的难度 我们可以使用**concept定义模板形参的约束条件T** 模板实力替换T后必须满足std::is_integral_v<C>;为true 例子: requires关键字可以直接约束模板形参T 如下: template <class C> concept …

向DataGridView中添加新的一行数据,可以添加到最后一行或作为第一行

我的开发环境&#xff1a;Microsoft Visual Studio .net 2005 这个程序是Windows Forms Application 新建一个Windows Forms Application项目&#xff0c;打开Form1&#xff0c;在窗体上放一个DataGridView控件和Button,在DataGridView的Columns中添加两列&#xff0c;Name分别…

DataGridView添加一行数据、全选、取消全选、清空数据、删除选中行

.net 2005下的Windows Form Application,一个DataGridView控件和4个Button&#xff0c;界面设置如下&#xff1a; 代码如下&#xff0c;有注解&#xff0c;相信大家都看得明白&#xff1a; using System;using System.Collections.Generic;using System.ComponentModel;using S…

类型萃取类型检查 Type-Traits LibraryType Checks --- C++20

类型萃取:类型检查 Type-Traits Library:Type Checks — C20 Type-Traits library 在C11的时候就已经发布,但依然随着C版本在不断更新 类型检查 Type Checks 每种类型就是十四种主要类型之一 主要类型 template <class T> struct is_void; template <class T>…