Windows零基础入门:4.3 获取窗口类背景画刷句柄和使用窗口类背景画刷句柄

4336 人浏览 | 时间: 2015-10-19 16:18:20 | 作者: codexia

    上节课我在《Windows零基础入门:4.2 获取窗口类信息之查询窗口类名称ID》一节中,详细介绍了窗口类ID的获取,也介绍了窗口类ID和窗口类名称的关系。讲解很详细,相信你应该看懂了吧。如果没有,请多看几遍,还不懂,留言!
    这节课我来讲一下获取窗口类的背景画刷句柄。我们使用的还是函数GetClassLong。使用下面的代码即可获得窗口类背景画刷:

HBRUSH hBrush = (HBRUSH)GetClassLong(hwnd, GCL_HBRBACKGROUND);
    你需要用一个HBRUSH画刷类型的变量来接收得到的画刷。因为GetClassLong函数返回的是LONG类型的值,与HBRUSH类型不一样,所以要强制转换。在《Windows零基础入门:4.1 获取窗口类信息之查询窗口类函数详细介绍》,你应该知道了GCL_HBRBACKGROUND索引的意义,就是用来获取窗口类的背景画刷句柄的。
    对于画刷是什么东西,我们在后面画图部分详细介绍,前面也有所提及。你只需要知道,画刷是用来画图用的,一般都是大面积的画,所以才叫画画的刷子即画刷。
    我们只要有这个画刷,就可以画图。当然,我们这里就已经知道了获取画刷的方法。代码也有了,也解释了。那是不是就讲完了呢?
    当然不是。我们在讲窗口类的部分,就要将学到的知识在这里尽可能多的使用,这样会学的更加深刻。当你学的很深刻了,用的很灵活了,那就学的差不多了。
    获取画刷倒是不难咯。这里我们需要了解背景技术知识,这是我们的核心。这也会是菜鸟学习和大神学习的区别,大神学习方法是抓住核心,而不是学习简单使用。只有抓住核心,使用才会灵活。
    窗口类的作用会影响所有由窗口类创建的窗口,这个在前面的课程讲过了。窗口类的信息对于这些窗口来讲,是全局性质的。所以,多个窗口相当于公用这个窗口类的信息。那么你在一个窗口中修改了窗口类的信息,那么在其他窗口中就可以得到改变后的信息。所以,我们这里不仅要对一个窗口的窗口类做改变验证,还要对两个窗口公用窗口类做验证,这样你会理解更加深入。当然,编写代码和思考问题的能力也会大大提升,所以,前提是,你一定要动手做,动脑想。
    我们先创建两个窗口,一个主窗口一个窗口2,他们用同一个窗口类创建,所以共用窗口类。在他们创建窗口时,都存储一下当前的窗口背景画刷句柄。用来做画刷切换使用的。默认的是白色的画刷,所以窗口默认是白色的背景。存储原始背景画刷代码如下:
case WM_CREATE:// - 窗口创建时
    static HBRUSH hBgOld;
    hBgOld = (HBRUSH)GetClassLong(hwnd, GCL_HBRBACKGROUND);
    这里说一下,这个窗口过程函数,是不停的执行的,每一次都是从头开始执行,所以,窗口过程函数里的变量如果是局部的,下次又重新定义初始化,所以,不要用局部变量存储需要一直存在的值。所以我们使用static局部变量或者全局变量。为了直观方便,所以使用static静态变量类型了。
    然后在单击消息中,做一些处理。首先还是获取当前的背景画刷句柄,然后与存储的原始的句柄比较,如果一样就设置窗口类背景画刷为黑色,否则就用存储的画刷设置窗口类的背景画刷。这里就是先了切换。切换窗口类的代码如下:
HBRUSH hBrush;
case WM_LBUTTONDOWN:
    // - 通过窗口类背景画刷实现窗口背景颜色切换
    hBrush = (HBRUSH)GetClassLong(hwnd, GCL_HBRBACKGROUND);// - 获取当前窗口类的背景画刷句柄
    if(hBrush == hBgOld)
        SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH));// - 如果存储的和获取的画刷一致,就用黑色画刷来设置
    else
        SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)hBgOld);// - 如果获取的不是存储的画刷,就用存储的恢复
    当鼠标左键不停的单击,就实现不停的切换窗口背景画刷。那么此时我们让背景重新绘画一下,就可以反映出来效果了。因为默认的窗口重画,会使用窗口类指定的背景画刷来重绘的。而我们刚好就修改了窗口类的背景画刷,就可以用我们设置的画刷来切换窗口背景。虽然这是在绘画,但是不用跟我们写任何绘画代码,我们只是改变了绘画的工具而已,给他换了画刷而已,然后系统拿到的就是不同的画刷,画出来的自然就不一样。
    我们要使窗口重绘,需要使用函数InvalidateRect,它的第一个参数为窗口句柄。第二个为矩形结构体地址,如果设置为NULL,则默认窗口整个客户区大小。第三个参数表示要不要先擦到之前的窗口,我们要让他擦掉,这样再重画,不擦掉,看不到效果。因为我们是需要它全部重新画,所以要擦掉。具体绘画的知识在绘图部分详细解释。使窗口客户区矩形无效重画,代码如下:
