简介
这次的项目需求,需要做一个可以开关门的动画控件,并且可以旋转,而且要不能覆盖到背后的图
八年前学习Win32SDK的时候,自己就不是很喜欢用GDI来画图
这次就选择WPF来做!
C# Code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Forms; using System.Threading; using System.Windows.Media.Animation; namespace AnimationDoor { /// <summary> /// Interaction logic for UserControl1.xaml /// </summary> public partial class AnimationDoor : System.Windows.Controls.UserControl { bool isOpened; System.Windows.Forms.Timer timer; Brush doorBrush; double angle; const int AnimationDuration = 300; public AnimationDoor() { InitializeComponent(); } public double DoorAngle { get { return angle; } set { angle = value; TransformGroup tg = new TransformGroup(); RotateTransform rotate = new RotateTransform(angle); rotate.CenterX = this.Width / 2; rotate.CenterY = this.Height / 2; tg.Children.Add(rotate); this.RenderTransform = tg; } } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); // change door height leftdoor.Height = sizeInfo.NewSize.Height; rightdoor.Height = sizeInfo.NewSize.Height; // change door width leftdoor.Width = sizeInfo.NewSize.Width / 2; rightdoor.Width = sizeInfo.NewSize.Width / 2; // margin Thickness tkleft = leftdoor.Margin; tkleft.Right = sizeInfo.NewSize.Width / 2; leftdoor.Margin = tkleft; // right door Thickness tkright = rightdoor.Margin; tkright.Left = sizeInfo.NewSize.Width / 2; rightdoor.Margin = tkright; } public Brush DoorColor { get { return doorBrush; } set { doorBrush = value; leftdoor.Fill = doorBrush; leftdoor.Stroke = doorBrush; rightdoor.Fill = doorBrush; rightdoor.Stroke = doorBrush; } } public bool DoorOpened { get { return isOpened; } set { isOpened = value; this.Dispatcher.BeginInvoke(new MethodInvoker(StartAnimiation), null); } } void StartAnimiation() { DoubleAnimation daleft = new DoubleAnimation(); daleft.Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration)); daleft.RepeatBehavior = new RepeatBehavior(1); daleft.AutoReverse = false; Thickness tkleft = leftdoor.Margin; ThicknessAnimation taleft = new ThicknessAnimation(); taleft.From = tkleft; taleft.Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration)); DoubleAnimation daright = new DoubleAnimation(); daright.Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration)); daright.RepeatBehavior = new RepeatBehavior(1); daright.AutoReverse = false; Thickness tkright = rightdoor.Margin; ThicknessAnimation taright = new ThicknessAnimation(); taright.From = tkright; taright.Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration)); if (isOpened) { daleft.From = this.leftdoor.Width; daleft.To = this.Width / 4; tkleft.Right = (this.Width / 4) * 3; taleft.To = tkleft; daright.From = this.leftdoor.Width; daright.To = this.Width / 4; tkright.Left = this.Width / 2 + (this.Width / 4); taright.To = tkright; } else { daleft.From = this.leftdoor.Width; daleft.To = this.Width / 2; tkleft.Right = (this.Width / 2); taleft.To = tkleft; daright.From = this.leftdoor.Width; daright.To = this.Width / 2; tkright.Left = (this.Width / 2); taright.To = tkright; } Storyboard sbleft = new Storyboard(); Storyboard.SetTargetProperty(taleft, new PropertyPath(Rectangle.MarginProperty)); sbleft.Children.Add(taleft); Storyboard.SetTargetProperty(daleft, new PropertyPath(Rectangle.WidthProperty)); sbleft.Children.Add(daleft); sbleft.Begin(leftdoor); Storyboard sbright = new Storyboard(); Storyboard.SetTargetProperty(taright, new PropertyPath(Rectangle.MarginProperty)); sbright.Children.Add(taright); Storyboard.SetTargetProperty(daright, new PropertyPath(Rectangle.WidthProperty)); sbright.Children.Add(daright); sbright.Begin(rightdoor); } } }
控件的旋转,我声明了一个DoorAngle属性,利用WPF的RenderTransform属性来旋转控件
开关门的动画部分,我同时使用了DoubleAnimation和ThinknessAnimation来达成门的开关效果
如果只是利用DoubleAnimation来控制Rectangle的Width的话,控件是会往中间缩小
所以要搭配ThinknessAnimation,修改Margin,让门在开启的时候固定在一个位置
使用到了两个动画,那就必须要使用StoryBoard來處理了
this.Dispatcher.BeginInvoke(new MethodInvoker(StartAnimiation), null);
我声明了一个DoorOpened属性,只要透过这个属性,就可以设定门的开关,并同时播放动画
但是由于项目的数据,会放在一个执行序中过更新的动作
所以透过Dispatcher呼叫StartAnimation,才可以正常运作
XAML
<UserControl x:Class="AnimationDoor.AnimationDoor" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="15" Width="160" Background="Transparent"> <Grid Background="Transparent"> <Rectangle Margin="0,0,80,0" Name="leftdoor" Fill="Blue" Stroke="Blue" Height="15" VerticalAlignment="Top" Width="80" ForceCursor="False"> </Rectangle> <Rectangle Margin="80,0,0,0" Name="rightdoor" Fill="Blue" Stroke="Blue" Height="15" VerticalAlignment="Top" Width="80"> </Rectangle> </Grid> </UserControl>
画面上,我声明了leftdoor和rightdoor, 利用两个Rectangle来做一个简单的门