中年人学C语言Windows程序设计,15窗口绘画:矩形绘制函数的应用

1. 绘制矩形

以前,我们是使用 Rectangle 函数绘制矩形,然后封闭图形是自带填充功能的,默认是填充白色,跟客户区的背景颜色一样。在绘制之前,通过 SelectObject 函数将画刷选入设备环境,即可设置矩形的颜色。如果不想让矩形填充背景,也可以,用 GetStockObject 函数获得一个叫 NULL_BRUSH 或者 HOLLOW_BRUSH 的空画刷即可。

Rectangle

函数功能:

Rectangle 函数用于绘制一个矩形。

该矩形用当前画笔绘制轮廓,用当前画刷填充。

在这里插入图片描述

API 函数原型:

注释:In 说明该参数是输入的。

BOOL Rectangle(
  _In_  HDC hdc,
  _In_  int xLeft,
  _In_  int yTop,
  _In_  int xRight,
  _In_  int yBottom
);

参数解析:

参数 含义
hdc 指定设备环境句柄
xLeft 指定矩形左上角的逻辑 x 坐标
yTop 指定矩形左上角的逻辑 y 坐标
xRight 指定矩形右下角的逻辑 x 坐标
yBottom 指定矩形右下角的逻辑 y 坐标

返回值:

  1. 如果函数调用成功,返回值是非 0;

  2. 如果函数调用失败,返回值是 0。

备注:

  1. 该函数既不使用当前位置,也不修改当前位置。

  2. 写过图形程序的程序员通常熟悉边界偏差(off-by-one)的问题:一些图形程序系统画出的图形包含了右坐标和底坐标表示的点,一些则只画到右坐标和底坐标表示的点之前的一点。Windows 使用后一种方法。

  3. 如果使用 PS_NULL 画笔,则矩形的尺寸高和宽比实际少一个像素。

2. 处理矩形

由于矩形在 Windows 中太常见了,基本上你看到的所有东西背后都是一个个矩形(不要跟我说垃圾桶它不是矩形,它只是背景部分使用了透明的颜色,如果你用鼠标选中它你就可以看到选中的是一个矩形的框框)。

因此,Windows 提供了一些便捷的函数让你创建矩形以及可以轻松的修改它们:

FillRect 函数用于绘制并填充矩形
FrameRect 函数用于绘制矩形边框
InvertRect 函数用于反转矩形内的像素
SetRect 函数用于设置矩形的范围及尺寸
OffsetRect 函数用于移动指定的矩形
InflateRect 函数用于缩放矩形
SetRectEmpty 函数用于将矩形各个坐标设置为 0
CopyRect 函数用于将一个矩形的坐标拷贝到另一个矩形中
IntersectRect 函数用于获得两个矩形的交集部分
UnionRect 函数用于获得两个矩形的并集部分
IsRectEmpty 函数用于判断一个矩形是否为空矩形
PtInRect 函数用于判断一个点是否在矩形中

