编程模型中的更改
以下各节介绍使用 Windows GDI+ 编程与使用 Windows 图形设备接口 (GDI) 编程的几种方法不同。
设备上下文、句柄和图形对象
如果你已使用 GDI (早期版本的 Windows) 中包含的图形设备界面编写程序,则你熟悉设备上下文 (DC) 。 设备上下文是 Windows 用来存储有关特定显示设备功能和属性的信息的结构,这些属性指定如何在该设备上绘制项。 视频显示的设备上下文也与显示器上的特定窗口相关联。 首先, (HDC) 获取设备上下文的句柄,然后将该句柄作为参数传递给实际执行绘图的 GDI 函数。 还可以将句柄作为参数传递给获取或设置设备上下文属性的 GDI 函数。
使用 GDI+时,不必像使用 GDI 时那样关注句柄和设备上下文。 只需创建 Graphics 对象,然后以熟悉的面向对象的样式(myGraphicsObject.DrawLine (参数) )调用其方法。 Graphics 对象位于 GDI+ 的核心,就像设备上下文位于 GDI 的核心一样。 设备上下文和 Graphics 对象扮演类似的角色,但与设备上下文 (GDI) 一起使用的基于句柄的编程模型与用于 Graphics 对象的面向对象的模型之间存在一些根本差异, (GDI+) 。
与设备上下文一样, Graphics 对象与屏幕上的特定窗口相关联,并包含 (属性,例如,平滑模式和文本呈现提示) 指定项目绘制方式。 但是,与设备上下文一样, Graphics 对象不绑定到笔、画笔、路径、图像或字体。 例如,在 GDI 中,必须先调用 SelectObject 以将笔对象与设备上下文关联,然后才能使用设备上下文来绘制线条。 这称为在设备上下文中选择笔。 在设备上下文中绘制的所有线条都将使用该笔,直到选择其他触控笔。 使用 GDI+,可将 Pen 对象作为参数传递给 Graphics 类的 DrawLine 方法。 可以在一系列 DrawLine 调用中的每个中使用不同的 Pen 对象,而无需将给定的 Pen 对象与 Graphics 对象相关联。
绘制线条的两种方法
以下两个示例分别绘制一条宽度为 3 的红线,从位置 (20,10) 到位置 (200,100) 。 第一个示例调用 GDI,第二个示例通过 C++ 类接口调用 GDI+ 。
使用 GDI 绘制线条
若要使用 GDI 绘制线条,需要两个对象:设备上下文和笔。 通过调用 BeginPaint 获取设备上下文的句柄,通过调用 CreatePen 获取笔的句柄。 接下来,调用 SelectObject 以在设备上下文中选择触控笔。 通过调用 MoveToEx 将笔位置设置为 (20, 10) ,然后通过调用 LineTo 从该笔位置绘制一条线到 (200, 100) 。 请注意,MoveToEx 和 LineTo 都接收 hdc 作为参数。
HDC hdc;
PAINTSTRUCT ps;
HPEN hPen;
HPEN hPenOld;
hdc = BeginPaint(hWnd, &ps);
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
hPenOld = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, 20, 10, NULL);
LineTo(hdc, 200, 100);
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
使用 GDI+ 和 C++ 类接口绘制线条
若要使用 GDI+ 和 C++ 类接口绘制线条,需要 一个 Graphics 对象和 一个 Pen 对象。 请注意,你不会要求 Windows 提供这些对象的句柄。 相反,使用构造函数创建 Graphics 类的实例 (Graphics 对象) ,使用 Pen 类的实例 (Pen 对象) 。 绘制线条涉及调用 Graphics 类的 Graphics::D rawLine 方法。 Graphics::D rawLine 方法的第一个参数是指向 Pen 对象的指针。 与将笔选择到设备上下文相比,这是一种更简单、更灵活的方案,如前面的 GDI 示例所示。
HDC hdc;
PAINTSTRUCT ps;
Pen* myPen;
Graphics* myGraphics;
hdc = BeginPaint(hWnd, &ps);
myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics = new Graphics(hdc);
myGraphics->DrawLine(myPen, 20, 10, 200, 100);
delete myGraphics;
delete myPen;
EndPaint(hWnd, &ps);
笔、画笔、路径、图像和字体作为参数
前面的示例表明 ,Pen 对象可以独立于提供绘图方法 的 Graphics 对象创建和维护。 也可以独立于 Graphics 对象创建和维护 Brush、GraphicsPath、Image 和 Font 对象。 Graphics 类提供的许多绘图方法接收 Brush、GraphicsPath、Image 或 Font 对象作为参数。 例如, Brush 对象的地址作为参数传递到 FillRectangle 方法, GraphicsPath 对象的地址作为参数传递到 Graphics::D rawPath 方法。 同样, Image 和 Font 对象的地址将传递给 DrawImage 和 DrawString 方法。 这与 GDI 相反,在 GDI 中,在设备上下文中选择画笔、路径、图像或字体,然后将句柄作为参数传递给绘图函数。
方法重载
许多 GDI+ 方法已重载;也就是说,多个方法具有相同的名称,但具有不同的参数列表。 例如,Graphics 类的 DrawLine 方法采用以下形式:
Status DrawLine(IN const Pen* pen,
IN REAL x1,
IN REAL y1,
IN REAL x2,
IN REAL y2);
Status DrawLine(IN const Pen* pen,
IN const PointF& pt1,
IN const PointF& pt2);
Status DrawLine(IN const Pen* pen,
IN INT x1,
IN INT y1,
IN INT x2,
IN INT y2);
Status DrawLine(IN const Pen* pen,
IN const Point& pt1,
IN const Point& pt2);
上述 四个 DrawLine 变体都会收到指向 Pen 对象的指针、起点的坐标和终点的坐标。 前两个变体以浮点数的形式接收坐标,最后两个变体以整数形式接收坐标。 第一个和第三个变体接收坐标作为四个独立数字的列表,而第二个和第四个变体接收坐标作为 一对 Point (或 PointF) 对象。
不再有当前位置
请注意,在前面显示的 DrawLine 方法中,线条的起点和终点都作为参数接收。 这与 GDI 方案不一致,在 GDI 方案中,调用 MoveToEx 设置当前笔位置后跟 LineTo ,以便绘制一条从 (x1、 y1) 开始,到 (x2、 y2) 结束的线条。 GDI+ 作为一个整体已经放弃了当前位置的概念。
绘制和填充的单独方法
在绘制轮廓和填充矩形等形状的内部时,GDI+ 比 GDI 更灵活。 GDI 具有 一个 Rectangle 函数,该函数在一个步骤中绘制轮廓并填充矩形的内部。 轮廓是使用当前所选的笔绘制的,内部用当前所选的画笔填充。
hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);
GDI+ 有单独的方法来绘制边框和填充矩形的内部。 Graphics 类的 DrawRectangle 方法将 Pen 对象的地址作为其参数之一,FillRectangle 方法将 Brush 对象的地址作为其参数之一。
HatchBrush* myHatchBrush = new HatchBrush(
HatchStyleCross,
Color(255, 0, 255, 0),
Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);
请注意,GDI+ 中的 FillRectangle 和 DrawRectangle 方法接收指定矩形的左边缘、顶部、宽度和高度的参数。 这与 GDIRectangle 函数形成鲜明对比,后者采用指定矩形的左边缘、右边缘、顶部和底部的参数。 另请注意,GDI+ 中 Color 类的构造函数有四个参数。 最后三个参数是通常的红色、绿色和蓝色值;第一个参数是 alpha 值,它指定所绘制的颜色与背景色混合的程度。
构造区域
GDI 提供了多个用于创建区域的函数:CreateRectRgn、CreateEllpticRgn、CreateRoundRectRgn、CreatePolygonRgn 和 CreatePolyPolygonRgn。 你可能希望 GDI+ 中的 Region 类具有类似的构造函数,这些构造函数采用矩形、椭圆形、圆角矩形和多边形作为参数,但情况并非如此。 GDI+ 中的 Region 类提供接收 Rect 对象引用的构造函数和接收 GraphicsPath 对象地址的另一个构造函数。 如果要基于椭圆、圆角矩形或多边形构造区域,可以通过创建包含椭圆的 GraphicsPath 对象 (轻松执行此操作,例如) 然后将该 GraphicsPath 对象的地址传递给 Region 构造函数。
GDI+ 通过组合形状和路径可以轻松形成复杂区域。 Region 类具有 Union 和 Intersect 方法,可用于使用路径或其他区域扩充现有区域。 GDI+ 方案的一个不错功能是 ,将 GraphicsPath 对象作为参数传递给 Region 构造函数时不会销毁它。 在 GDI 中,可以使用 PathToRegion 函数将路径转换为区域,但该路径在此过程中被销毁。 此外, 当 GraphicsPath 对象的地址作为参数传递到 Union 或 Intersect 方法时,不会销毁该对象,因此可以使用给定路径作为多个独立区域的构建基块。 下面的示例说明了这一点。 假设 onePath 是指向 GraphicsPath 对象的指针, (已初始化的简单或复杂) 。
Region region1(rect1);
Region region2(rect2);
region1.Union(onePath);
region2.Intersect(onePath);
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