silverlight如何在运行时用代码动态控制(或创建)动画

silverlight做一些复杂动画时,不可能所有的动画都事先用Blend之类的设计工具"画"好(或者在设计期就在vs里编好),很多时候我们希望在运行时能动态控制动画,或者凭空动态创建一段动画.

sl3.0的官方sdk文档里有一节"以编程方式使用动画"讲的就是这个,今天研究了下整理分析于此:

对于事先"画"好(或者称之为在设计期准备好的动画),我们可以在运行时通过名字获取动画引用,进而改变某些属性:

1.示例1(代码来自sdk,以下同),运行时动态改变动画的To属性值,从而实现鼠标点击跟随效果

Xaml部分:

Change.Xaml
<UserControl x:Class="AnimationControl.Change"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" >
    <Canvas MouseLeftButtonDown="Handle_MouseDown" Background="DarkSeaGreen" Width="400" Height="300" Cursor="Hand">
        <Canvas.Resources>
            <Storyboard x:Name="myStoryboard">
                <PointAnimation x:Name="myPointAnimation" Storyboard.TargetProperty="Center" Storyboard.TargetName="MyAnimatedEllipseGeometry" Duration="0:0:0.5"/>
            </Storyboard>
        </Canvas.Resources>

        <TextBlock Text="请在圆形之外的空白处点击"  Foreground="White" FontStretch="Normal" FontWeight="Bold" FontSize="18" TextAlignment="Center" Canvas.Left="100" Canvas.Top="130" Cursor="Hand" Opacity="0.5">

        </TextBlock>
        
        <Path Fill="Blue">
            <Path.Data>
                <EllipseGeometry x:Name="MyAnimatedEllipseGeometry" Center="200,100" RadiusX="15" RadiusY="15" />
            </Path.Data>
        </Path>

        
    </Canvas>
</UserControl>

布局很简单,一个Canvas上放了一个圆,并创建了一个动画myPointAnimation

CS部分:

Change.Xaml.Cs
 1using System.Windows;
 2using System.Windows.Controls;
 3using System.Windows.Input;
 4
 5namespace AnimationControl
 6{
 7    public partial class Change : UserControl
 8    {
 9        public Change()
10        {
11            InitializeComponent();
12        }
13
14        private void Handle_MouseDown(object sender, MouseButtonEventArgs e)
15        {
16            //取得鼠标当前在Canvas中的点击坐标
17            double newX = e.GetPosition(sender as UIElement).X;
18            double newY = e.GetPosition(sender as UIElement).Y;
19            Point myPoint = new Point();
20            myPoint.X = newX;
21            myPoint.Y = newY;
22
23            //动态设置动画的To属性值
24            myPointAnimation.To = myPoint;
25
26            //播放
27            myStoryboard.Begin();
28        }
29
30    }
31}
32

代码不长,一看就明,获取鼠标的点击坐标后,赋值为动画myPointAnimation的To属性(即移动后的目标坐标值),然后播放

2.示例2,有时候很多对象可能会引用到同一效果的动画,每个对象都去创建一个动画太浪费,这时候我们可以把类似的动画通过改变TartgetName值得以重用

但有一点要注意:因为同一个动画同一时间只能有一个Target,所以如果给这个动画赋值了TartgetName,并且该动画正在播放的过程中,又用代码给动画的TargetName属性赋值另外一个对象,并要求播放,显示是会失效的。(实际测试中发现,虽然这样不会抛出任何异常)

为避免这种错误的发生,sdk中的示例代码提示我们可以这样做:
Xaml部分:

Code
 1<UserControl x:Class="AnimationControl.Change2"
 2    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6    mc:Ignorable="d"
 7   >
 8
 9    <StackPanel Orientation="Horizontal">