实例演示

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    回调函数
    参数:
    hwnd : 窗口句柄
    message : 消息ID
    wParam和lParam:消息参数
        //int i;
        //size_t j;
    HDC hdc;//定义设备环境句柄  
    static TCHAR  szBuffer[128];
    //TEXTMETRIC tm;
   // SCROLLINFO si;
    static POINT pt;
    PAINTSTRUCT ps;
    static RECT rect,rect2;
    size_t iStrLength;
    static int cxChar, cyChar, cxClient, cyClient;
    HBRUSH hNewBrush, hOldBrush;
    switch (message)
    {
        case WM_SIZE://窗体大小改变
            hdc = GetDC(hWnd);
            //GetClientRect(hwnd, &rect);
            cxClient = LOWORD(lParam);//当前x像素
            cyClient = HIWORD(lParam);//当前y像素
            StringCchPrintf(szBuffer, 128, TEXT("矩形绘画函数应用"));
            StringCchLength(szBuffer, 128, &iStrLength);
            SetTextAlign(hdc, TA_CENTER | TA_TOP);
            TextOut(hdc, cxClient / 2, 0, szBuffer, iStrLength);
            ReleaseDC(hWnd, hdc);
            return 0;


        case WM_LBUTTONDOWN:
            pt.x = LOWORD(lParam);
            pt.y = HIWORD(lParam);
            if (PtInRect(&rect2, pt))
            {
                MessageBox(hWnd, TEXT("鼠标提示,你点中了矩形2"), TEXT("消息框"),MB_OKCANCEL);
            }
            return 0;

        case WM_PAINT://窗口绘画:点
        {
            //int x, xLast;
            HDC hdc = BeginPaint(hWnd, &ps);//函数为指定窗口进行绘画作准备,并用将和绘画有关的信息填充到一个 PAINTSTRUCT 结构中。
            hNewBrush = CreateSolidBrush(RGB(255, 0, 0));
            hOldBrush = SelectObject(hdc, hNewBrush);
            Rectangle(hdc, 50, 50, 200, 150);
           

            rect.left = cxClient/ 4;
            rect.top = cyClient/ 4;
            rect.right = cxClient / 2;
            rect.bottom = cyClient / 2;
            FillRect(hdc, &rect, hNewBrush);
            InvertRect(hdc, &rect);

            SetRect(&rect2, rect2.left = cxClient * 5 / 8, rect2.top=cyClient/4,\
                rect2.right = cxClient * 7 / 8, rect2.bottom = cyClient / 2);
            
            FrameRect(hdc, &rect2, hNewBrush);
            
            DeleteObject(SelectObject(hdc, hOldBrush));
            EndPaint(hWnd, &ps);
            return 0;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    return 0;
}

在这里插入图片描述

点击rect2 区域内 ,弹出消息框
在这里插入图片描述

PeekMessage

函数功能:

PeekMessage 函数检查线程的消息队列并获取消息(如果存在的话)。

API 函数原型:

BOOL WINAPI PeekMessage(
  _Out_    LPMSG lpMsg,
  _In_opt_ HWND  hWnd,
  _In_     UINT  wMsgFilterMin,
  _In_     UINT  wMsgFilterMax,
  _In_     UINT  wRemoveMsg
);

参数解析:

参数 含义
lpMsg 指向 MSG 结构的指针,用于存放获取到的消息
hWnd 1. 需要获取消息的窗口的句柄,该窗口必须属于当前线程
2. 当其值是 NULL 时,将获取所有的当前线程的窗口消息和线程消息
3. 当其值是 -1 时,只获取当前线程消息
wMsgFilterMin 指定被可以被获取的消息值的最小整数(消息其实就是一个被定义的整数)
wMsgFilterMax 指定被可以被获取的消息值的最小整数
wRemoveMsg 指定消息如何被处理(该值可以是下边一个或多个组合):
值 含义
PM_NOREMOVE 消息被获取后不从消息队列中删除
PM_REMOVE 消息被获取后并从消息队列中删除
PM_NOYIELD 1. 防止系统释放任何正在等待被调用的线程
2. 跟 PM_NOREMOVE 或 PM_REMOVE 相结合使用

默认设置下处理所有类型的消息,若要求只处理某些消息,则指定下列一个或多个组合:
值 含义
PM_QS_INPUT 处理鼠标和键盘消息
PM_QS_PAINT 处理绘图消息
PM_QS_POSTMESSAGE 处理所有 posted 的消息,包括计时器和快捷键消息
PM_QS_SENDMESSAGE 处理所有 send 的消息

返回值:

  1. 如果获取到有效的消息,返回值是非 0;

  2. 如果获取不到消息,返回值是 0。

备注:

  1. 如果 hWnd 参数不为零,则 PeekMessage 函数只获取属于 hWnd 窗口的消息,以及被 IsChild 函数确定为 hWnd 的子窗口的消息;

  2. wMsgFilterMin 和 wMsgFilterMax 参数限定消息获取的范围,如果二者都为 0,则消息获取的范围为所有消息;

  3. 注意,即便你通过 wMsgFilterMin 和 wMsgFilterMax 参数限定消息获取的范围,但 WM_QUIT 不受此范围影响(无法阻止此消息被 PeekMessage 函数获取);

  4. 调用此函数的过程中,如果接收到非排队消息 —— 也就是由该线程调用 SendMessage, SendMessageCallback, SendMessageTimeout, 或 SendNotifyMessage 发送给所属窗口的消息 —— 系统先暂时挂起等待,然后再获取过滤器匹配的第一个队列消息。如果不指定过滤器,那么按照以下顺序获取消息:

Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages

  1. PeekMessage 函数通常不会从消息队列中删除 WM_PAINT 消息,WM_PAINT 会持续保留在消息队列中直到它们被处理,除非一个 WM_PAINT 消息指定的区域为 NULL。

小甲鱼注释:大家应该还记得有三个消息 WM_QUIT, WM_PAINT, WM_TIMER 会被特殊处理,它们总是放在队列的最后面,直到没有其它消息的时候才被处理,连续的 WM_PAINT 消息将被合并成一个以提高绘图效率。

  1. 如果一个顶层窗口停止响应的消息超过几秒钟,系统认为窗口没有响应,并使用一个备用窗口替代,该窗口具有相同的 Z 序列、位置、尺寸和视觉属性。这使得用户可以移动、调整大小,甚至关闭该应用程序。然而,这些仅仅是动作,现有的程序事实上仍然没有响应(为了提高用户体验而制作的假象)。当一个程序被调试时,系统并不会为其生成备用窗口。

代码实例

如果什么都不做,则产生随机矩形,填充client区域的下半区


#define _CRT_SECURE_NO_WARNINGS 1
/* -------------------------------------------------------------------
                   中年人学C语言Windows程序设计
--------------------------------------------------------------------*/

#include <windows.h>
#include<strsafe.h>
int cxClient, cyClient;

//#include"sysmets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawRectangle(HWND hWnd);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
//主函数
// 四个参数:
//hInstance : 程序当前实例的句柄(handle to current instance),以后随时可以用GetModuleHandle(0)来获得
//hPrevInstance : 前一个实例的句柄(handle to previous instance),在Win32中,每一个进程都有一个独立的4G地址空间,从0到2G属于进程私有,对其他进程来说是不可见的。所以,在Win32中,hPrevInstance总是为NULL。
//szCmdLine : 指向以 / 0结尾的命令行,不包括EXE本身的文件名(pointer to command line),以后随时可以用GetCommandLine()来获取完整的命令行。
//iCmdShow : 指明应该以什么方式显示主窗口(show state of window)。

{
    static TCHAR szAppName[] = TEXT("MyWindows");
    HWND hwnd;//句柄
    MSG msg;//消息
    WNDCLASS wndclass;//定义窗口类结构
    HBRUSH hCulorBrush = CreateSolidBrush(RGB(200, 200, 200));//设定背景颜色

    //结构成员:
    wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口类型
    wndclass.lpfnWndProc = WndProc;  //窗口过程(必须是回调函数)
    wndclass.cbClsExtra = 0;//预留的额外空间,一般为0
    wndclass.cbWndExtra = 0;//预留的额外空间,一般为0
    wndclass.hInstance = hInstance;//应用程序的实例句柄
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//为所有基于该窗口类的窗口设定一个图标
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//为所有基于该窗口类的窗口设定一个鼠标指针
    //wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);//指定窗口背景色
    wndclass.hbrBackground = hCulorBrush;//指定窗口背景色
    wndclass.lpszMenuName = NULL; //指定窗口菜单
    wndclass.lpszClassName = szAppName; //指定窗口类名

    //注册窗口类
    if (!RegisterClass(&wndclass))
        /*  RegisterClass:注册在随后调用CreateWindow函数和CreateWindowEx函数中使用的窗口类。
          参数为lpWndClass,指向一个WNDCLASS结构的指针
          返回值:ATOM的宏定义
          typedef WORD ATOM;
          typedef unsigned short WORD;
          关于RegisterClassEx:参数lpwcx指向一个WNDCLASSEX结构的指针如果函数成功,返回这个窗口类型的标识号;
          如果函数失败,返回值为0。若想获得更多错误信息,请调用GetLastError函数。*/
    {
        MessageBox(NULL, TEXT("这个程序需要在Windows系统执行!"), szAppName, MB_ICONERROR);
        return 0;
    }
    //实例化创建窗口
    hwnd = CreateWindow(szAppName,// 窗口类名称
        TEXT("中年人学C语言Windows程序设计"),// 窗口标题
        WS_OVERLAPPEDWINDOW | WS_VSCROLL,// 窗口风格,或称窗口格式
        CW_USEDEFAULT,// 初始 x 坐标
        CW_USEDEFAULT, // 初始 y 坐标
        CW_USEDEFAULT,// 初始 x 方向尺寸
        CW_USEDEFAULT,// 初始 y 方向尺寸
        NULL,// 父窗口句柄
        NULL,// 窗口菜单句柄
        hInstance,// 程序实例句柄
        NULL);// 创建参数

    /*_In_说明该参数是输入的,_opt_说明该参数是可选参数
        函数成功返回窗口句柄,否则返回NULL*/

        //显示窗口
    ShowWindow(hwnd, iCmdShow);
    /* 第一次调用时应使用WinMain的参数nCmdShow作为参数
         如果窗口之前可见,则返回非0否则返回0*/

         //更新窗口
    UpdateWindow(hwnd);
    /* 绕过消息队列直接向窗口过程发送WM_PAINT消息
         函数调用成功返回非0*/

         //消息循环
    /*while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }*/
    while (TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))//1. 如果获取到有效的消息,返回值是非 0;2. 如果获取不到消息,返回值是 0。
        {
            if (msg.message == WM_QUIT)//如果关系窗口则跳出循环
            {
                break;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else//如果是空闲态,则绘画随机矩形
        {
            DrawRectangle(hwnd);
        }
    }
    /*作用:从当前线程的消息队列里取出一个消息并放入MSG结构中,不能获得其他线程的消息
        若消息队列为空,函数会一直等待到有消息到来才有返回值
        返回值:
        函数出现错误则返回 - 1,
        获得WM_QUIT消息返回0
        否则返回非0*/
    return msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    回调函数
    参数:
    hwnd : 窗口句柄
    message : 消息ID
    wParam和lParam:消息参数
        //int i;
        //size_t j;
    HDC hdc;//定义设备环境句柄  
    static TCHAR  szBuffer[128];
    //TEXTMETRIC tm;
   // SCROLLINFO si;
    static POINT pt;
    PAINTSTRUCT ps;
    static RECT rect,rect2,rectRand;
    size_t iStrLength;
    //static int cxChar, cyChar, cxClient, cyClient;
    HBRUSH hNewBrush, hOldBrush;
    switch (message)
    {
        case WM_SIZE://窗体大小改变
            hdc = GetDC(hWnd);
            //GetClientRect(hwnd, &rect);
            cxClient = LOWORD(lParam);//当前x像素
            cyClient = HIWORD(lParam);//当前y像素
            StringCchPrintf(szBuffer, 128, TEXT("矩形绘画函数应用"));
            StringCchLength(szBuffer, 128, &iStrLength);
            SetTextAlign(hdc, TA_CENTER | TA_TOP);
            TextOut(hdc, cxClient / 2, 0, szBuffer, iStrLength);
            ReleaseDC(hWnd, hdc);
            return 0;


        case WM_LBUTTONDOWN:
            pt.x = LOWORD(lParam);//获取鼠标坐标
            pt.y = HIWORD(lParam);
            if (PtInRect(&rect2, pt))//判断鼠标点击是否在RECT2内部
            {
                MessageBox(hWnd, TEXT("鼠标提示,你点中了矩形2"), TEXT("消息框"),MB_OKCANCEL);
            }
            return 0;

        case WM_PAINT://窗口绘画:点
        {
            //int x, xLast;
            HDC hdc = BeginPaint(hWnd, &ps);//函数为指定窗口进行绘画作准备,并用将和绘画有关的信息填充到一个 PAINTSTRUCT 结构中。
            hNewBrush = CreateSolidBrush(RGB(255, 0, 0));
            hOldBrush = SelectObject(hdc, hNewBrush);
            Rectangle(hdc, 50, 50, 200, 150);
            SelectObject(hdc, hOldBrush);

            rect.left = cxClient/ 4;
            rect.top = cyClient/ 4;
            rect.right = cxClient / 2;
            rect.bottom = cyClient / 2;
            FillRect(hdc, &rect, hNewBrush);//绘制矩形
            InvertRect(hdc, &rect);//对矩形颜色取反

            SetRect(&rect2, rect2.left = cxClient * 5 / 8, rect2.top=cyClient/4,\
                rect2.right = cxClient * 7 / 8, rect2.bottom = cyClient / 2);//设置矩形坐标
            
            FrameRect(hdc, &rect2, hNewBrush);//画一个无填充的矩形

            //绘制一个黑色矩形里面呈现
            hNewBrush = CreateSolidBrush(RGB(0, 0, 0));
            SetRect(&rectRand, rectRand.left = 0, rectRand.top = cyClient / 2, \
                rectRand.right = cxClient, rectRand.bottom = cyClient );//设置矩形坐标
            FillRect(hdc, &rectRand, hNewBrush);//画一个矩形           
            DeleteObject(hNewBrush);
            EndPaint(hWnd, &ps);
            return 0;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    return 0;
}

void DrawRectangle(HWND hWnd)
{
    HBRUSH hBrush;
    RECT rect;
    HDC hdc = GetDC(hWnd); 
    int xLeft = rand() % cxClient;
    int xRight = rand() % cxClient;
    int yTop = rand() % cyClient/2+ cyClient / 2;
    int yBottom = rand() % cyClient/2+ cyClient / 2;
    if (cxClient == 0 || cyClient == 0|| xLeft >xRight || yTop > yBottom)
        return;
    hBrush = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));
    SetRect(&rect, xLeft, yTop, xRight, yBottom);
    FillRect(hdc, &rect, hBrush);
    DeleteObject(hBrush);
    ReleaseDC(hWnd,hdc);
    return;
}

效果

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值