日期:2008-03-28 浏览次数:20628 次
侥幸的是当编写一个典型的Windows 窗体程序时,窗体和控件的绘制、效果等操作是不需求特别加以考虑的。这是为什么呢?由于通过使用 .Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件相关联的代码然后在IDE中按F5,一个完完全全的窗体程序就诞生了!所有控件都将本人绘制本人,窗体或者控件的大小和缩放都调整自若。在这里经常会用到的,且需求惹起一点留意的就是控件效果。游戏,自定义图表控件以及屏幕保护程序的编写会需求程序员额外撰写用于呼应 Paint 事件的代码。
本文针对那些Windows 窗体开发人员并有助于他们在使用程序编制过程中使用简单的绘图技术。首先,我们会讨论一些基本的绘图概念。到底谁在担任进行绘制操作?Windows 窗体程序是如何知道何时该进行绘制的?那些绘制代码究竟被放置在哪里?之后,还将引见图像绘制的双重缓冲区技术,你将会看到它是怎样任务的,怎样通过一个方法来实现缓存和实际显示的图像间的交替。最后,我们将会探讨”智能无效区域”,实际就是仅仅重绘或者清除使用程序窗体上的无效部分,加快程序的显示和呼应速度。希望这些概念和技术能够引导读者阅读完本文,并且有助于更快和更无效的开发Windows 窗体程序。
Windows 窗体使用GDI+图像引擎,在本文中的所有绘图代码都会涉及使用托管的.Net 框架来操纵和使用Windows GDI+图像引擎。
虽然本文用于基本的窗体绘图操作,但是它同样提供了快速的、无效的且有助于提高程序功用的技术和方法。所以,在通读本文之前建议读者对.Net框架有个基本的了解,包括Windows 窗体事件处理、简单的GDI+对象譬如Line,Pen和Brush等。熟悉Visual Basic .Net或者C#编程言语。
概念
Windows 使用程序是本人担任绘制的,当一个窗体”不干净”了,也就是说窗体改变了大小,或者部分被其它程序窗体遮盖,或者从最小化形状恢复时,程序都会收到需求绘制的信息。Windows把这种”不干净”形状称为”无效的(Invalidated)”形状,我们理解为:需求重绘,当Windows 窗体程序需求重绘窗体时它会从Windows音讯队列中获取绘制的信息。这个信息经过.Net框架封装然后传递到窗体的 PaintBackground 和 Paint 事件中去,在上述事件中适当的书写专门用于绘制的代码即可。
简单的绘图示例如下:
using System;
using System.Drawing;
using System.Windows.Forms;
public class BasicX : Form
{
public BasicX()
{
InitializeComponent();
}
private void BasicX_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen p = new Pen(Color.Red);
int width = ClientRectangle.Width;
int height= ClientRectangle.Height;
g.DrawLine(p, 0,0, width, height);
g.DrawLine(p, 0, height, width, 0);
p.Dispose();
}
private void InitializeComponent()
{
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.ClientSize = new System.Drawing.Size(300, 300);
this.Text = "BasicX";
this.Paint += new PaintEventHandler(this.BasicX_Paint);
}
[System.STAThreadAttribute()]
public static void Main()
{
Application.Run(new BasicX());
}
}
上述代码分成两个基本的步骤来创建示例程序。首先 InitializeComponent 方法包含一些属性的设置和附加窗体 Paint 事件的处理过程。留意,在方法中控件的款式也同时被设置,设置控件的款式也是自定义Windows 窗体及控件行为的一种无效途径,譬如:控件的"ResizeRedraw"属性指示当窗体的大小变化发生当前需求对其完全进行重绘,也就是说重绘时总是需求对整个窗体的客户区域进行重绘。窗体的“客户区域”是指除了标题栏和边框的所有窗体区域。可以进行一个风趣的试验,取消该控件的属性然后再运转程序,我们可以很明显的看出为什么该属性会被经常的设置,由于窗体调整大小后的无效区域基本不会被重绘。
好了,我们需求留意一下BasicX_Paint方法,正如先前所提到的,Paint 事件在程序需求重绘时被激活,程序窗体利用Paint事件来担任回应需求重绘的系统音讯,BasicX_Paint方法的调用需求一个对象 sender 和一个PaintEventArgs类型的变量,PaintEventArgs类的实例或称之为变量 e 封装了两个重要的数据,第一个就是窗体的 Graphics 对象,该对象表示窗体可绘制的表面也称之为画布用于绘制诸如线、文本以及图像等,第二个数据就是ClipRectangle,该Rectangle对象表示窗体上无效的的矩形范围,或者说就是窗体需求重绘的区域。记住,当窗体的ResizeRedDraw设置后,调整大小后该ClipRectangle的大小实际就等于窗体整个客户区域的大小,或者是被其它程序窗体遮盖的那部分剪切区域。关于部分剪切区域的用途我们会在智能重绘章节作更详细的阐述。
双重缓冲区绘图技术
双重缓冲区技术能够使程序的绘图愈加快速和平滑,无效减少绘制时的图像闪烁。该技术的基本原理是先将图像绘制到内存中的一块画布上,一旦所有的绘制操作都完成了,再将内存中的画布推到窗体的或者控件的表面将其显示出来。通过这种操作后的程序能使用户感觉其愈加快速和美观。
下面提供的示例程序能够阐明双重缓冲区的概念和实现方法,这个示例所包含的功用已相当完整,且完全可以在实际使用中使用。在该章节后面还会提及该技术应该配合控件的一些属性设置才能达到更好的效果。
要想领略双重缓冲区绘图技术所带来的好处就请运转SpiderWeb示例程序吧。程序启动并运转后对窗口大小进行调整,你会发现使用这种绘图算法的效率不高,并且在调整大小的过程中有大量的闪烁出现。
不具备双重缓冲区技术的SpiderWeb示例程序
纵观程序的源码你会发如今程序Paint事件激活后是通过调用LineDrawRoutine方法来实现线的绘制的。LineDrawRoutine方法有两个参数,第一个是Graphics对象是用于绘制线条的地方,第二个是绘图工具Pen对象用来画线条。代码相当简单,一个循环语句,LINEFREQ常量等,程序从窗体表面的左下不断划线到其右上。请留意,程序使用浮点数来计算在窗体上的绘制位置,这样做的好处就是当窗体的大小发生变化时位置数据会愈加精确。
private void LineDrawRoutine(Graphics g, Pen p)
{
float width = ClientRectangle.Width;
float height = ClientRectangle.Height;
float xDelta = width / LINEFREQ;
float yDelta = height /