GetClientRect()和InvalidateRect()的使用

书籍:《Visual C++ 2017从入门到精通》的2.3.8 Win32控件编程

环境:visual studio 2022

内容:例2.17】复选按钮的使用

说明:以下内容大部分来自腾讯元宝。

问题:勾选复选按钮时,内容没有同步显示,需要等好一会儿才刷新。

原因:在InvalidateRect()之前没有调用GetClientRect(hWnd, &rt)获取当前窗口客户区的尺寸导致。

case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        int wmEvent = HIWORD(wParam);

        /*  功能:获取窗口客户区(除去标题栏、边框等)的矩形区域,用于定位绘图坐标。
            ​参数说明:
            hWnd:窗口句柄,标识目标窗口。
            & rt:接收客户区坐标的RECT结构体指针。
            ​应用场景:
            在WM_PAINT中确定提示文本“选择你爱吃的水果:”的显示位置(rt初始值)。
            在WM_COMMAND中标记整个客户区为无效区域,触发重绘*/
        GetClientRect(hWnd, &rt); //获取窗口客户区矩形坐标

        // 分析菜单选择:
        switch (wmId)
        {
        /*  功能:通过BM_GETCHECK消息获取按钮的选中状态(BST_CHECKED或BST_UNCHECKED)。
            ​参数说明:
            (HWND)lParam:复选按钮的句柄。
            0:忽略参数,仅查询状态。
            ​状态更新逻辑:
            若按钮被选中,将对应缓冲区(如szbuf1)填充为“西瓜”。
            若取消选中,清空对应缓冲区内容,确保显示结果与实际状态一致。*/
        case IDC_CHECK1:
            iCheckFlag = (int)SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
            if(iCheckFlag)
            {
                _tcscpy_s(szbuf1, _T("西瓜"));
            }
            else
            {
                _tcscpy_s(szbuf1, _T(""));
            }

            /*  功能:将指定矩形区域标记为无效,触发WM_PAINT消息以重绘该区域。
                ​参数说明:
                hWnd:窗口句柄。
                & rt:需重绘的矩形区域(此处为整个客户区)。
                TRUE:重绘时擦除背景(默认背景色为白色)。
                ​实现逻辑:
                每次复选按钮状态变化时,调用InvalidateRect标记客户区无效。
                系统将WM_PAINT消息加入消息队列,待当前消息处理完成后执行重绘。
                重绘时,WM_PAINT通过GetClientRect重新获取最新客户区坐标,确保绘制位置准确。*/
            InvalidateRect(hWnd, &rt, TRUE);
            break;
        
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;

1. GetClientRect() 的作用

GetClientRect()用于获取窗口客户区的矩形坐标,即窗口中除去标题栏、边框等非客户区后的可绘制区域。其核心功能是将客户区的左上角和右下角坐标存储到RECT结构体中。

代码中的具体作用:

在处理菜单命令(WM_COMMAND)时,通过GetClientRect(hWnd, &rt)获取当前窗口客户区的尺寸,确保后续重绘操作基于最新的客户区范围。
示例中,rt变量存储了客户区的坐标信息,后续通过InvalidateRect()标记该区域为无效,触发重绘。

​2. InvalidateRect() 的作用

InvalidateRect()用于标记指定矩形区域为无效,从而触发系统发送WM_PAINT消息,强制窗口重绘该区域。其参数含义如下:

​**hWnd**:窗口句柄。
​**lpRect**:指向要标记为无效的矩形区域(若为NULL,则整个客户区无效)。
​**bErase**:是否擦除背景(TRUE表示擦除,FALSE保留原背景)。
代码中的具体作用:

每当菜单项(如IDC_CHECK1、IDC_CHECK2)状态变化时,更新对应的字符串变量(如szbuf1、szbuf2)。
调用InvalidateRect(hWnd, &rt, TRUE)将整个客户区标记为无效,并擦除背景,触发WM_PAINT消息,强制重绘界面以反映最新的文本内容。
​注意:此处每次更新都标记整个客户区为无效,可能效率较低。更优的做法是仅标记文本变化的具体区域,减少重绘范围。

​总结

​GetClientRect():获取客户区尺寸,确保重绘区域准确。
​InvalidateRect():强制重绘客户区,更新界面显示。
​代码逻辑:通过菜单操作修改字符串后,立即标记客户区无效并重绘,实现界面动态更新。

在InvalidateRect()之前未调用GetClientRect()

如果在调用 InvalidateRect() 之前没有调用 GetClientRect(),InvalidateRect() 中的 lpRect 参数(即 &rt)将指向一个未初始化或错误的 RECT 结构体。这种情况下会导致以下结果:

1. 行为异常

​未初始化的 RECT 结构体:
如果 rt 变量未通过 GetClientRect() 初始化,它可能包含随机的内存值(垃圾数据)。此时 InvalidateRect(hWnd, &rt, TRUE) 会将一个无效的矩形区域标记为无效,导致系统发送 WM_PAINT 消息时传入错误的坐标范围。

​潜在后果:
若 rt 的坐标超出窗口范围,可能触发无效的重绘操作(例如尝试绘制到屏幕外)。
若 rt 的坐标完全错误(如负数或极大值),可能导致程序崩溃或出现不可预测的图形错误。
​默认行为(lpRect = NULL)​:
如果代码中 rt 未被初始化但传递给 InvalidateRect() 的 lpRect 参数为 NULL(例如写错为 InvalidateRect(hWnd, NULL, TRUE)),则整个窗口客户区会被标记为无效。这与正确调用 GetClientRect() 后的效果类似,但缺乏针对性:

​优点:仍能触发重绘以更新界面。
​缺点:效率较低,因为重绘范围是整个客户区,而非仅需要更新的局部区域。

​2. 具体代码中的影响

在您提供的代码中:

GetClientRect(hWnd, &rt); // 获取窗口客户区坐标
// ...
InvalidateRect(hWnd, &rt, TRUE); // 标记整个客户区为无效

如果没有调用 GetClientRect(),则 rt 未被初始化。假设 rt 是一个全局变量或静态变量,其内容可能是上次使用时的残留值,甚至是随机的未初始化内存。这将导致以下问题:

​不可预测的重绘范围:InvalidateRect() 可能基于错误的矩形区域触发重绘,导致界面显示异常(如部分区域不刷新或闪烁)。
​程序崩溃风险:如果 rt 的值恰好指向无效内存地址(如 NULL 或越界地址),程序可能崩溃。

​3. 正确做法

​必须调用 GetClientRect():
在调用 InvalidateRect() 之前,始终需要通过 GetClientRect() 获取当前窗口客户区的准确坐标,确保重绘范围正确。


GetClientRect(hWnd, &rt); // 获取正确的客户区矩形
InvalidateRect(hWnd, &rt, TRUE); // 标记该区域为无效

​优化局部重绘(可选)​:
如果只需要更新窗口中的特定部分(如某个按钮或文本框),可以缩小 InvalidateRect() 的参数范围,减少重绘开销

// 例如仅更新某个子区域
InvalidateRect(hWnd, &specificRect, FALSE); // 不擦除背景

​总结

​未调用 GetClientRect() 的直接后果:
InvalidateRect() 使用的 RECT 参数无效,可能导致程序崩溃或界面显示异常。
​实际代码中的表现:
如果原代码中 rt 已被正确初始化(例如在其他地方调用过 GetClientRect()),则可能暂时正常运行,但这属于巧合,并非可靠做法。
​最佳实践:
始终在调用 InvalidateRect() 前通过 GetClientRect() 获取最新的客户区坐标,确保重绘范围的正确性和程序的稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值