首页 > 代码库 > Windows MDI(Multiple-Document Interface)

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
/*-----------------------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

/*--------------------------------------------------------   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 windowtypedef struct tagHELLODATA{    UINT     iColor ;    COLORREF clrText ;}HELLODATA, *LPHELLODATA ;// structure for storing data unique to each Rect child windowtypedef struct tagRECTDATA{    short cxClient ;    short cyClient ;}RECTDATA, *LPRECTDATA ;// global variableschar      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 =http://www.mamicode.com/ (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 = http://www.mamicode.com/(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 = http://www.mamicode.com/(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

/*----------------------------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}