【IT168 技术文档】
在Windows Form的程序中有的时候需要显示动画,例如连接网络的动态图标。传统的做法可能是做一组BMP表示不同的状态,然后在Form上添加一个定时器,隔若干毫秒,绘制下一幅图片,如此循环。但是只能是BMP,没法自动支持本身有动画效果的Gif格式的图片。
在DotNET中有一个ImageAnimator类,动画处理包含基于时间的帧的图像,这样的在C#中就不需要加载一组图片了,一个Gif轻松搞定,程序也干净很多。
ImageAnimator类
这个类非常简单,主要有几个方法
1)Animator,启动一个多帧的图片,开始动画显示。这里需要制定一个回调函数onFrameChangedHandler,当图片内部间隔时间达到时触发,通常在这个回调函数中刷新界面,显示下一帧图片。特别强调一点,可能ImageAnimator内部用线呈池的定时器检查时间间隔,所以该回调函数触发时,不在程序的主线程中,要注意同步的问题。
2)CanAnimator方法,判断图片是否是动画图片。
3)StopAnimator方法,比较简单,就是停止正在运行的动画。
4)UpdateFrame方法,使该帧在当前正被动画处理的所有图像中前移。新帧在下一次呈现图像时绘制。如(1)所述onFrameChangedHandler不是在主线程中被触发,所以利用这个事件调用UpdateFrame时一定要考虑同步,如果在绘制图片的同时调用UpdateFrame,或者在调用UpdateFrame时绘制图片都会产生资源冲突的异常。所以建议UpdateFrame和绘制图片的动作都在主线程中去执行。
一个完整的示例
显示动画图片的思路是,加载图片,然后调用ImageAnimator.Animator方法,同时指定onFrameChangedHandler毁掉函数;onFrameChangedHandler被触发时重绘界面;在窗口绘制时,调用UpdateFrame,然后绘制图片。代码如下:
代码#region 代码 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Diagnostics; namespace Graphics ......{ /**//**//**//// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form ......{ /**//**//**//// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; private Image m_imgImage = null; private EventHandler m_evthdlAnimator = null; public Form1() ......{ // // Required for Windows Form Designer support // InitializeComponent(); this.SetStyle(ControlStyles.UserPaint,true); this.SetStyle(ControlStyles.DoubleBuffer,true); this.SetStyle(ControlStyles.AllPaintingInWmPaint,true); // // TODO: Add any constructor code after InitializeComponent call // m_evthdlAnimator = new EventHandler(OnImageAnimate); Debug.Assert(m_evthdlAnimator != null); } /**//**//**//// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) ......{ if( disposing ) ......{ if (components != null) ......{ components.Dispose(); } } base.Dispose( disposing ); } protected override void OnPaint(PaintEventArgs e) ......{ base.OnPaint (e); if (m_imgImage != null) ......{ UpdateImage(); // 绘制图片的当前帧 e.Graphics.DrawImage(m_imgImage,new Rectangle(10,10,m_imgImage.Width,m_imgImage.Height)); } } protected override void OnLoad(EventArgs e) ......{ base.OnLoad (e); m_imgImage = Image.FromFile("10.gif"); // 加载测试用的Gif图片 BeginAnimate(); } Windows Form Designer generated codeWindows Form Designer generated code#region Windows Form Designer generated code /**//**//**//// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() ......{ // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(292, 266); this.Name = "Form1"; this.Text = "Form1"; this.Closed += new System.EventHandler(this.Form1_Closed); } #endregion /**//**//**//// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() ......{ Application.Run(new Form1()); } private void Form1_Closed(object sender, System.EventArgs e) ......{ if (m_imgImage != null) ......{ StopAnimate(); m_imgImage = null; } } functions of animator imagefunctions of animator image#region functions of animator image private void BeginAnimate() ......{ if (m_imgImage == null) return; if (ImageAnimator.CanAnimate(m_imgImage)) ......{ ImageAnimator.Animate(m_imgImage,m_evthdlAnimator); } } private void StopAnimate() ......{ if (m_imgImage == null) return; if (ImageAnimator.CanAnimate(m_imgImage)) ......{ ImageAnimator.StopAnimate(m_imgImage,m_evthdlAnimator); } } private void UpdateImage() ......{ if (m_imgImage == null) return; if (ImageAnimator.CanAnimate(m_imgImage)) ......{ ImageAnimator.UpdateFrames(m_imgImage); } } /**//**//**//// <summary> /// 动画图片帧变化的时间触发,这个事件并非在主线程中触发 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnImageAnimate(Object sender,EventArgs e) ......{ this.Invalidate(); } #endregion // End of functions of animator image } } #endregion
最后还有一点注意事项:动画图片的绘制可能会比较频繁,由此造成闪烁,解决办法有两个:
1)不要调用this.Invalid()重绘整个窗口,而应该调用this.Invalid(rect)仅绘制显示图形的区域。
2)指定一下窗口风格,启动窗口的双缓存。
this.SetStyle(ControlStyles.UserPaint,true); this.SetStyle(ControlStyles.DoubleBuffer,true); this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);