感觉这次加的注释比较多,把全部代码拿上来了。
这个实现的效果与第一个比是一样的,但是实现方法不一样。
我认为值得学习的地方:
1、首次实现两个窗口类的注册,并且在子窗口类中加入cbWndExtra来保存子窗口状态。
2、循环创建25个子窗口,句柄保存到数组中。
3、首次一个函数中出现两个回调函数。
#include <windows.h> #define DIVISIONS 5 LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); LRESULT CALLBACK ChildWndProc(HWND,UINT,WPARAM,LPARAM); TCHAR szChileClass[]=TEXT("Checker3_Child"); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow ) { static TCHAR szAppName[]=TEXT("Checker3"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style=CS_HREDRAW|CS_VREDRAW; wndclass.lpfnWndProc=WndProc; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hInstance=hInstance; wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.lpszClassName=szAppName; wndclass.lpszMenuName=NULL; if(!RegisterClass(&wndclass)) { MessageBox(NULL,TEXT("Program requires Windows NT!"),szAppName,MB_ICONERROR); return 0; } //以下是注册子窗口类 wndclass.lpfnWndProc=ChildWndProc; //cbWndExtra这个字段被设置成长整形变量的4个字节 //这个字段通知Windows在内部结构中给基于这个窗口类的每个窗口预留4个字节的额外空间。 //用户利用这些空间为每个窗口保存不同的信息。 wndclass.cbWndExtra=sizeof(long); wndclass.hIcon=NULL; wndclass.lpszClassName=szChileClass; //注册子窗口类 RegisterClass(&wndclass); hwnd=CreateWindow(szAppName,TEXT("Checker3 Mouse Hit-Test Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL); 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) { static HWND hwndChild[DIVISIONS][DIVISIONS]; int cxBlock,cyBlock,x,y; switch(message) { //创建多个子窗口 case WM_CREATE: for(x=0;x<DIVISIONS;x++) for(y=0;y<DIVISIONS;y++) //(HMENU)字段为每个子窗口ID,(HINSTANCE)创建子窗口时,必须调用GetWindowLong从Windows给窗口保留的结构中提取hInstnace的值. //hwndChild[x][y]保存窗口句柄 hwndChild[x][y]=CreateWindow(szChileClass,NULL,WS_CHILDWINDOW|WS_VISIBLE, //y左移8位后与x进行位或运算 //获取应用实例的句柄 0,0,0,0,hwnd,(HMENU)(y<<8|x),(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),NULL); return 0; //改变大小后,子窗口也要跟随移动改变 case WM_SIZE: cxBlock=LOWORD(lParam)/DIVISIONS; cyBlock=HIWORD(lParam)/DIVISIONS; for(x=0;x<DIVISIONS;x++) for(y=0;y<DIVISIONS;y++) //改变窗口的位置和大小 MoveWindow(hwndChild[x][y],x*cxBlock,y*cyBlock,cxBlock,cyBlock,TRUE); return 0; //这个在实际应用中其实不是有很大的影响。 //只是在窗口下边与右边没有被子窗口覆盖的部分,会发出声音 case WM_LBUTTONDOWN: MessageBeep(0); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,message,wParam,lParam); } //子窗口的回调函数 LRESULT CALLBACK ChildWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: //设置前面为每个子窗口预留的cbWndExtra为0,为了保存矩形当前状态。 SetWindowLong(hwnd,0,0); return 0; case WM_LBUTTONDOWN: //点击之后,取出cbWndExtra中的值与1做位异或运算 SetWindowLong(hwnd,0,1^GetWindowLong(hwnd,0)); //并使子窗口无效,重绘区域 InvalidateRect(hwnd,NULL,FALSE); return 0; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); //获取子窗口的区域 GetClientRect(hwnd,&rect); //绘制方格 Rectangle(hdc,0,0,rect.right,rect.bottom); //下面判断,是否绘制对角线 if(GetWindowLong(hwnd,0)) { MoveToEx(hdc,0,0,NULL); LineTo(hdc,rect.right,rect.bottom); MoveToEx(hdc,0,rect.bottom,NULL); LineTo(hdc,rect.right,0); } EndPaint(hwnd,&ps); return 0; } return DefWindowProc(hwnd,message,wParam,lParam); }