C# Winform中绘制动画的方法
最近在做一个图片查看器,由于使用一般的PctureBox,在性能和缩放控制上都无法满足预期的要求,因此所有组件的呈现均是通过重写控件的OnPaint事件来绘制。在查看gif图片时发现Graphics.DrawImage只呈现第一帧,无法满足预期要求,因此经过摸索寻找到了解决自绘gif的较好办法。
这里介绍一个.net自身携带的类ImageAnimator,这个类类似于控制动画的时间轴,使用ImageAnimator.CanAnimate可以判断一个图片是否为动画,调用ImageAnimator.Animate可以开始播放动画,即每经过一帧的时间触发一次OnFrameChanged委托,我们只要在该委托中将Image的活动帧选至下一帧再迫使界面重绘就可以实现动画效果了。
为了方便以后的使用,我将这些代码整合到了一起,形成一个AnimateImage类,该类提供了CanAnimate、FrameCount、CurrentFrame等属性,以及Play()、Stop()、Reset()等动画常用的方法,代码如下
Code
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Drawing;
5using System.Drawing.Imaging;
6
7namespace GifTest
8{
9 /**//// <summary>
10 /// 表示一类带动画功能的图像。
11 /// </summary>
12 public class AnimateImage
13 {
14 Image image;
15 FrameDimension frameDimension;
16 /**//// <summary>
17 /// 动画当前帧发生改变时触发。
18 /// </summary>
19 public event EventHandler<EventArgs> OnFrameChanged;
20
21 /**//// <summary>
22 /// 实例化一个AnimateImage。
23 /// </summary>
24 /// <param name="img">动画图片。</param>
25 public AnimateImage(Image img)
26 {
27 image = img;
28
29 lock (image)
30 {
31 mCanAnimate = ImageAnimator.CanAnimate(image);
32 if (mCanAnimate)
33 {
34 Guid[] guid = image.FrameDimensionsList;
35 frameDimension = new FrameDimension(guid[0]);
36 mFrameCount = image.GetFrameCount(frameDimension);
37 }
38 }
39 }
40
41 bool mCanAnimate;
42 int mFrameCount = 1, mCurrentFrame = 0;
43
44 /**//// <summary>
45 /// 图片。
46 /// </summary>
47 public Image Image
48 {
49 get { return image; }
50 }
51
52 /**//// <summary>
53 /// 是否动画。
54 /// </summary>
55 public bool CanAnimate
56 {
57 get { return mCanAnimate; }
58 }
59
60 /**//// <summary>
61 /// 总帧数。
62 /// </summary>
63 public int FrameCount
64 {
65 get { return mFrameCount; }
66 }
67
68 /**//// <summary>
69 /// 播放的当前帧。
70 /// </summary>
71 public int CurrentFrame
72 {
73 get { return mCurrentFrame; }
74 }
75
76 /**//// <summary>
77 /// 播放这个动画。
78 /// </summary>
79 public void Play()
80 {
81 if (mCanAnimate)
82 {
83 lock (image)
84 {
85 ImageAnimator.Animate(image, new EventHandler(FrameChanged));
86 }
87 }
88 }
89
90 /**//// <summary>
91 /// 停止播放。
92 /// </summary>
93 public void Stop()
94 {
95 if (mCanAnimate)
96 {
97 lock (image)
98 {
99 ImageAnimator.StopAnimate(image, new EventHandler(FrameChanged));
100 }
101 }
102 }
103
104 /**//// <summary>
105 /// 重置动画,使之停止在第0帧位置上。
106 /// </summary>
107 public void Reset()
108 {
109 if (mCanAnimate)
110 {
111 ImageAnimator.StopAnimate(image, new EventHandler(FrameChanged));
112 lock (image)
113 {
114 image.SelectActiveFrame(frameDimension, 0);
115 mCurrentFrame = 0;
116 }
117 }
118 }
119
120 private void FrameChanged(object sender, EventArgs e)
121 {
122 mCurrentFrame = mCurrentFrame + 1 >= mFrameCount ? 0 : mCurrentFrame + 1;
123 lock (image)
124 {
125 image.SelectActiveFrame(frameDimension, mCurrentFrame);
126 }
127 if (OnFrameChanged != null)
128 {
129 OnFrameChanged(image, e);
130 }
131 }
132 }
133}
134
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Drawing;
5using System.Drawing.Imaging;
6
7namespace GifTest
8{
9 /**//// <summary>
10 /// 表示一类带动画功能的图像。
11 /// </summary>
12 public class AnimateImage
13 {
14 Image image;
15 FrameDimension frameDimension;
16 /**//// <summary>
17 /// 动画当前帧发生改变时触发。
18 /// </summary>
19 public event EventHandler<EventArgs> OnFrameChanged;
20
21 /**//// <summary>
22 /// 实例化一个AnimateImage。
23 /// </summary>
24 /// <param name="img">动画图片。</param>
25 public AnimateImage(Image img)
26 {
27 image = img;
28
29 lock (image)
30 {
31 mCanAnimate = ImageAnimator.CanAnimate(image);
32 if (mCanAnimate)
33 {
34 Guid[] guid = image.FrameDimensionsList;
35 frameDimension = new FrameDimension(guid[0]);
36 mFrameCount = image.GetFrameCount(frameDimension);
37 }
38 }
39 }
40
41 bool mCanAnimate;
42 int mFrameCount = 1, mCurrentFrame = 0;
43
44 /**//// <summary>
45 /// 图片。
46 /// </summary>
47 public Image Image
48 {
49 get { return image; }
50 }
51
52 /**//// <summary>
53 /// 是否动画。
54 /// </summary>
55 public bool CanAnimate
56 {
57 get { return mCanAnimate; }
58 }
59
60 /**//// <summary>
61 /// 总帧数。
62 /// </summary>
63 public int FrameCount
64 {
65 get { return mFrameCount; }
66 }
67
68 /**//// <summary>
69 /// 播放的当前帧。
70 /// </summary>
71 public int CurrentFrame
72 {
73 get { return mCurrentFrame; }
74 }
75
76 /**//// <summary>
77 /// 播放这个动画。
78 /// </summary>
79 public void Play()
80 {
81 if (mCanAnimate)
82 {
83 lock (image)
84 {
85 ImageAnimator.Animate(image, new EventHandler(FrameChanged));
86 }
87 }
88 }
89
90 /**//// <summary>
91 /// 停止播放。
92 /// </summary>
93 public void Stop()
94 {
95 if (mCanAnimate)
96 {
97 lock (image)
98 {
99 ImageAnimator.StopAnimate(image, new EventHandler(FrameChanged));
100 }
101 }
102 }
103
104 /**//// <summary>
105 /// 重置动画,使之停止在第0帧位置上。
106 /// </summary>
107 public void Reset()
108 {
109 if (mCanAnimate)
110 {
111 ImageAnimator.StopAnimate(image, new EventHandler(FrameChanged));
112 lock (image)
113 {
114 image.SelectActiveFrame(frameDimension, 0);
115 mCurrentFrame = 0;
116 }
117 }
118 }
119
120 private void FrameChanged(object sender, EventArgs e)
121 {
122 mCurrentFrame = mCurrentFrame + 1 >= mFrameCount ? 0 : mCurrentFrame + 1;
123 lock (image)
124 {
125 image.SelectActiveFrame(frameDimension, mCurrentFrame);
126 }
127 if (OnFrameChanged != null)
128 {
129 OnFrameChanged(image, e);
130 }
131 }
132 }
133}
134
使用如下方法调用:
Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
namespace GifTest
{
public partial class Form1 : Form
{
AnimateImage image;
public Form1()
{
InitializeComponent();
image = new AnimateImage(Image.FromFile(@"C:\Documents and Settings\Administrator\My Documents\My Pictures\未命名.gif"));
image.OnFrameChanged += new EventHandler<EventArgs>(image_OnFrameChanged);
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
void image_OnFrameChanged(object sender, EventArgs e)
{
Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
image.Play();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
lock (image.Image)
{
e.Graphics.DrawImage(image.Image, new Point(0, 0));
}
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text.Equals("Stop"))
{
image.Stop();
button1.Text = "Play";
}
else
{
image.Play();
button1.Text = "Stop";
}
Invalidate();
}
private void button2_Click(object sender, EventArgs e)
{
image.Reset();
button1.Text = "Play";
Invalidate();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
namespace GifTest
{
public partial class Form1 : Form
{
AnimateImage image;
public Form1()
{
InitializeComponent();
image = new AnimateImage(Image.FromFile(@"C:\Documents and Settings\Administrator\My Documents\My Pictures\未命名.gif"));
image.OnFrameChanged += new EventHandler<EventArgs>(image_OnFrameChanged);
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
void image_OnFrameChanged(object sender, EventArgs e)
{
Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
image.Play();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
lock (image.Image)
{
e.Graphics.DrawImage(image.Image, new Point(0, 0));
}
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text.Equals("Stop"))
{
image.Stop();
button1.Text = "Play";
}
else
{
image.Play();
button1.Text = "Stop";
}
Invalidate();
}
private void button2_Click(object sender, EventArgs e)
{
image.Reset();
button1.Text = "Play";
Invalidate();
}
}
}
有点不完美的地方,在Paint事件中,必须锁定Image,否则很容易出现“对象当前正在其他地方使用。”的异常,因为AnimateImage也在使用这个Image对象。如果你有更好的解决办法,欢迎给我留言~~