- Windows程序设计与架构
- 蔺华 汤春林 蔡兴旺编著
- 4222字
- 2020-08-28 17:44:33
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); }