InvalidateRect(hwnd, NULL, TRUE);
    当然,我们只看主窗口,单击可以不停的切换背景颜色。这说明修改窗口类背景画刷改变了窗口的背景颜色,而且修改后就可以了。这意思是,窗口类信息和窗口是动态联系的,窗口需要重绘会去查询窗口类的,而不是创建窗口完毕就和窗口类断绝关系的。如果只看一个窗口,是不够说服力的。所以我们创建了两个窗口。当第一个窗口单击变成黑色之后,表面窗口类的背景画刷设置成了黑色。如果不考虑主窗口,那么窗口2默认的是白色的,然后再点击就会变成黑色的。但是事实上,单击窗口2时第一次并没有变成黑色,而是依然是白色。因为当窗口2单击时获取窗口类背景画刷时已经变成了黑色,所以,他就应该设置成白色的。所以就和默认的一致,所以画出来没有变化。再单击一次窗口2,它就变黑了。这就表明了他们确实在共享一个窗口类的信息,因为修改窗口类背景画刷是相互影响的。
    因为我们没有响应WM_PAINT消息,所以需要额外的使用函数让窗口重绘。所以,在调整窗口大小时因为窗口类背景画刷没有变,所以看不到变化。

    这里简单的说一下,SetClassLong就是设置窗口类信息的函数,我们在修改窗口类部分再一个个介绍,这里知道是修改就行了。第二个参数就是索引标志,和GetClassLong指示的窗口类成员是一样的。第三个参数就是传值进去修改。

    下面是一个窗口单击前后的效果:

    获取窗口类背景画刷和使用窗口类背景画刷
    下面是完整的代码:
#include <Windows.h>
#include <tchar.h>

LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);// - 窗口过程函数的声明
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, PSTR szCmdLine, int iCmdShow)
{
    WNDCLASS wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WinProc;
    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 = 0;
    wndclass.lpszClassName = L"MyClass";
    ATOM atomCls = RegisterClass(&wndclass);
    if (!atomCls)
    {
        MessageBox(NULL, L"注册窗口类失败,此程序需要运行在Windows NT平台下。", L"注册窗口类提示", MB_ICONERROR);
        return 0;
    }

    HWND hwnd = CreateWindow(MAKEINTRESOURCE(atomCls), L"主窗口_C++技术网", WS_OVERLAPPEDWINDOW, 100, 100, 600, 600, NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd, SW_SHOWNORMAL);
    UpdateWindow(hwnd);

    HWND hwnd2 = CreateWindow(MAKEINTRESOURCE(atomCls), L"窗口2_C++技术网", WS_OVERLAPPEDWINDOW, 100, 100, 600, 600, NULL, NULL, hInstance, NULL);
    ShowWindow(hwnd2, SW_SHOWNORMAL);
    UpdateWindow(hwnd2);

    MSG  msg;
    // - 消息循环
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    RECT rect;
    static HBRUSH hBgOld;//-静态变量可以长期存在
    HBRUSH hBrush;
    switch (message)
    {
    case WM_CREATE:
        hBgOld = (HBRUSH)GetClassLong(hwnd, GCL_HBRBACKGROUND);// - 第一次存储原始的画刷,白色画刷
        return 0;

    case WM_LBUTTONDOWN:
        // - 通过窗口类背景画刷实现窗口背景颜色切换
        hBrush = (HBRUSH)GetClassLong(hwnd, GCL_HBRBACKGROUND);// - 获取当前窗口类的背景画刷句柄
        if (hBrush == hBgOld)
            SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)GetStockObject(BLACK_BRUSH));// - 如果存储的和获取的画刷一致,就用黑色画刷来设置
        else
            SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)hBgOld);// - 如果获取的不是存储的画刷,就用存储的恢复
        InvalidateRect(hwnd, NULL, TRUE);// - 让指定的这个矩形大小无效,导致窗口重画。这样系统就会触发WM_PAINT使用窗口类的背景画刷绘制窗口无效区域。
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
请微信扫码阅读
为防止恶意爬虫,
已开启反爬机制

Win32课程菜单