这节课我来讲一下获取窗口类的背景画刷句柄。我们使用的还是函数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);
}