Windows MDI(Multiple-Document Interface)
Windows多文档窗口编程中,需要注意的以下几点:
1、主窗口与文档窗口之间还有一个Client Window。
2、创建文档窗口。通常认为创建子窗口就用CreateWindow,但是MDI中创建文档窗口时,用的是发送消息的方式。具体的CreateWindow的工作由Client Window来完成。该消息是WM_MDICREATE。
3、主菜单的变化。切换到不同的文档窗口时,主菜单会随文档窗口的类型、内容等变化。文档子窗口是通过处理WM_MDIACTIVATE消息完成的。
4、主窗口默认的消息处理函数不是DefWindowProc,而是DefFrameProc。文档窗口和主窗口一样,默认的消息处理函数也变了,是DefMDIChildProc。它们的原型分别是
LRESULT DefFrameProc(
HWND hWnd, HWND hWndMDIClient, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT DefMDIChildProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
<<Windows Programming>>第19章源码如下:
MDIDEMO.H
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*----------------------- MDIDEMO.H header file -----------------------*/ #define EXPORT __declspec (dllexport) #define INIT_MENU_POS 0 #define HELLO_MENU_POS 2 #define RECT_MENU_POS 1 #define IDM_NEWHELLO 10 #define IDM_NEWRECT 11 #define IDM_CLOSE 12 #define IDM_EXIT 13 #define IDM_BLACK 20 #define IDM_RED 21 #define IDM_GREEN 22 #define IDM_BLUE 23 #define IDM_WHITE 24 #define IDM_TILE 30 #define IDM_CASCADE 31 #define IDM_ARRANGE 32 #define IDM_CLOSEALL 33 #define IDM_FIRSTCHILD 100
MDIDEMO.C
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*-------------------------------------------------------- MDIDEMO.C -- Multiple Document Interface Demonstration (c) Charles Petzold, 1996 --------------------------------------------------------*/ #include <windows.h> #include <stdlib.h> #include "mdidemo.h" LRESULT CALLBACK FrameWndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK CloseEnumProc (HWND, LPARAM) ; LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM) ; LRESULT CALLBACK RectWndProc (HWND, UINT, WPARAM, LPARAM) ; // structure for storing data unique to each Hello child window typedef struct tagHELLODATA{ UINT iColor ; COLORREF clrText ; }HELLODATA, *LPHELLODATA ; // structure for storing data unique to each Rect child window typedef struct tagRECTDATA{ short cxClient ; short cyClient ; }RECTDATA, *LPRECTDATA ; // global variables char szFrameClass[] = "MdiFrame" ; char szHelloClass[] = "MdiHelloChild" ; char szRectClass[] = "MdiRectChild" ; HINSTANCE hInst ; HMENU hMenuInit, hMenuHello, hMenuRect ; HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HACCEL hAccel ; HWND hwndFrame, hwndClient ; MSG msg ; WNDCLASSEX wndclass ; hInst = hInstance ; if (!hPrevInstance) { // Register the frame window class wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = FrameWndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szFrameClass ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; // Register the Hello child window class wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = HelloWndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = sizeof (HANDLE) ; 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 = szHelloClass ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; // Register the Rect child window class wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = RectWndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = sizeof (HANDLE) ; 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 = szRectClass ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; } // Obtain handles to three possible menus & submenus hMenuInit = LoadMenu (hInst, "MdiMenuInit") ; hMenuHello = LoadMenu (hInst, "MdiMenuHello") ; hMenuRect = LoadMenu (hInst, "MdiMenuRect") ; hMenuInitWindow = GetSubMenu (hMenuInit, INIT_MENU_POS) ; hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ; hMenuRectWindow = GetSubMenu (hMenuRect, RECT_MENU_POS) ; // Load accelerator table hAccel = LoadAccelerators (hInst, "MdiAccel") ; // Create the frame window hwndFrame = CreateWindow (szFrameClass, "MDI Demonstration", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, hMenuInit, hInstance, NULL) ; hwndClient = GetWindow (hwndFrame, GW_CHILD) ; ShowWindow (hwndFrame, iCmdShow) ; UpdateWindow (hwndFrame) ; // Enter the modified message loop while (GetMessage (&msg, NULL, 0, 0)) { if (!TranslateMDISysAccel (hwndClient, &msg) && !TranslateAccelerator (hwndFrame, hAccel, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } // Clean up by deleting unattached menus DestroyMenu (hMenuHello) ; DestroyMenu (hMenuRect) ; return msg.wParam ; } LRESULT CALLBACK FrameWndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndClient ; CLIENTCREATESTRUCT clientcreate ; HWND hwndChild ; MDICREATESTRUCT mdicreate ; switch (iMsg) { case WM_CREATE : // Create the client window clientcreate.hWindowMenu = hMenuInitWindow ; clientcreate.idFirstChild = IDM_FIRSTCHILD ; hwndClient = CreateWindow ("MDICLIENT", NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU) 1, hInst, (LPSTR) &clientcreate) ; return 0 ; case WM_COMMAND : switch (wParam) { case IDM_NEWHELLO : // Create a Hello child window mdicreate.szClass = szHelloClass ; mdicreate.szTitle = "Hello" ; mdicreate.hOwner = hInst ; mdicreate.x = CW_USEDEFAULT ; mdicreate.y = CW_USEDEFAULT ; mdicreate.cx = CW_USEDEFAULT ; mdicreate.cy = CW_USEDEFAULT ; mdicreate.style = 0 ; mdicreate.lParam = 0 ; hwndChild = (HWND) SendMessage (hwndClient, WM_MDICREATE, 0, (LPARAM) (LPMDICREATESTRUCT) &mdicreate) ; return 0 ; case IDM_NEWRECT : // Create a Rect child window mdicreate.szClass = szRectClass ; mdicreate.szTitle = "Rectangles" ; mdicreate.hOwner = hInst ; mdicreate.x = CW_USEDEFAULT ; mdicreate.y = CW_USEDEFAULT ; mdicreate.cx = CW_USEDEFAULT ; mdicreate.cy = CW_USEDEFAULT ; mdicreate.style = 0 ; mdicreate.lParam = 0 ; hwndChild = (HWND) SendMessage (hwndClient, WM_MDICREATE, 0, (LPARAM) (LPMDICREATESTRUCT) &mdicreate) ; return 0 ; case IDM_CLOSE : // Close the active window hwndChild = (HWND) SendMessage (hwndClient, WM_MDIGETACTIVE, 0, 0) ; if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0)) SendMessage (hwndClient, WM_MDIDESTROY, (WPARAM) hwndChild, 0) ; return 0 ; case IDM_EXIT : // Exit the program SendMessage (hwnd, WM_CLOSE, 0, 0) ; return 0 ; // messages for arranging windows case IDM_TILE : SendMessage (hwndClient, WM_MDITILE, 0, 0) ; return 0 ; case IDM_CASCADE : SendMessage (hwndClient, WM_MDICASCADE, 0, 0) ; return 0 ; case IDM_ARRANGE : SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0) ; return 0 ; case IDM_CLOSEALL : // Attempt to close all children EnumChildWindows (hwndClient, &CloseEnumProc, 0) ; return 0 ; default : // Pass to active child... hwndChild = (HWND) SendMessage (hwndClient, WM_MDIGETACTIVE, 0, 0); if (IsWindow (hwndChild)) SendMessage (hwndChild, WM_COMMAND, wParam, lParam) ; break ; // ...and then to DefFrameProc } break ; case WM_QUERYENDSESSION : case WM_CLOSE : // Attempt to close all children SendMessage (hwnd, WM_COMMAND, IDM_CLOSEALL, 0) ; if (NULL != GetWindow (hwndClient, GW_CHILD)) return 0 ; break ; // I.e., call DefFrameProc case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } // Pass unprocessed messages to DefFrameProc (not DefWindowProc) return DefFrameProc (hwnd, hwndClient, iMsg, wParam, lParam) ; } BOOL CALLBACK CloseEnumProc (HWND hwnd, LPARAM lParam) { if (GetWindow (hwnd, GW_OWNER)) // Check for icon title return 1 ; SendMessage (GetParent (hwnd), WM_MDIRESTORE, (WPARAM) hwnd, 0) ; if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0)) return 1 ; SendMessage (GetParent (hwnd), WM_MDIDESTROY, (WPARAM) hwnd, 0) ; return 1 ; } LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static COLORREF clrTextArray[] = { RGB (0, 0, 0), RGB (255, 0, 0), RGB (0, 255, 0), RGB ( 0, 0, 255), RGB (255, 255, 255) } ; static HWND hwndClient, hwndFrame ; HDC hdc ; HMENU hMenu ; LPHELLODATA lpHelloData ; PAINTSTRUCT ps ; RECT rect ; switch (iMsg) { case WM_CREATE : // Allocate memory for window private data lpHelloData = (LPHELLODATA) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof (HELLODATA)) ; lpHelloData->iColor = IDM_BLACK ; lpHelloData->clrText = RGB (0, 0, 0) ; SetWindowLong (hwnd, 0, (long) lpHelloData) ; // Save some window handles hwndClient = GetParent (hwnd) ; hwndFrame = GetParent (hwndClient) ; return 0 ; case WM_COMMAND : switch (wParam) { case IDM_BLACK : case IDM_RED : case IDM_GREEN : case IDM_BLUE : case IDM_WHITE : // Change the text color lpHelloData = (LPHELLODATA) GetWindowLong (hwnd, 0) ; hMenu = GetMenu (hwndFrame) ; CheckMenuItem (hMenu, lpHelloData->iColor, MF_UNCHECKED) ; lpHelloData->iColor = wParam ; CheckMenuItem (hMenu, lpHelloData->iColor, MF_CHECKED) ; lpHelloData->clrText = clrTextArray[wParam - IDM_BLACK] ; InvalidateRect (hwnd, NULL, FALSE) ; } return 0 ; case WM_PAINT : // Paint the window hdc = BeginPaint (hwnd, &ps) ; lpHelloData = (LPHELLODATA) GetWindowLong (hwnd, 0) ; SetTextColor (hdc, lpHelloData->clrText) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, "Hello, World!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_MDIACTIVATE : // Set the Hello menu if gaining focus if (lParam == (LPARAM) hwnd) SendMessage (hwndClient, WM_MDISETMENU, (WPARAM) hMenuHello, (LPARAM) hMenuHelloWindow) ; // Check or uncheck menu item lpHelloData = (LPHELLODATA) GetWindowLong (hwnd, 0) ; CheckMenuItem (hMenuHello, lpHelloData->iColor, (lParam == (LPARAM) hwnd) ? MF_CHECKED : MF_UNCHECKED) ; // Set the Init menu if losing focus if (lParam != (LPARAM) hwnd) SendMessage (hwndClient, WM_MDISETMENU, (WPARAM) hMenuInit, (LPARAM) hMenuInitWindow) ; DrawMenuBar (hwndFrame) ; return 0 ; case WM_QUERYENDSESSION : case WM_CLOSE : if (IDOK != MessageBox (hwnd, "OK to close window?", "Hello", MB_ICONQUESTION | MB_OKCANCEL)) return 0 ; break ; // I.e., call DefMDIChildProc case WM_DESTROY : lpHelloData = (LPHELLODATA) GetWindowLong (hwnd, 0) ; HeapFree (GetProcessHeap (), 0, lpHelloData) ; return 0 ; } // Pass unprocessed message to DefMDIChildProc return DefMDIChildProc (hwnd, iMsg, wParam, lParam) ; } LRESULT CALLBACK RectWndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndClient, hwndFrame ; HBRUSH hBrush ; HDC hdc ; LPRECTDATA lpRectData ; PAINTSTRUCT ps ; int xLeft, xRight, yTop, yBottom ; short nRed, nGreen, nBlue ; switch (iMsg) { case WM_CREATE : // Allocate memory for window private data lpRectData = (LPRECTDATA) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof (RECTDATA)) ; SetWindowLong (hwnd, 0, (long) lpRectData) ; // Start the timer going SetTimer (hwnd, 1, 250, NULL) ; // Save some window handles hwndClient = GetParent (hwnd) ; hwndFrame = GetParent (hwndClient) ; return 0 ; case WM_SIZE : // If not minimized, save the window size if (wParam != SIZE_MINIMIZED) { lpRectData = (LPRECTDATA) GetWindowLong (hwnd, 0) ; lpRectData->cxClient = LOWORD (lParam) ; lpRectData->cyClient = HIWORD (lParam) ; } break ; // WM_SIZE must be processed by DefMDIChildProc case WM_TIMER : // Display a random rectangle lpRectData = (LPRECTDATA) GetWindowLong (hwnd, 0) ; xLeft = rand () % lpRectData->cxClient ; xRight = rand () % lpRectData->cxClient ; yTop = rand () % lpRectData->cyClient ; yBottom = rand () % lpRectData->cyClient ; nRed = rand () & 255 ; nGreen = rand () & 255 ; nBlue = rand () & 255 ; hdc = GetDC (hwnd) ; hBrush = CreateSolidBrush (RGB (nRed, nGreen, nBlue)) ; SelectObject (hdc, hBrush) ; Rectangle (hdc, min (xLeft, xRight), min (yTop, yBottom), max (xLeft, xRight), max (yTop, yBottom)) ; ReleaseDC (hwnd, hdc) ; DeleteObject (hBrush) ; return 0 ; case WM_PAINT : // Clear the window InvalidateRect (hwnd, NULL, TRUE) ; hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_MDIACTIVATE : // Set the appropriate menu if (lParam == (LPARAM) hwnd) SendMessage (hwndClient, WM_MDISETMENU, (WPARAM) hMenuRect, (LPARAM) hMenuRectWindow) ; else SendMessage (hwndClient, WM_MDISETMENU, (WPARAM) hMenuInit, (LPARAM) hMenuInitWindow) ; DrawMenuBar (hwndFrame) ; return 0 ; case WM_DESTROY : lpRectData = (LPRECTDATA) GetWindowLong (hwnd, 0) ; HeapFree (GetProcessHeap (), 0, lpRectData) ; KillTimer (hwnd, 1) ; return 0 ; } // Pass unprocessed message to DefMDIChildProc return DefMDIChildProc (hwnd, iMsg, wParam, lParam) ; }
MDIDEMO.RC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*---------------------------- MDIDEMO.RC resource script ----------------------------*/ #include <windows.h> #include "mdidemo.h" MdiMenuInit MENU { POPUP "&File" { MENUITEM "New &Hello", IDM_NEWHELLO MENUITEM "New &Rectangles", IDM_NEWRECT MENUITEM SEPARATOR MENUITEM "E&xit", IDM_EXIT } } MdiMenuHello MENU { POPUP "&File" { MENUITEM "New &Hello", IDM_NEWHELLO MENUITEM "New &Rectangles", IDM_NEWRECT MENUITEM "&Close", IDM_CLOSE MENUITEM SEPARATOR MENUITEM "E&xit", IDM_EXIT } POPUP "&Color" { MENUITEM "&Black", IDM_BLACK MENUITEM "&Red", IDM_RED MENUITEM "&Green", IDM_GREEN MENUITEM "B&lue", IDM_BLUE MENUITEM "&White", IDM_WHITE } POPUP "&Window" { MENUITEM "&Cascade\tShift+F5", IDM_CASCADE MENUITEM "&Tile\tShift+F4", IDM_TILE MENUITEM "Arrange &Icons", IDM_ARRANGE MENUITEM "Close &All", IDM_CLOSEALL } } MdiMenuRect MENU { POPUP "&File" { MENUITEM "New &Hello", IDM_NEWHELLO MENUITEM "New &Rectangles", IDM_NEWRECT MENUITEM "&Close", IDM_CLOSE MENUITEM SEPARATOR MENUITEM "E&xit", IDM_EXIT } POPUP "&Window" { MENUITEM "&Cascade\tShift+F5", IDM_CASCADE MENUITEM "&Tile\tShift+F4", IDM_TILE MENUITEM "Arrange &Icons", IDM_ARRANGE MENUITEM "Close &All", IDM_CLOSEALL } } MdiAccel ACCELERATORS { VK_F5, IDM_CASCADE, VIRTKEY, SHIFT VK_F4, IDM_TILE, VIRTKEY, SHIFT }