1.8 GDI+编程

1.8.1 介绍GDI+

公共语言运行库使用Windows图形设备接口(GDI)的高级版本,其名称为GDI+。GDI+旨在提供良好的性能和易用性。它支持二维图形、版式和图像。

新的二维功能包括下列内容。

  • 对所有图形基元的Alpha混合支持。
  • 消除锯齿。
  • 渐变填充和纹理填充。
  • 宽线。
  • 基数样条。
  • 可缩放区域。
  • 浮点坐标。
  • 复合线。
  • 嵌入钢笔。
  • 高质量的筛选和缩放。
  • 大量的线型和笔尖选项。

图像支持包括下列内容。

  • 对图像文件格式(如.jpeg、.png、.gif、.bmp、.tiff、.exif和.icon)的本机支持。
  • 用于编码和解码任意光栅图像格式的公共接口。
  • 用于动态添加新图像文件格式的可扩展结构。
  • 对通用点操作(如亮度、对比度、色彩平衡、模糊和溶解)的本机图像处理支持。对通用转换(如旋转、裁切等)的支持。

颜色管理包括对sRGB、ICM2和sRGB64的支持。

版式支持包括下列内容。

  • 本机ClearType支持。
  • 纹理填充和渐变填充的文本。
  • 在所有平台上对Unicode的完全支持。
  • 对所有Windows 2000脚本的支持。
  • Unicode 3.0标准的更新。
  • 文本行服务支持,可使文本更具可读性。

GDI+可与Windows窗体和Web窗体一起使用。例如,Web窗体控件可使用基于用户输入的GDI+动态生成.jpeg文件,并从Web页引用它。

本节将集中讨论Windows窗体。

1.8.2 GDI和GDI+之间的差异

本节将讲解使用GDI+的基础知识。开始之前,有必要指出GDI和GDI+之间的最大差异。GDI具有有状态的编程模型,而GDI+具有无状态的编程模型。使用GDI,可在绘图表面上设置属性(如前景色和背景色),然后在它上面进行绘制。例如,若要绘制黑色文本字符串,代码类似于下面的示例。

Graphics g ;
g.ForeColor = Color.Black;
g.BackColor = Color.White;
g.Font = new Font("Times New Roman",26);
g.DrawString("Hello,World",0,0);

使用GDI+时,始终将要使用的属性作为绘图命令的一部分传递。上面的代码更改为如下所示:

Graphics g ;
Color foreColor = Color.Black;
Color backColor = Color.White;
Font font = new Font("Times New Roman",26);
g.FillRectangle(new SolidBrush(backColor),ClientRectangle);
g.DrawString("Hello World",font,new SolidBrush(foreColor),15,15);

1.8.3 GDI+命名空间

GDI+类驻留于System.Drawing、System.Drawing.Drawing2D、System.Drawing.Imaging和System. Drawing.Text命名空间中。这些命名空间包含在程序集System.Drawing.DLL中。

1.8.4 创建图形对象

GDI+绘图表面由Graphics类表示。为了使用GDI+,首先需要一个对图形对象的引用。通常,在控件或窗体的Paint事件中或在PrintDocument的PrintPage事件中获取对图形对象的引用。

可以通过为Paint事件创建事件处理程序来处理该事件。代码如下所示:

public class GdiPlusDemo : Form {
   public GdiPlusDemo () {
      //Hook the paint event of the form
      this.Paint += new PaintEventHandler(form1_Paint);
   }
   private void form1_Paint(object sender,PaintEventArgs pe) {
      Graphics g = pe.Graphics;
      //Simply fill a rectangle with red
      g.FillRectangle(new SolidBrush(Color.Red),40,40,140,140);
   }
}

更常用的是创建OnPaint方法的子类,并重写该方法。代码如下所示:

public class GdiPlusDemo : Form {
   public GdiPlusDemo () {
   }
   protected override void OnPaint(PaintEventArgs pe) {
       Graphics g = pe.Graphics;
       g.FillRectangle(new SolidBrush(Color.Red),40,40,140,140);
   }
}

还可以从Image的任何派生类创建Graphics对象实例。代码如下所示:

Bitmap newBitmap = new Bitmap(600,400,PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBitmap);
g.FillRectangle(new SolidBrush(Color.Red),40,40,140,140);
newBitmap.Save("c:\\temp\\TestImage.jpg",ImageFormat.Jpeg) ;

创建Graphics对象后,可以使用它绘制线、填充形状、绘制文本等。与Graphics对象一起使用的主要对象如下表所示:

1.8.5 Alpha混合

颜色可以是Alpha混合的,这使得易于创建基于颜色叠加的效果。例如,下面的示例绘制一个红色矩形,然后在不遮掩底层的红色矩形的情况下叠加一个黄色矩形。代码如下所示:

Graphics g = pe.Graphics;
g.FillRectangle(new SolidBrush(Color.Red),600,350,100,100);
g.FillRectangle(new SolidBrush(Color.FromArgb(180,Color.Yellow)),650,400,100,100);

1.8.6 使用画笔

使用画笔填充形状和路径的内部。例如,若要创建红色矩形,请参见下面的示例。

Graphics g = pe.Graphics;
g.FillRectangle(new SolidBrush(Color.Red),600,350,100,100);

画笔可以是纯色的、带阴影的、带纹理的或渐变的。阴影画笔只不过是使用图案进行绘画的画笔。代码如下所示:

Graphics g = pe.Graphics;
HatchBrush hb = new HatchBrush(HatchStyle.ForwardDiagonal,Color.Green,Color.FromArgb(100,Color.Yellow));
g.FillEllipse(hb,250,10,100,100);

带纹理的画笔使用位图绘画。例如,下面的代码使用纹理画笔绘制窗体的背景,然后对它应用白色“冲洗”以冲淡位图中颜色的色调。代码如下所示:

Graphics g = pe.Graphics;
Image colorbars = new Bitmap("colorbars.jpg");
Brush backgroundBrush = new TextureBrush(colorbars);
g.FillRectangle(backgroundBrush,ClientRectangle);
g.FillRectangle(new SolidBrush(Color.FromArgb(180,Color.White)),ClientRectangle);

LinearGradientBrush使用两种颜色绘画。它根据在画笔上设置的属性填充给定的形状,逐渐从一种颜色过渡到另一种颜色。例如,可以用下面所示代码创建下列填充效果如图1-19所示。

Rectangle r = new Rectangle(500,300,100,100);
LinearGradientBrush lb = new LinearGradientBrush(r,Color.Red,Color.Yellow,LinearGradientMode.BackwardDiagonal);
e.Graphics.FillRectangle(lb,r);

PathGradient画笔允许创建更复杂的效果,如图1-20所示。代码如下所示:

图1-19 填充效果

图1-20 填充效果

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(backgroundBrush,ClientRectangle);
   e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(180,Color. White)),ClientRectangle);
   GraphicsPath path = new GraphicsPath(new Point[] {
      New Point(40,140),
      New Point(275,200),
      New Point(105,225),
      New Point(190,300),
      New Point(50,350),
      New Point(20,180),
      },new byte[] {
         (byte)PathPointType.Start,
         (byte)PathPointType.Bezier,
         (byte)PathPointType.Bezier,
         (byte)PathPointType.Bezier,
         (byte)PathPointType.Line,
         (byte)PathPointType.Line,
         });
   PathGradientBrush pgb = new PathGradientBrush(path);
   Pgb.SurroundColors = new Color[] {
      Color.Green,
      Color.Yellow,
      Color.Red,
      Color.Blue,
      Color.Orange,
      Color.White,
   };
   e.Graphics.FillPath(pgb,path);
}

1.8.7 使用钢笔

使用钢笔绘制直线和曲线。可以设置属性,如PenType、DashStyle、Width、Color和EndCap,以控制Pen如何进行绘制。

下面的代码绘制一条曲线。

Graphics g;
//Create a pen 20 pixels wide that is and purple and partially transparent
Pen penExample = new Pen(Color.FromArgb(150,Color.Purple),20);
//Make it a dashed pen
Pen penExample.DashStyle = DashStyle.Dash;
//Make the ends round
Pen penExample.StartCap = LineCap.Round;
Pen penExample.EndCap = LineCap.Round;
//Now draw a curve using the pen
g.DrawCurve(penExample,new Point[] {
          new Point(200,140),
          new Point(700,240),
          new Point(500,340),
          new Point(140,140),
          new Point(40,340),
});

还可以使用带纹理的画笔将纹理用做钢笔的填充效果。代码如下所示:

Graphics g;
Brush textureBrush = new TextureBrush(new Bitmap("Boiling Point.jpg"));
Pen penExample = new Pen(textureBrush,25);
penExample.DashStyle = DashStyle.DashDotDot;
penExample.StartCap = LineCap.Triangle;
penExample.EndCap = LineCap.Round;
g.DrawLine(penExample,10,450,550,400);

1.8.8 绘制文本

Graphics对象的DrawString方法在绘图表面上呈现文本。将要使用的字体和颜色传递给DrawString方法。例如,下面的代码使用窗体的字体和黑色画笔显示文本“Hello World”。

Protected override void OnPaint(PaintEventArgs e) {
   Graphics g = e.Graphics;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   g.DrawString("Hello World",this.Font,new SolidBrush(Color.Black),10,10);
}

因为DrawString采用画笔,所以可以使用任何画笔呈现文本。其中包括纹理画笔。例如,下面的代码呈现具有大理石效果和背景阴影的文本。