10        <StackPanel.Resources>
11            <Storyboard x:Name="myStoryboard1" Completed="Storyboard_Completed">
12                <DoubleAnimation x:Name="myDoubleAnimation1" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" />
13            </Storyboard>
14            <Storyboard x:Name="myStoryboard2" Completed="Storyboard_Completed">
15                <DoubleAnimation x:Name="myDoubleAnimation2" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" />
16            </Storyboard>
17            <Storyboard x:Name="myStoryboard3" Completed="Storyboard_Completed">
18                <DoubleAnimation x:Name="myDoubleAnimation3" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" />
19            </Storyboard>
20        </StackPanel.Resources>
21        <Rectangle x:Name="MyAnimatedRectangle1" Margin="3" Width="90" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" Cursor="Hand" />
22        <Rectangle x:Name="MyAnimatedRectangle2" Margin="3" Width="90" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" Cursor="Hand" />
23        <Rectangle x:Name="MyAnimatedRectangle3" Margin="3" Width="90" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" Cursor="Hand" />
24        <Rectangle x:Name="MyAnimatedRectangle4" Margin="3" Width="90" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" Cursor="Hand" />
25    </StackPanel>
26    
27</UserControl>
28

StackPanel中横向放了4个矩形,同时放置了三个完全相同的double型动画(用来让对象的透明度从1变到0,即渐渐淡去),实现目的:4个矩形,3个动画,显示按照一一对应的默认原则,总会有一个矩形无法分配到动画,如何实现重用呢?看下面的

cs部分:

Code
 1using System;
 2using System.Windows.Controls;
 3using System.Windows.Input;
 4using System.Windows.Media.Animation;
 5using System.Windows.Shapes;
 6
 7namespace AnimationControl
 8{
 9    public partial class Change2 : UserControl
10    {
11        public Change2()
12        {
13            InitializeComponent();
14        }
15
16        bool storyboard1Active = false;
17        bool storyboard2Active = false;
18        bool storyboard3Active = false;
19            
20        private void Start_Animation(object sender, MouseEventArgs e)
21        {
22            //得到被点击的矩形对象引用
23            Rectangle myRect = (Rectangle)sender;
24
25            if (!storyboard1Active)
26            {
27                myStoryboard1.Stop();
28                myDoubleAnimation1.SetValue(Storyboard.TargetNameProperty, myRect.Name);
29                myStoryboard1.Begin();
30                storyboard1Active = true;
31            }
32            else if (!storyboard2Active)
33            {
34                myStoryboard2.Stop();
35                myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name);
36                myStoryboard2.Begin();
37                storyboard2Active = true;
38            }
39            else if (!storyboard3Active)
40            {
41                myStoryboard3.Stop();
42                myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name);
43                myStoryboard3.Begin();
44                storyboard3Active = true;
45            }
46        }
47
48        private void Storyboard_Completed(object sender, EventArgs e)
49        {
50            Storyboard myStoryboard = sender as Storyboard;
51            switch (myStoryboard.GetValue(NameProperty).ToString())
52            {
53                case "myStoryboard1": storyboard1Active = false; break;
54                case "myStoryboard2": storyboard2Active = false; break;
55                case "myStoryboard3": storyboard3Active = false; break;
56            }
57        }
58
59    }
60}
61

这里注意:定义了三个标识变量,用于标识每个动画是否正在播放中,如果播放完成后该变量为false,否则为true(即正在播放),这个每个矩形上点击请求播放动画时,总是优先找到空闲(即处于播放状态)的动画,然后为该动画赋值TargetName属性并播放,同时播放途中把对应的标识变量改成true,以防止播放过程中被人修改TargetName值

也许有人会问了:如果没找到空闲的动画,不是没效果了?Yes,你猜对了,如果快速依次点击4个矩形,会发现最后一次点击没什么变化。这种情况就要用到下面提到的代码动态创建动画了

3。示例3 代码动态创建动画
理解起来很简单,代码创建动画对象,并让其播放。
xaml部分:

Code
 1<UserControl x:Class="AnimationControl.Create"
 2    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6    mc:Ignorable="d"
 7   >
 8
 9    <Canvas Name="LayoutRoot" Background="DarkOliveGreen" Width="400" Height="300">
