专栏导航
下一篇:无
本专栏课件
关于本专栏课件的获取方法,请参考下述课节。
参考课节:MFC笔记:本专栏课件
本节前言
对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。
我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。
本节,我们继续来做小的练习题。
本节的代码依然不难。
不过呢,鉴于本节的程序有点长,所以呢,我会将代码放进 Gitee 课件代码仓库里面的。
一. 插图介绍
之前的有一个课节,里面用到了如下的插图。
图1
这个插图出现在下述课节中。
这个插图呢,它主要的部分,是一个圆角矩形。
不过呢,它还有两个辅助部分,一个辅助部分,就是圆角矩形所在的矩形。圆角矩形,我们用实线、蓝色线条风格的画笔来绘制。
这个矩形,我们在绘制的时候,我要用虚线来绘制。那就是需要用自定义画笔,并且将这个画笔设置为 PS_DASH 风格。颜色的话,不一定要与图1 一致,你可以自己选定一个颜色。我设定的颜色是 RGB(255, 0, 255) 。
第二个辅助部分,就是四个圆角所在的椭圆了。它们呢,需要用虚线来绘制。我在自己的程序里,我是创建了一个自定义画笔,并将画笔设置为 PS_DASH、红色线条风格。
那么,在具体绘制的时候,我呢,首先是绘制矩形,然后是绘制四个辅助椭圆,最后是绘制圆角矩形。
有一个问题,如果你直接这么按次序来绘制的话,那么,在程序绘制完圆角矩形的时候,内部的四个小椭圆就会消失了。那是因为,默认地,我们会用白色画刷来填充圆角矩形的内部。就在这个填充的过程中,之前绘制的四个小椭圆,它的大部分的线条,就被白色画刷给涂抹掉了。
为了避免默认的白色画刷将四个小椭圆给涂抹掉,我们呢,需要在绘制圆角矩形之前,给设备环境选入一个 NULL_BRUSH 画刷。有了这个 NULL_BRUSH 画刷,Windows 就不会用某个画刷来填充圆角矩形的内部,而是直接沿用原来的背景颜色。
基本的绘制思路,我们就讲解完了。
接下来,我们在下一节,来展示程序代码。
二. 本节程序
代码如下。
/*------------------------------------------------------------
RoundRect 函数讲解插图
水饺编程,2026
------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("RoundRect_Teach");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("注册窗口类失败!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, // window class name
TEXT("RoundRect 绘图讲解图示"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static int xLeft, xRight, yTop, yBottom;
static int xCornerEllipse, yCornerEllipse;
COLORREF color;
HPEN hPen, hOldPen;
HBRUSH hBrush, hOldBrush;
switch (message)
{
case WM_CREATE:
xLeft = 100;
xRight = 600;
yTop = 100;
yBottom = 500;
xCornerEllipse = 160;
yCornerEllipse = 120;
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
color = RGB(255, 0, 255);
hPen = CreatePen(PS_DASH, 0, color);
hOldPen = (HPEN)SelectObject(hdc, hPen);
Rectangle(hdc, xLeft, yTop, xRight, yBottom);
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
color = RGB(255, 0, 0);
hPen = CreatePen(PS_DASH, 0, color);
hOldPen = (HPEN)SelectObject(hdc, hPen);
Ellipse(hdc, xLeft, yTop,
xLeft + xCornerEllipse, yTop + yCornerEllipse);
Ellipse(hdc, xRight - xCornerEllipse, yTop,
xRight, yTop + yCornerEllipse);
Ellipse(hdc, xLeft, yBottom - yCornerEllipse,
xLeft + xCornerEllipse, yBottom);
Ellipse(hdc, xRight - xCornerEllipse, yBottom - yCornerEllipse,
xRight, yBottom);
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
color = RGB(0, 0, 255);
hPen = CreatePen(PS_SOLID, 0, color);
hOldPen = (HPEN)SelectObject(hdc, hPen);
RoundRect(hdc, xLeft, yTop, xRight, yBottom,
xCornerEllipse, yCornerEllipse);
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
SelectObject(hdc, hOldBrush);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
代码本身不难,不过呢,我还是得提醒两句。
第一点,对于 static ,有它在,我们才可以在 WM_CREATE 消息中初始化一些个变量,而在 WM_PAINT 消息中使用 WM_CREATE 消息中的初始化值。
不同的消息,是处在不同的窗口过程函数 WinProc 的调用之中的。要想在不同的 WinProc 函数的调用中使用同一个变量的值,则需要将相关变量设置为 static 属性。
第二点,在本程序中,画刷我们使用了一次,并且使用的是备用画刷 NULL_BRUSH 。备用画刷也好,备用画笔也好,用完了,需要将其选出设备环境,但是不能删除它。而对于自定义画笔和自定义画刷,则需要在用完以后,选出设备环境,然后再删除它。
三. 程序编译与运行
我将解决方案和项目名称,设为 RoundRect_Teach 。源文件的名称,我将其设置为 WinMain.cpp 。
关于如何编译和运行程序,请参考下述课节。
参考课节:使用 VS2010 编写 C语言程序
参考课节:用 VS2019 编写C语言程序
程序的运行结果如下。
图2
可以看到,图2 与图1 并不完全一致。区别主要在于画笔的颜色。
结束语
程序不难。
本节,依然是以让大家熟悉 GDI 对象的使用流程为目的。
同时呢,大家也可以有一种经验,对于某些个,你用 PhotoShop 和 Visio 软件不容易绘制的图,或许,你可以考虑用编程的方式来绘制它。
程序员可以用编程的方式来绘图,也可以学习 PhotoShop 来绘图。
编程有编程的方便。
专栏导航
下一篇:无