子窗口的创建非常非常重要
步骤:1、新建窗口类,在窗口类中指名对应的自定义的窗口过程。窗口类类名要唯一,它
是各窗口类相互区别的标识。注意,类名或为静态变量,或为全局变量,因为程
序随时都用他们。
2、创建窗体时,指定其风格之一为WS_CHILD, 指定其父窗口句柄,得到父窗体实
例句柄,并赋给子窗体。
3、在主窗体的create消息中,创建0尺寸的子窗体,主窗体的size消息中,利用
movewindow函数,重设窗体大小,重置窗体位置。
实例为CTRL002,CTRL003
实例CTRL001单击按钮显示对应文字
子窗口向父窗口发送消息
CreateWindow呼叫使用下面这些参数:
Class name(类别名称) Window text(窗口文字) Window style(窗口样式) x position(x位置) y position(y位置) Width(宽度) Height(高度) Parent window(父窗口) Child window ID(子窗口ID) Instance handle(执行实体句柄 )Extra parameters(附加参数) |
TEXT ("button") button[i].szText WS_CHILD|WS_VISIBLE|button[i].iStyle cxChar cyChar * (1 + 2 * i) 20 * xChar 7 * cyChar / 4 hwnd (HMENU)i ((LPCREATESTRUCT) lParam) -> hInstance NULL |
说明:从WM_COMMAND区别出单击了哪个按钮。通过子窗口的ID号来区分。每个子窗口在创建时,就已经分配了一个唯一的ID号。ID号包含在WM_COMMAND消息中wParam的低字节位。用LOWORD()宏来获取ID。
“用鼠标单击按钮时,子窗口控制就向其父窗口发送一个WM_COMMAND消息。...捕获WM_COMMAND消息....”
“LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码
lParam 子窗口句柄”
#include int iFlag=1;//记录被单击按钮的ID //定义一个按钮类型结构体,方便创建按钮,提高代码复用率 struct { int iStyle ; TCHAR * szText ; } button[] = { BS_PUSHBUTTON, TEXT ("PUSHBUTTON"), BS_DEFPUSHBUTTON, TEXT ("DEFPUSHBUTTON"), BS_CHECKBOX, TEXT ("CHECKBOX"), BS_AUTOCHECKBOX, TEXT ("AUTOCHECKBOX"), BS_RADIOBUTTON, TEXT ("RADIOBUTTON"), BS_3STATE, TEXT ("3STATE"), BS_AUTO3STATE, TEXT ("AUTO3STATE"), BS_GROUPBOX, TEXT ("GROUPBOX"), BS_AUTORADIOBUTTON, TEXT ("AUTORADIO"), BS_OWNERDRAW, TEXT ("OWNERDRAW") } ; #define NUM (sizeof button / sizeof button[0]) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("BtnLook") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Button Look"), 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 hwndButton[NUM] ;//句柄是一个数值,所以可以定义成数组形式 static int cxChar, cyChar ; int iLength=0; int i ; HDC hdc ; PAINTSTRUCT ps ; switch (message) { case WM_CREATE : cxChar = LOWORD (GetDialogBaseUnits ()) ; cyChar = HIWORD (GetDialogBaseUnits ()) ; //得用 button[] ,简化创建一系列按钮过程,这就是编辑的艺术 for (i = 0 ; i < NUM-1 ; i++) hwndButton[i] = CreateWindow ( TEXT("button"), button[i].szText, WS_CHILD | WS_VISIBLE | button[i].iStyle, cxChar, cyChar * (1 + 2 * i), 20 * cxChar, 7 * cyChar / 4, hwnd, (HMENU) i, ((LPCREATESTRUCT) lParam)->hInstance, NULL) ; //((HMENU) i),为子窗口指定唯一的ID号。按钮是子窗口的一种。该参数通常用于指定程序的菜单,因此子窗口ID必须被强制转换为HMENU /* hwndButton[9] = CreateWindow ( TEXT("button"), button[9].szText, WS_CHILD | WS_VISIBLE | button[9].iStyle, cxChar, cyChar * (1 + 2 * 9), 20 * cxChar, 7 * cyChar / 4, hwnd, (HMENU) 9, ((LPCREATESTRUCT) lParam)->hInstance, NULL) ; *这种按钮,会不断地触发WM_COMMAND消息,不断地调用WM_COMMAND中的InvalidateRect, 使客户区不断更新,出现闪烁现象。 */ return 0 ; case WM_PAINT : //InvalidateRect (hwnd, &rect, TRUE) ; hdc = BeginPaint (hwnd, &ps) ; //为举例方便起见,将窗口ID号数值大小顺序设定成自定义按钮结构体的顺序 TextOut(hdc,300,200,button[iFlag].szText,lstrlen(button[iFlag].szText)); EndPaint (hwnd, &ps) ; return 0 ; case WM_DRAWITEM : case WM_COMMAND : hdc = GetDC (hwnd) ; //得到子窗口的ID号 iFlag=LOWORD(wParam);//LOWORD=LOW WORD,HIWORD=HIGH WORD ReleaseDC (hwnd, hdc) ; InvalidateRect (hwnd, NULL, TRUE) ;//使客户区失效,发出WM_PAINT消息,导致重绘发生 return 0; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
实例CTRL002 利用多个静态窗体绘制棋盘
说明:1、窗体类别必需是static类,否则程序不能生成静态窗体
2、利用GetWindowLong得到窗体实例句柄
3、HWND是数值类型,可以定义成数组
4、创建子窗体时,将大小设为0,以便重置
4、利用movewidow(...)函数重置静态子窗体位置
#include LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = CreateSolidBrush (0) ;//将背景色设成黑色 wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Chess"), // window caption WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME & ~ WS_MINIMIZEBOX //去除max、minbox,禁止改变窗体大小 , // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters 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 hwndRect[32]; HINSTANCE hinstance; int iw,ik,im,cxClient, cyClient ;//不能将它们定义在switch中 switch (message) { case WM_CREATE: hinstance=(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE); //得到程序的实例 //窗体类别必需是static类,否则程序不能生成静态窗体。 for(iw=0;iw<32;iw++) { hwndRect[iw]=CreateWindow(TEXT("STATIC"),NULL, WS_CHILD|WS_VISIBLE|SS_WHITERECT, //将static 窗体背景色设成白色,好像只能设成黑/白彩色 0,0,0,0, hwnd,(HMENU)iw, hinstance, NULL ); } MoveWindow(hwnd,0,0,300,300,TRUE); return 0 ; case WM_SIZE: //MoveWindow(hwnd,0,0,400,400,TRUE); cxClient = LOWORD (lParam) ;//得到客户区宽度 cyClient = HIWORD (lParam) ;//得到客户区高度 //SetRect(&rcColor,icxClient/2,0,icxClient,icyClient); /* //归纳法 for(ik=0;ik<4;ik++) //for(im=0;im<4;im++) MoveWindow (hwndRect[ik], (0%2+ik)*cxClient/4, 0, cxClient/8, cyClient/8, TRUE) ; for(ik=0;ik<4;ik++) MoveWindow (hwndRect[ik+4], (1%2)*cxClient/8+ik*cxClient/4, cyClient/8, cxClient/8, cyClient/8, TRUE) ; */ MoveWindow(hwnd,200,100,300,300,TRUE); for(ik=0;ik<4;ik++) for(im=0;im<8;im++) MoveWindow (hwndRect[ik+4*im], (im%2)*cxClient/8+ik*cxClient/4, im*cyClient/8, cxClient/8, cyClient/8, TRUE) ; return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
运行结果如下图
实例CTRL003利用多个子窗口制做简易五子棋 #include LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;//主窗口过程 LRESULT CALLBACK ChessWndProc (HWND, UINT, WPARAM, LPARAM) ;//棋盘cell窗口过程 TCHAR szChdChess[]=TEXT("ChessPlane"); #define iRow 15 #define iColumn 15 int iFlag=0; int iYN[iRow][iColumn]; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("MyCHess") ;//需用类名,故声明为静态,也可声明为全局变量 HWND hwnd ; MSG msg ; WNDCLASS wndclass ; //主窗口类 WNDCLASS chd_chessclass;//棋盘子窗口类 WNDCLASS chd_stateclass;//状态子窗口类,可以用wndclass类为模板,但这样写是为了明确过程,便于理解 //主窗口类 wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 = NULL ; wndclass.lpszClassName = szAppName ; //棋盘子窗口类 chd_chessclass.style = CS_HREDRAW | CS_VREDRAW ; //创建窗体是加子(WS_CHILDWINDOW)窗体风格 chd_chessclass.lpfnWndProc = ChessWndProc ; //修改项:换成指定的窗口过程 chd_chessclass.cbClsExtra = 0 ; chd_chessclass.cbWndExtra = sizeof(long) ; chd_chessclass.hInstance = hInstance ; chd_chessclass.hIcon = NULL;//修改项:不需要图标,设为NULL chd_chessclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; chd_chessclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH) ;//修改项:改为黑色 chd_chessclass.lpszMenuName = NULL ; chd_chessclass.lpszClassName = szChdChess ;//修改项:修改类名,窗口类之间相互区分的标识 if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } RegisterClass(&chd_chessclass);//注册自定义的窗口类 hwnd = CreateWindow (szAppName, // window class name TEXT ("SimpleChess"), // window caption WS_OVERLAPPEDWINDOW& ~WS_MAXIMIZEBOX & ~WS_THICKFRAME & ~ WS_MINIMIZEBOX, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters 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或声明为全局变量,否则新建的窗口,在下次窗口过程调用中会丢失。 static HWND hwndChess[iRow][iColumn]; HDC hdc ; PAINTSTRUCT ps ; RECT rect ; int ixclient,iyclient,ik,iw,ixtemp,iytemp; switch (message) { case WM_CREATE: //创建chesscell窗体,加上子窗体风格,成为子窗体 for(ik=0;ik