使用GDI+进行绘图

GDI+由.NET基类集组成,这些基类可用于在屏幕上完成定制绘图,能把合适的指令发送到图形设备的驱动程序上,确保在监视器屏幕上显示正确的输出(或打印到硬拷贝中)。

一、理解绘图规则

1、 GDI和GDI+

一般来说,Windows的一个优点(实际上是现代操作系统的一个优点)是它可以让开发人员不考虑特定设备的细节,例如:不需要理解硬盘设备驱动程序,只需在相关的.NET类中调用合适的方法(以前是等价的Windows API 函数),就可以编程读写磁盘上的文件。这些规则也适用于绘图,计算机在屏幕绘图时,把指令发送给视频卡,问题是市面上有几百种不同的视频卡,大多数有不同的指令集和功能,如果把这个考虑在内,在应用程序中为每一个视频卡驱动程序编写在屏幕上绘图的特定代码,这样的应用程序就根本不可能编写出来。这就是为什么在Windows最早期的版本中就有Windows Graphical Device Interface(GDI)的原因。

GDI+提供了一个抽象层,隐藏了不同视频卡之间的区别,这样就可以调用Windows API函数完成指定的任务了,GDI会在内部指出在运行特定的代码时,如何让客户机的视频卡完成要绘制的图形,GDI还可以完成其他任务,大多数计算机都有多个显示设备,例如:监视器和打印机,GDI成功的使应用程序所使用的打印机看起来和屏幕一样,如果答应某些东西,而不是显示它们,只需告诉系统输出的设备是打印机,再用相同的方式调用相同的Windows API函数即可。

可以看出,DC(设备环境)是一个功能非常强大的对象,在GDI下,所有的绘图工作都必须通过设备环境来完成。DC甚至可用于不涉及在屏幕或其他硬件设备上绘图的其他操作,例如在内存中修改图像。

GDI给开发人员提供了一个相当高级的API,但它仍是一个基于旧的Windows API并且有C语言风格的API,所以使用起来很是不方便,GDI+在很大程度上是GDI和应用程序之间的一层,提供了更直观、基于继承性的对象模型,尽管GDI+基本上是GDI的一个包装器,但Microsoft已经能通过GDI+提供新功能了,并宣称它有一些性能上的改进。

2、 GDI+基类的主要命名空间

System.Drawing

包含与基本绘图功能有关的大多数类、结构、枚举和委托

System.Drawing.Drawing2D

为大多数高级2D和矢量绘图操作提供了支持,包括消除锯齿、几何转换、和图像路径。

System.Drawing.Imaging

帮助处理图像(位图、GIF文件等)的各种类

System.Drawing.Printing

把打印机或打印预览窗口作为输出设备时使用的类

System.Drawing.Design

一些预定义的对话框、属性表和其他用户界面元素,与在设计期间拓展用户界面相关。

System.Drawing.Text

对字体和字体系列执行更高级操作的类

二、下面用几个示例说明如何在应用程序中绘图

1、 使用VS2005新建一个Windows窗体,并在启动时在构造函数中绘制它。

启动VS2005,创建一个Windows应用程序,首先把窗体背景色设置为白色,把这行代码放在InitializeComponent()方法中,这样VS2005就会识别该命令,并改变窗体的设计视图的外观。展开Form1.cs文件旁边的加好,就可以看到Form1.Designer.cs文件,在这个文件中,包含了InitializeComponent()方法。现修改如下:

  
    
private void InitializeComponent()
{
this .SuspendLayout();
//
// Form1
//
this .AutoScrollMargin = new System.Drawing.Size( 5 , 13 );
this .BackColor = System.Drawing.Color.White;
this .ClientSize = new System.Drawing.Size( 292 , 266 );
this .Name = " Form1 " ;
this .Text = " DrawShapes Sample " ;
this .ResumeLayout( false );
}

接着给Form1构造函数添加代码,使用窗体的CreateGraphics()方法创建一个Graphics对象这个对象包含绘图时需要使用的Windows设备环境,创建的设备环境与显示设备相关,也和这个窗口相关。

 

  
    
public Form1()
{
InitializeComponent();
Graphics dc
= this .CreateGraphics();
this .Show();
Pen bluePen
= new Pen(Color.Blue, 3 );
dc.DrawRectangle(bluePen,
0 , 0 , 50 , 50 );
Pen redPen
= new Pen(Color.Red, 1 );
dc.DrawEllipse(redPen,
0 , 50 , 80 , 60 );
}