Protected override void OnPaint(PaintEventArgs e) {
   TextureBrush titleBrush = new TextureBrush(new Bitmap("marble.jpg"));
   TextureBrush backgroundBrush = new TextureBrush(new Bitmap("colorbars.jpg"));
   SolidBrush titleShadowBrush = new SolidBrush(Color.FromArgb(70,Color.Black));
   Font titleFont = new Font("Lucida Sans Unicode",60);
   String titleText = "Graphics Samples";
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(backgroundBrush,ClientRectangle);
   e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(180,Color. White)),ClientRectangle);
   e.Graphics.DrawString(titleText,titleFont,titleShadowBrush,15,15);
   e.Graphics.DrawString(titleText,titleFont,titleBrush,10,10);
}

如果提供带有Rectangle的DrawString,则文本将换行以适合该矩形。代码如下所示:

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   Font textFont = new Font("Lucida Sans Unicode",12);
   RectangleF rectangle = new RectangleF(100,100,250,350);
   e.Graphics.FillRectangle(new SolidBrush(Color.Gainsboro),rectangle);
   string flowedText="Simplicity and power: Windows Forms is a programming model for\n"
      + "developing Windows applications that combines the simplicity of the Visual\n"
      + "Basic 6.0 programming model with the power and flexibility of the common\n"
      + "language runtime.\n"
      + "Lower total cost of ownership: Windows Forms takes advantage of the versioning
                 and\n"
      + "deployment features of the common language runtime to offer reduced deployment\n"+ "costs and higher application robustness over time. This significantly lowers
                 the\n"
      + "maintenance costs (TCO) for applications\n"
      + "written in Windows Forms.\n"
      + "Architecture for controls: Windows Forms offers an architecture for\n"
      + "controls and control containers that is based on concrete implementation of the\n"
      + "control and container classes. This significantly reduces\n"
      + "control-container interoperability issues.\n";
   e.Graphics.DrawString(flowedText,textFont,new SolidBrush(Color.Blue),rectangle);

可以使用StringFormat对象控制如何绘制文本。例如,下面的代码使得可以在特定区域内绘制居中的文本。

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   Font textFont = new Font("Lucida Sans Unicode",8);
   RectangleF rectangle = new RectangleF(100,100,250,350);
   e.Graphics.FillRectangle(new SolidBrush(Color.Gainsboro),rectangle);
   StringFormat format = new StringFormat();
   Format.Alignment=StringAlignment.Center;
   String flowedText="Simplicity and power: Windows Forms is a programming model for\n"
      + "developing Windows applications that combines the simplicity of the Visual\n"
      + "Basic 6.0 programming model with the power and flexibility of the common\n"
      + "language runtime.\n"
      + "Lower total cost of ownership: Windows Forms takes advantage of the versioning
                 and\n"
      + "deployment features of the common language runtime to offer reduced deployment\n"
      + "costs and higher application robustness over time. This significantly lowers the\n"
      + "maintenance costs (TCO) for applications\n"
      + "written in Windows Forms.\n"
      + "Architecture for controls: Windows Forms offers an architecture for\n"
      + "controls and control containers that is based on concrete implementation of the\n"
      + "control and container classes. This significantly reduces\n"
      + "control-container interoperability issues.\n";
  e.Graphics.DrawString(flowedText,textFont,new SolidBrush(Color.Blue),rectangle,format);
}

如果要在绘制字符串时确定该字符串的长度,可使用MeasureString。例如,若要将某个字符串在窗体上居中显示,应该使用下面的代码。

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   string textToDraw="Hello Symetrical World";
   Font textFont = new Font("Lucida Sans Unicode",8);
   Float windowCenter=this.DisplayRectangle.Width/2;
   SizeF stringSize=e.Graphics.MeasureString(textToDraw,textFont);
   Float startPos=windowCenter-(stringSize.Width/2);
   e.Graphics.DrawString(textToDraw,textFont,new SolidBrush(Color.Blue),startPos,40);
}