10        <TextBlock Text="点击我将动态创建一段动画" MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" Foreground="White" FontStretch="Normal" FontWeight="Bold" FontSize="18" TextAlignment="Center" Canvas.Left="100" Canvas.Top="130" Cursor="Hand" Opacity="0.5">
11            
12        </TextBlock>
13    </Canvas>
14</UserControl>
15

一个几乎是空的Canvas,没啥特别的
再来看cs部分:

Code
 1using System;
 2using System.Windows;
 3using System.Windows.Controls;
 4using System.Windows.Media;
 5using System.Windows.Media.Animation;
 6using System.Windows.Shapes;
 7
 8namespace AnimationControl
 9{
10    public partial class Create : UserControl
11    {
12        public Create()
13        {
14            
15            InitializeComponent();
16           
17        }
18
19        public void CreateAnimation()
20        {
21            //创建一个矩形
22            Rectangle myRectangle = new Rectangle();
23            myRectangle.Width = 50;
24            myRectangle.Height = 50;           
25            myRectangle.Fill = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
26
27            //把矩形加入到Canvas中
28            LayoutRoot.Children.Add(myRectangle);
29
30            //创建二个double型的动画,并设定播放时间为2秒
31            Duration duration = new Duration(TimeSpan.FromSeconds(2));            
32            DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
33            DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
34
35            myDoubleAnimation1.Duration = duration;
36            myDoubleAnimation2.Duration = duration;
37
38            //创建故事版,并加入上面的二个double型动画
39            Storyboard sb = new Storyboard();
40            sb.Duration = duration;
41
42            sb.Children.Add(myDoubleAnimation1);
43            sb.Children.Add(myDoubleAnimation2);
44
45            //设置动画的Target目标值
46            Storyboard.SetTarget(myDoubleAnimation1, myRectangle);
47            Storyboard.SetTarget(myDoubleAnimation2, myRectangle);
48
49            //设置动画的变化属性
50            Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(Canvas.Left)"));
51            Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(Canvas.Top)"));
52
53            myDoubleAnimation1.To = 200;
54            myDoubleAnimation2.To = 200;
55
56            if (!LayoutRoot.Resources.Contains("unique_id"))
57            {
58                //将动画版加入Canvas资源,注意:这里的unique_id必须是资源中没有的唯一键
59                LayoutRoot.Resources.Add("unique_id", sb);
60                sb.Completed += new EventHandler(sb_Completed);
61
62                //播放
63                sb.Begin();
64            }
65            else
66            {
67                sb = null;
68                LayoutRoot.Children.Remove(myRectangle);
69            }
70
71            
72
73        }
74
75        void sb_Completed(object sender, EventArgs e)
76        {
77            LayoutRoot.Resources.Remove("unique_id");//播放完成后,移除资源,否则再次点击时将报错
78        }
79
80        private void TextBlock_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
81        {
82            CreateAnimation();
83        }
84    }
85}
86

几乎所有关键的地方,都加了注释了应该能容易看明白

这里有一点要注意:创建动画的代码,必须放在构造函数中的InitializeComponent()之后调用,原因很简单,如果组件尚未初始化完毕,这时向根容器加入一些动态创建的元件当然会报错。

时间: 2024-09-01 19:30:13

silverlight如何在运行时用代码动态控制(或创建)动画的相关文章

一起谈.NET技术,为什么我支持托管运行时(虚拟机)

最近博客园上在炒关于C#性能的问题,其实应该说是.NET性能的问题,其中某位仁兄提出,他希望C#能够直接编译为原生代码,而不是在CLR这样一个托管运行时上执行,因为虚拟机啊,JIT什么的性能差.后来发到TL上以后,也有朋友认为,"基于虚拟机的语言都是大公司为了利益在推动,说白了就是政治",因此"对C#提高性能的建议感到可笑,因为它本来就不是用来开发高性能程序的",再有,"C.C++已经明确不和这些后进争所谓的'容易开发'的头衔",那么其他语言为什

为什么我支持托管运行时(虚拟机)

