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
View Code
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 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) ;
}
View Code
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	Shift+F5",     IDM_CASCADE
            MENUITEM "&Tile	Shift+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	Shift+F5",     IDM_CASCADE
            MENUITEM "&Tile	Shift+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
}
View Code
原文地址:https://www.cnblogs.com/licb/p/MDI.html