在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,然后绘制图片。
代码如下:
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 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 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
}
}
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 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 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
}
}
最后还有一点注意事项
动画图片的绘制可能会比较频繁,由此造成闪烁,解决办法有两个:
1)不要调用
this.Invalid()重绘整个窗口,而应该调用
this.Invalid(rect)仅绘制显示图形的区域。
2)指定一下窗口风格,启动窗口的双缓存。
this.SetStyle(ControlStyles.UserPaint,true);
this.SetStyle(ControlStyles.DoubleBuffer,true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
this.SetStyle(ControlStyles.DoubleBuffer,true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
参考文档