MeasureString还可用于确定呈现多少行和多少个字符。例如,可以确定在上一个讨论过的文本示例中呈现多少行和多少个字符。

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   Font textFont = new Font("Lucida Sans Unicode",12);
   RectangleF rectangle = new RectangleF(100,100,250,350);
   Int lines;
   Int characters;
   String flowedText="Simplicity and power: Windows Forms is a programming model for\n"
      + "developing Windows applications that combines the simplicity of the Visual\n"
      + "Basic 6.0 programming model with the power and flexibility of the common\n"
      + "language runtime.\n"
      + "Lower total cost of ownership: Windows Forms takes advantage of the versioning and\n"
      + "deployment features of the common language runtime to offer reduced deployment\n"
      + "costs and higher application robustness over time. This significantly lowers the\n"
      + "maintenance costs (TCO) for applications\n"
      + "written in Windows Forms.\n"
      + "Architecture for controls: Windows Forms offers an architecture for\n"
      + "controls and control containers that is based on concrete implementation of the\n"
      + "control and container classes. This significantly reduces\n"
      + "control-container interoperability issues.\n";
   string whatRenderedText;
   e.Graphics.FillRectangle(new SolidBrush(Color.Gainsboro),rectangle);
   e.Graphics.MeasureString(flowedText,textFont,rectangle.Size,
   new StringFormat(),out characters,out lines);
   whatRenderedText="We printed " + characters + " characters and " + lines+ " lines";
   e.Graphics.DrawString(flowedText,textFont,new SolidBrush(Color.Blue),rectangle);
   e.Graphics.DrawString(whatRenderedText,this.Font,new SolidBrush(Color.Black),10,10);
}

GDI+完全支持Unicode。这意味着可以用任何语言呈现文本。例如,下面的代码用日语绘制字符串(必须安装有日语语言包)。

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliased;
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   try {
      Font japaneseFont = new Font("MS Mincho",32);
      String japaneseText = new string(new char[] {(char)31169,(char)12398,(char)
      21517,
      (char)21069,(char)12399,(char)12463,(char)12522,(char) 12473,
      (char)12391,(char)12377,(char)12290});
      e.Graphics.DrawString(japaneseText,japaneseFont,new SolidBrush(Color.Blue),20,40);
   } catch (Exception ex) {
       MessageBox.Show("You need to install the Japanese language pack to run this sample");
       Application.Exit();
   }
}

1.8.9 使用图像

GDI+完全支持各种图像格式,包括.jpeg、.png、.gif、.bmp、.tiff、.exif和.icon文件。

呈现图像非常简单。例如,下面的代码呈现一个.jpeg图像。

Protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.FillRectangle(new SolidBrush(Color.White),ClientRectangle);
   e.Graphics.DrawImage(new Bitmap("sample.jpg"),29,20,283,212);
}

使用位图作为呈现图像也非常简单。下面的代码将一些文本和线呈现到位图上,然后将该位图作为.png文件保存到磁盘中。

Bitmap newBitmap = new Bitmap(800,600,PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBitmap);
g.FillRectangle(new SolidBrush(Color.White),new Rectangle(0,0,800,600));
Font textFont = new Font("Lucida Sans Unicode",12);
RectangleF rectangle = new RectangleF(100,100,250,350);
Int lines;
Int characters;
String flowedText="Simplicity and power: Windows Forms is a programming model for\n"
   + "developing Windows applications that combines the simplicity of the Visual\n"
   + "Basic 6.0 programming model with the power and flexibility of the common\n"
   + "language runtime.\n"
   + "Lower total cost of ownership: Windows Forms takes advantage of the versioning and\n"
   + "deployment features of the common language runtime to offer reduced deployment\n"
   + "costs and higher application robustness over time. This significantly lowers the\n"
   + "maintenance costs (TCO) for applications\n"
   + "written in Windows Forms.\n"
   + "Architecture for controls: Windows Forms offers an architecture for\n"
   + "controls and control containers that is based on concrete implementation of the\n"
   + "control and container classes. This significantly reduces\n"
   + "control-container interoperability issues.\n";
g.FillRectangle(new SolidBrush(Color.Gainsboro),rectangle);
g.DrawString(flowedText,textFont,new SolidBrush(Color.Blue),rectangle);
Pen penExample = new Pen(Color.FromArgb(150,Color.Purple),20);
penExample.DashStyle = DashStyle.Dash;
penExample.StartCap = LineCap.Round;
penExample.EndCap = LineCap.Round;
g.DrawCurve(penExample,new Point[] {
          new Point(200,140),
          new Point(700,240),
          new Point(500,340),
          new Point(140,140),
          new Point(40,340),
});
newBitmap.Save("TestImage.png",ImageFormat.Png) ;

1.8.10 其他信息

1.标准钢笔和画笔

Pen和Brush类包括一组用于所有已知颜色的标准纯色钢笔和画笔。

2.消除锯齿

将Graphics.SmoothingMode设置为SmoothingMode.AntiAlias将可显示更锐化的文本和图形。

3.图形对象的范围

Paint事件的参数(PaintEventArgs)中包含的图形对象根据Paint事件处理程序的返回结果进行处置。因此,不应保持对范围超出Paint事件之外的此Graphics对象的引用。尝试在Paint事件之外使用此Graphics对象将出现不可预知的结果。

4.处理调整大小

默认情况下,当调整控件或窗体大小时不引发Paint事件。如果希望在调整窗体大小时引发Paint事件,则需要相应地设置控件样式。代码如下所示:

Public MyForm() {
   SetStyle(ControlStyles.ResizeRedraw,true);
}