最近博客园上在炒关于C#性能的问题,其实应该说是.NET性能的问题,其中某位仁兄提出,他希望C#能够直接编译为原生代码,而不是在CLR这样一个托管运行时上执行,因为虚拟机啊,JIT什么的性能差.后来发到TL上以后,也有朋友认为,"基于虚拟机的语言都是大公司为了利益在推动,说白了就是政治",因此"对C#提高性能的建议感到可笑,因为它本来就不是用来开发高性能程序的",再有,"C.C++已经明确不和这些后进争所谓的'容易开发'的头衔",那么其他语言为什

实现运行时从字符串动态创建对象

创建|动态|对象|字符串 在运行时任意指定对象的创建类型,甚至是用表示类型的名字的字符串创建所需的对象,.net Framwork的反射机制给我们带来了解决问题的方法.这里,若只需要创建一般的对象,我们可以通过System.Activator来实现,而较复杂的我们可以通过获取构造方法来实现. 反射Reflection是.net中重要机制,通过反射,可以在运行时获得.net中每一个类型(包括类.结构.委派.接口.枚举)的成员,包括方法.属性.事件以及构造函数等,还可以获得每个成员的名称.限定符和参

Silverlight代码创建动画

代码中使用了 C# 3.0 语法 效果是一个红色矩形从右下角移动到左上角 仅仅是示例,演示如何在代码中动态创建动画 MainPage.xaml <UserControl x:Class="Hongcing.Silverlight.Create_And_Run_Animation" xmlns="http://schemas. microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://sch

混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该...

今天在把以前写的代码生成工具从原来的.NET3.5升级到.NET4.0,同时准备进一步完善,将程序集都更新后,一运行程序在一处方法调用时报出了一个异常:   混合模式程序集是针对"v2.0.50727"版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集   其调用的方法是从sqlite数据库中获取原来已经使用过的数据库连接,当时也没注意,就是准备设断点然后单步调试,结果竟然是断点无法进入方法体内,后来仔细看了一下方法体的时候发现了一个问题,就是现有的Sy

图片-大神,我这代码运行时为什么出错?

问题描述 大神,我这代码运行时为什么出错? #include #include #include struct sport { char sex,athname[10]; char itemtype,itemname[10]; int itemrank,itemnum,mgrade,wgrade; }ath[2]; struct school { int num; char name[10]; struct sport ath[2]; //int score; }sch[2]; void xue

控制-自己编的程序运行时串口被程序自己占用,如何用代码强制关闭重新连接?

问题描述 自己编的程序运行时串口被程序自己占用,如何用代码强制关闭重新连接? 我用vs2010基于MFC编一个小程序,其中有一部分是用单片机转USB串口与电脑程序相连.现在问题是: 不小心动了Usb线的话,程序会卡住,在想打开串口会显示串口被占用(就是自己的程序占用).需要把USB线拔下来才能解决问题.求大侠指导,能不能用代码控制,按个软件上的button,就能自动连接上com1. 求大侠指导! 解决方案 结束进程再重启看看行不行.首先要确定卡死不是驱动的问题,如果是驱动的问题,程序就无能为力了

c++的问题-c++编译通过,运行时弹出窗口说“该内存不能为written”代码如下

问题描述 c++编译通过,运行时弹出窗口说"该内存不能为written"代码如下 //对一串字符串进行分类. #include #include using namespace std; #define N 256 void getsym(char STR[N],string SYM,string *ID,int *NUM){ char *s; for(int i=0;i<N;i++) //去空格 { if(STR[i]!=' ') { *s=STR[i]; s++; } } f

c++-我的代码编译没有问题,但运行时出现访问冲突:

问题描述 我的代码编译没有问题,但运行时出现访问冲突: 0x00421ab3 处未处理的异常: 0xC0000005: 读取位置 0xfdfdfe05 时发生访问冲突 我检查代码发现他是执行到 p= "^(@|$)"; CRegexpT reg( p,IGNORECASE ); MatchResult match_ret = reg.Match( Buf_Line); 发生的,调用堆栈第一行是 Test.exe!CBuilderT::Clear() 行1658 + 0x3e 字节 C+