问题描述:
在Windows Phone开发时候,可能存在这样的问题:
某一个控件需要一个特定的展现(这里假定是一个特定动画),那么我们会这么解决这个问题呢?
打开Blend,根据需求需求给控件添加动画,Building,Runing,任务完成。
过了一段时间,在另外一个地方,同样的控件需要同样的动画展现。
此时会有两个做法:
(1).到之前已经完成实现的.xaml中,Copy xaml代码,粘贴到当下需要它的地方,然后做适当修改之后即可使用。
(2).对此需求进行封装,使用UserControl或者自定义控件都可以实现。
显然第一种方法必须坚决杜绝,而要使用第二种方法,让代码可复用,易维护。
现在介绍第三种方法,使用AnimationHelper
简介:
AnimationHelper其实只是使用XamlReader加载xaml,生成Storyboard,然后执行.
如何使用:
1.使用Blend创建我们需要的Storyboard
Storyboard的XAML描述如下:
(显然以上的XAML描述的Storyboard是我们通过Blend创建的满足需求的Storyboard,为了方便维护和重用,我们把它复制到AnimationHelper中)
2.创建AnimationHelper类,定义相关方法
a).添加方法,返回值为Storyboard。(如果需要控制动画的执行时间,可以通过参数传入,对XAML稍作修改,通过string.Format拼接到正确的地方)
1 public class AnimationHelper 2 { 3 public static Storyboard BtnAnimation(UIElement uiElement) 4 { 5 Storyboard sb = null; 6 7 // Storyboard的XAML描述 8 string templateString = string.Format(@" 9 XAML描述"); 10 11 // 如果涉及到形状变换,则UIElement的RenderTransform属性需要被赋值, 12 // 为了避免在前段XAML忘记定义RenderTransform,所以在这里每一次 13 // 都创建RenderTransform 14 uiElement.RenderTransform = new CompositeTransform(); 15 16 // 设置动画锚点 17 uiElement.RenderTransformOrigin = new Point(0.5, 0.5); 18 19 // 加载XAML 20 sb = XamlReader.Load(templateString) as Storyboard; 21 22 // 为Storyboard的Children设置作用对象 23 foreach (var t in sb.Children) 24 Storyboard.SetTarget(t, uiElement); 25 26 return sb; 27 } 28 }
b).复制Blend生成的Storyboard,替换上述代码中的"XAML描述"
注意:
(下面把Blend生成的XAML称作原XAML)
<1>把原XAML中的Storyboard.TargetName="xxxx"部分去掉。因为我们将通过方法参数出入Target
<2>正确解析双引号。(在原XAML中,每逢碰到双引号,就在之前再加一个双引号)
<3>加入XAML名字空间。解析XAML时,需要知道它的名字空间,所以我们需要在原XAML中加入它的名字空间。把xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""加入到Storyboard标签中作为其属性
1 public static Storyboard BtnAnimation(UIElement uiElement) 2 { 3 Storyboard sb = null; 4 5 // Storyboard的XAML描述 6 string templateString = string.Format(@" 7 <Storyboard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" 8 Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.ScaleX)""> 9 <DoubleAnimationUsingKeyFrames> 10 <EasingDoubleKeyFrame KeyTime=""0:0:0.2"" Value=""0.5""> 11 <EasingDoubleKeyFrame.EasingFunction> 12 <CubicEase EasingMode=""EaseIn""/> 13 </EasingDoubleKeyFrame.EasingFunction> 14 </EasingDoubleKeyFrame> 15 <EasingDoubleKeyFrame KeyTime=""0:0:0.4"" Value=""1""> 16 <EasingDoubleKeyFrame.EasingFunction> 17 <QuinticEase EasingMode=""EaseOut""/> 18 </EasingDoubleKeyFrame.EasingFunction> 19 </EasingDoubleKeyFrame> 20 </DoubleAnimationUsingKeyFrames> 21 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(CompositeTransform.ScaleY)""> 22 <EasingDoubleKeyFrame KeyTime=""0:0:0.2"" Value=""0.5""> 23 <EasingDoubleKeyFrame.EasingFunction> 24 <CubicEase EasingMode=""EaseIn""/> 25 </EasingDoubleKeyFrame.EasingFunction> 26 </EasingDoubleKeyFrame> 27 <EasingDoubleKeyFrame KeyTime=""0:0:0.4"" Value=""1""> 28 <EasingDoubleKeyFrame.EasingFunction> 29 <QuinticEase EasingMode=""EaseOut""/> 30 </EasingDoubleKeyFrame.EasingFunction> 31 </EasingDoubleKeyFrame> 32 </DoubleAnimationUsingKeyFrames> 33 </Storyboard>"); 34 35 // 如果涉及到形状变换,则UIElement的RenderTransform属性需要被赋值, 36 // 为了避免在前段XAML忘记定义RenderTransform,所以在这里每一次 37 // 都创建RenderTransform 38 uiElement.RenderTransform = new CompositeTransform(); 39 40 // 设置动画锚点 41 uiElement.RenderTransformOrigin = new Point(0.5, 0.5); 42 43 // 加载XAML 44 sb = XamlReader.Load(templateString) as Storyboard; 45 46 // 为Storyboard的Children设置作用对象 47 foreach (var t in sb.Children) 48 Storyboard.SetTarget(t, uiElement); 49 50 return sb; 51 }
3.使用AnimationHelper
在我们需要执行的地方,调用刚定义的方法,传入UI元素
1 void button2_Click(object sender, RoutedEventArgs e) 2 { 3 var sb = AnimationHelper.BtnAnimation(this.button2); 4 if (sb != null) 5 { 6 sb.Begin(); 7 } 8 }
AnimationHelper优劣分析:
优势:
1.折中的管理零散的Storyboard,无需封装控件,把Storyboard紧紧耦合在控件内,复用简单。
不足:
1.把UI逻辑引入了业务逻辑中,违背了MVVM模式设计理念。
2.对于复杂的Storyboard则不太适用(因为双引号太多... 哈哈)
3.调试困难。(那里漏了一个双引号则很难发现)
4.使用XamlReader实时解析XAML,效率较低
总结:
从优劣分析中可以看出,不足之处多余优点,所以使用这种方法的时候需要慎重考虑,但是如果用对地方或者用法巧妙也许能规避此解决方案的不足而得到出奇的效果
Demo下载:
The End.