新的开始

不以物喜,不以己悲

导航

WinForm中使用GDI+实现滚动动画

  前两天有人问用.NET怎么做类似于心电图的图像控件。我以前也从来没做过,但是感觉应该不是很困难,关键是要知道从哪开始下手。所以当时只是讲了下我的初步想法。

  1.心电图显示的是线,而线则是由多个点组成,所以做这个效果实际上就是画点。

  2.要画点,首先就需要设置参考坐标系,然后根据指定坐标来画点。

  3.静态的点画好之后就要实现动态的效果了,也就是随着时间的推移,将所有的点往一个横向方向上移动,然后在最后面空出地方再补充上新的点。不过这里要注意的是,我们看到的仅仅只是显示区域的点,对于已经移出视野的那些点就可以抛弃了,不然越积越多,内存就要爆了。


  昨天晚上没什么事,就想自己动手实践一下,看看理论与实际之间是否存在很大的差距。

  首先,存放这么多个点需要一个容器,由于显示区域的宽度并没有固定,所以这个容器的容量应该是动态的,比如List<T>。但在初步设想时,我就已经想到了在增加新点的同时,要将不需要的点都去掉,而实现这个功能的首选就是泛型队列(Queue<T>)。当然,如果直接使用它,还是存在三个问题:1.队列是不限制容量的;2.队列不会在容量达到设定的上限时自动弹出多余的元素;3.队列不会自动改变点的横坐标,使点动起来。当然,这些问题都可以使用外部方法解决,但如果能在里面解决,使代码更清晰,不是更好吗?

  基于以上三个原因,我选择了从Queue<T>中派生出一个新类PQueue。对于点,本来在写这篇随便的时候,想简化代码,直接使用System.Drawing.Point结构,后来发现结构是值类型的,在foreach(Point p in PQueue){}的内嵌代码中是不能修改它的属性的,而Queue<T>又没有索引器,连for循环也没法使用。所以最后还是创建了一个类似于Point结构的P类。

public class P
{
public int X { get; set; }
public int Y { get; set; }
}

 


  对于P类,在真正使用的时候,可以对它的功能进行扩展,增加颜色等属性,使图像看起来更加丰富多彩。

  创建好点的类,再创建PQueue类。

代码
public class PQueue : Queue<P>
{
//队列的最大容量
public int MaxCount { get; set; }
//队列是否已装满
private bool isFull = false;

public PQueue(int max)
{
MaxCount
= max;
}

//替换父类的Enqueue方法
new public void Enqueue(P item)
{
if (Count == MaxCount)
{
if (!isFull)
isFull
= true;
else
{
//平移所有点, 图像开始动起来
foreach (P p in this)
p.X
= p.X - 1;
}
}
base.Enqueue(item);
//在添加新元素时弹出超过MaxCount的元素
while (Count > MaxCount)
Dequeue();
}
}

  下面就要开始画图了,不过我没有选择直接操作PictureBox,而是单独建了个类,这样使用起来更加方便。

class Img
public class Img
{
private PQueue pQueue; //要显示的点的队列

public int Width { get; set; } //显示区域的宽度
public int Height { get; set; } //显示区域的高度
public Color BackColor { get; set; } //背景色
public Color PColor { get; set; } //显示的点的颜色
public Image Image { get; private set; } //显示的图像

public Img(int width, int height, Color backcolor, Color pcolor)
{
//设定属性的初始值
Width = width;
Height
= height;
pQueue
= new PQueue(Width);
BackColor
= backcolor;
PColor
= pcolor;
Image
= new Bitmap(Width, Height);
}

/// <summary>
/// 画图
/// </summary>
/// <param name="pheight">要画的点的Y值,它是不变的, 而X值是要随着时间变化的</param>
public void Draw(int pheight)
{
//默认的坐标原点是左上角点, 为了好看, 这里将点翻转了一下
P p=new P(){X = pQueue.Count - 1, Y = Height - pheight +1};
pQueue.Enqueue(p);

Graphics g
= Graphics.FromImage(Image);
g.Clear(BackColor);
using(Pen pen = new Pen(PColor))
{
foreach (P p1 in pQueue)
{
Point point1
= new Point(p1.X, p1.Y);
//一个像素点太小,把它加长一点就能看得更清楚了
Point point2 = new Point(p1.X, p1.Y + 3);
g.DrawLine(pen, point1, point2);
}
}
g.Dispose();
}
}

  画图的功能已经做好了,剩下的工作就是检验了。在使用以下代码前,现在窗体上放个PictureBox控件,命名为pic,宽度和高度都设为50.

代码
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
img
= new Img(pic.Width, pic.Height, Color.Black, Color.White);
System.Timers.Timer timer
= new System.Timers.Timer(50);
timer.Enabled
= true;
timer.Elapsed
+= new System.Timers.ElapsedEventHandler(timer_Elapsed);
}

private Img img;
private int num = 20;
private int i = 1;

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
img.Draw(num);
pic.Image
= img.Image;
num
= num + i;
if (40 == num)
i
= -1;
if (10 == num)
i
= 1;
}
}

  运行一下,最后可以看到一条运动的波浪线。以上代码仅仅只是实现了最简单的动画效果,其实只要在改造一下,就可以弄成像迅雷等下载工具的悬浮窗的速度显示效果了。

  注:以上代码需要以下引用命名空间:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

 

posted on 2010-04-24 00:42  新的开始  阅读(3063)  评论(1编辑  收藏  举报