运行程序即可得到绘制的一个矩形和一个椭圆。用鼠标拖动窗口到任何位置,绘制的矩形和椭圆均正常显示,但是如果你最小化该窗体,或者一个新的Windows窗体遮盖住该窗体的一部分,均会发现窗体不能正常显示或者是显示不够完全,该问题的原因是Windows通常会在一个窗口全部或部分被隐藏后就会立即删除与其中显示相关的所有信息,这是必须的,否则储存屏幕数据的内存量会是个天文数字,一般计算机运行时,视频卡的显示是1024X768像素,24位彩色模式,这表示屏幕上的每个像素占3个字节,整个屏幕要用2.25MB,如果用户打开10—20个窗口,就会占用45MB以上,这就会达到或超出显卡的内存,很显然Windows系统得及时释放并收回内存。

如果我们想在最小化之后,恢复显示图像,我们就得重新绘制图形,下例我们将实现这一功能。

2、  使用OnPaint()绘制图形

上面的解释让你觉得绘制自己的 用户界面是比较复杂的,实际并非如此,让应用程序在需要时绘制自身是非常简单的。

Windows会利用Paint事件通知应用程序完成一些重新绘制的要求,有趣的是,Form类已经执行了这个事件的处理程序,因此不需要再添加      处理程序了,Paint事件的Form1处理程序处理虚方法OnPaint()的调用,并给它传递一个参数PaintEventArgs,

这表示我们只需重写OnPaint()执行绘图操作。

Form1类中添加如下代码:

  
    
protected override void OnPaint( PaintEventArgs e)
{
base .OnPaint(e);
Graphics dc
= e.Graphics;
Pen bluePen
= new Pen(Color.Blue, 3 );
dc.DrawRectangle(bluePen,
0 , 0 , 50 , 50 );
Pen redPen
= new Pen(Color.Red, 2 );
dc.DrawEllipse(redPen,
0 , 50 , 80 , 60 );
}

现在,不管你是最小化窗体还是遮盖后都能正常显示绘图内容了。

上面的示例说明了在窗口中绘图的主要规则,但是它并不是很高效,原因是它试图绘制窗口中的所有内容,而没有考虑到需要绘制多少内容,我们可以只绘制未显示的图像不管它是全部还是部分,我们在GDI+中称未显示的部分为剪切区域,设备环境指导这个区域的内容,它截取在这个区域外部的绘图操作,且不把相关的绘图命令传送给图形卡,可以检查绘图区域以及进行哪些绘图工作,例如我们改进上述代码为:

  
    
protected override void OnPaint(PaintEventArgs e)
{
base .OnPaint(e);
Graphics dc
= e.Graphics;
if (e.ClipRectangle.Top < 132 && e.ClipRectangle.Left < 82 )
{

Pen bluePen
= new Pen(Color.Blue, 3 );
dc.DrawRectangle(bluePen,
0 , 0 , 50 , 50 );
Pen redPen
= new Pen(Color.Red, 2 );
dc.DrawEllipse(redPen,
0 , 50 , 80 , 60 );
}
}

首先,需要获得剪切区域的信息。这需要使用PaintEventArgs的另一个属性。这个属性叫做ClipRectangle,包含要重绘区域的坐标,并包装在一个结构实例System.Drawing. Rectangle中。Rectangle是一个相当简单的结构,包含4个属性:Top、Bottom、Left和 Right。它们分别包含矩形的上下的垂直坐标、左右的水平坐标。

接着,需要确定进行什么测试,以决定是否进行绘制。这里进行一个简单的测试。注意,在绘图过程中,矩形和椭圆完全包含在(0,0)到(80,130)的矩形客户区域中,实际上,点(82,132)就已经在安全区域中了,因为线条大约偏离这个区域一个像素。所以我们要看看剪切区域的左上角是否在这个矩形区域内。如果是,就重新绘制,如果不是,就不必麻烦了。

下面是代码:

      protected override void OnPaint( PaintEventArgs e )
{
base.OnPaint(e);
Graphics dc = e.Graphics;
         if (e.ClipRectangle.Top < 132 && e.ClipRectangle.Left < 82)
{
Pen bluePen = new Pen(Color.Blue, 3);
dc.DrawRectangle(bluePen, 0,0,50,50);
Pen redPen = new Pen(Color.Red, 2);
dc.DrawEllipse(redPen, 0, 50, 80, 60);
}
}

注意,显示的结果与以前显示的结果完全相同-- 但这次进行了早期测试,确定了哪些区域不需要绘制,提高了性能。还要注意这个是否进行绘图的测试是非常粗略的。还可以进行更精细的测试,确定矩形或者椭圆是否要重新绘制。这里有一个平衡。可以在OnPaint()中进行更复杂的测试,以提高性能,也可以使OnPaint()代码复杂一些。进行一些测试总是值得的,因为编写一些代码,可以更多地了解除Graphics实例之外的绘制内容,Graphics实例只是盲目地执行绘图命令。

From:http://www.cnblogs.com/cardgames/articles/1400876.html


你可能感兴趣的:(DI)