我是靠谱客的博主 壮观百合,最近开发中收集的这篇文章主要介绍Win32 Series - A Sample MDI ImplementationA Sample MDI Implementation,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

http://www-user.tu-chemnitz.de/~heha/petzold/

 

 

A Sample MDI Implementation

The MDIDEMO program, shown in Figure 19-2, demonstrates the basics of writing an MDI application.

Figure 19-2. The MDIDEMO program.

 

MDIDEMO.C

/*--------------------------------------------------------
MDIDEMO.C -- Multiple-Document Interface Demonstration
(c) Charles Petzold, 1998
--------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
#define INIT_MENU_POS
0
#define HELLO_MENU_POS
2
#define RECT_MENU_POS
1
#define IDM_FIRSTCHILD
50000
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, * PHELLODATA ;
// structure for storing data unique to each Rect child window
typedef struct tagRECTDATA
{
short cxClient ;
short cyClient ;
}
RECTDATA, * PRECTDATA ;
// global variables
TCHAR
szAppName[]
= TEXT ("MDIDemo") ;
TCHAR
szFrameClass[] = TEXT ("MdiFrame") ;
TCHAR
szHelloClass[] = TEXT ("MdiHelloChild") ;
TCHAR
szRectClass[]
= TEXT ("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 ;
WNDCLASS wndclass ;
hInst = hInstance ;
// Register the frame window class
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 ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
// Register the Hello child window class
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 ;
RegisterClass (&wndclass) ;
// Register the Rect child window class
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 ;
RegisterClass (&wndclass) ;
// Obtain handles to three possible menus & submenus
hMenuInit
= LoadMenu (hInstance, TEXT ("MdiMenuInit")) ;
hMenuHello = LoadMenu (hInstance, TEXT ("MdiMenuHello")) ;
hMenuRect
= LoadMenu (hInstance, TEXT ("MdiMenuRect")) ;
hMenuInitWindow
= GetSubMenu (hMenuInit,
INIT_MENU_POS) ;
hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;
hMenuRectWindow
= GetSubMenu (hMenuRect,
RECT_MENU_POS) ;
// Load accelerator table
hAccel = LoadAccelerators (hInstance, szAppName) ;
// Create the frame window
hwndFrame = CreateWindow (szFrameClass, TEXT ("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 message,
WPARAM wParam, LPARAM lParam)
{
static HWND
hwndClient ;
CLIENTCREATESTRUCT clientcreate ;
HWND
hwndChild ;
MDICREATESTRUCT
mdicreate ;
switch (message)
{
case WM_CREATE:
// Create the client window
clientcreate.hWindowMenu
= hMenuInitWindow ;
clientcreate.idFirstChild = IDM_FIRSTCHILD ;
hwndClient = CreateWindow (TEXT ("MDICLIENT"), NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU) 1, hInst,
(PSTR) &clientcreate) ;
return 0 ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_FILE_NEWHELLO:
// Create a Hello child window
mdicreate.szClass = szHelloClass ;
mdicreate.szTitle = TEXT ("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_FILE_NEWRECT:
// Create a Rect child window
mdicreate.szClass = szRectClass ;
mdicreate.szTitle = TEXT ("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_FILE_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_APP_EXIT:
// Exit the program
SendMessage (hwnd, WM_CLOSE, 0, 0) ;
return 0 ;
// messages for arranging windows
case IDM_WINDOW_TILE:
SendMessage (hwndClient, WM_MDITILE, 0, 0) ;
return 0 ;
case IDM_WINDOW_CASCADE:
SendMessage (hwndClient, WM_MDICASCADE, 0, 0) ;
return 0 ;
case IDM_WINDOW_ARRANGE:
SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0) ;
return 0 ;
case IDM_WINDOW_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_WINDOW_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, message, wParam, lParam) ;
}
BOOL CALLBACK CloseEnumProc (HWND hwnd, LPARAM lParam)
{
if (GetWindow (hwnd, GW_OWNER))
// Check for icon title
return TRUE ;
SendMessage (GetParent (hwnd), WM_MDIRESTORE, (WPARAM) hwnd, 0) ;
if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0))
return TRUE ;
SendMessage (GetParent (hwnd), WM_MDIDESTROY, (WPARAM) hwnd, 0) ;
return TRUE ;
}
LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT message,
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 ;
PHELLODATA
pHelloData ;
PAINTSTRUCT
ps ;
RECT
rect ;
switch (message)
{
case WM_CREATE:
// Allocate memory for window private data
pHelloData = (PHELLODATA) HeapAlloc (GetProcessHeap (),
HEAP_ZERO_MEMORY, sizeof (HELLODATA)) ;
pHelloData->iColor
= IDM_COLOR_BLACK ;
pHelloData->clrText = RGB (0, 0, 0) ;
SetWindowLong (hwnd, 0, (long) pHelloData) ;
// Save some window handles
hwndClient = GetParent (hwnd) ;
hwndFrame
= GetParent (hwndClient) ;
return 0 ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_COLOR_BLACK:
case IDM_COLOR_RED:
case IDM_COLOR_GREEN:
case IDM_COLOR_BLUE:
case IDM_COLOR_WHITE:
// Change the text color
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
hMenu = GetMenu (hwndFrame) ;
CheckMenuItem (hMenu, pHelloData->iColor, MF_UNCHECKED) ;
pHelloData->iColor = wParam ;
CheckMenuItem (hMenu, pHelloData->iColor, MF_CHECKED) ;
pHelloData->clrText = clrTextArray[wParam - IDM_COLOR_BLACK] ;
InvalidateRect (hwnd, NULL, FALSE) ;
}
return 0 ;
case WM_PAINT:
// Paint the window
hdc = BeginPaint (hwnd, &ps) ;
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
SetTextColor (hdc, pHelloData->clrText) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("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
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
CheckMenuItem (hMenuHello, pHelloData->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, TEXT ("OK to close window?"),
TEXT ("Hello"),
MB_ICONQUESTION | MB_OKCANCEL))
return 0 ;
break ;
// i.e., call DefMDIChildProc
case WM_DESTROY:
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
HeapFree (GetProcessHeap (), 0, pHelloData) ;
return 0 ;
}
// Pass unprocessed message to DefMDIChildProc
return DefMDIChildProc (hwnd, message, wParam, lParam) ;
}
LRESULT CALLBACK RectWndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static HWND hwndClient, hwndFrame ;
HBRUSH
hBrush ;
HDC
hdc ;
PRECTDATA
pRectData ;
PAINTSTRUCT ps ;
int
xLeft, xRight, yTop, yBottom ;
short
nRed, nGreen, nBlue ;
switch (message)
{
case WM_CREATE:
// Allocate memory for window private data
pRectData = (PRECTDATA) HeapAlloc (GetProcessHeap (),
HEAP_ZERO_MEMORY, sizeof (RECTDATA)) ;
SetWindowLong (hwnd, 0, (long) pRectData) ;
// 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)
{
pRectData = (PRECTDATA) GetWindowLong (hwnd, 0) ;
pRectData->cxClient = LOWORD (lParam) ;
pRectData->cyClient = HIWORD (lParam) ;
}
break ;
// WM_SIZE must be processed by DefMDIChildProc
case WM_TIMER:
// Display a random rectangle
pRectData = (PRECTDATA) GetWindowLong (hwnd, 0) ;
xLeft
= rand () % pRectData->cxClient ;
xRight
= rand () % pRectData->cxClient ;
yTop
= rand () % pRectData->cyClient ;
yBottom = rand () % pRectData->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:
pRectData = (PRECTDATA) GetWindowLong (hwnd, 0) ;
HeapFree (GetProcessHeap (), 0, pRectData) ;
KillTimer (hwnd, 1) ;
return 0 ;
}
// Pass unprocessed message to DefMDIChildProc
return DefMDIChildProc (hwnd, message, wParam, lParam) ;
}

 

MDIDEMO.RC (excerpts)

//Microsoft Developer Studio generated resource script.
#include "resource.h"
#include "afxres.h"
/
// Menu
MDIMENUINIT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello",
IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle",
IDM_FILE_NEWRECT
MENUITEM SEPARATOR
MENUITEM "E&xit",
IDM_APP_EXIT
END
END
MDIMENUHELLO MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello",
IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle",
IDM_FILE_NEWRECT
MENUITEM "&Close",
IDM_FILE_CLOSE
MENUITEM SEPARATOR
MENUITEM "E&xit",
IDM_APP_EXIT
END
POPUP "&Color"
BEGIN
MENUITEM "&Black",
IDM_COLOR_BLACK
MENUITEM "&Red",
IDM_COLOR_RED
MENUITEM "&Green",
IDM_COLOR_GREEN
MENUITEM "B&lue",
IDM_COLOR_BLUE
MENUITEM "&White",
IDM_COLOR_WHITE
END
POPUP "&Window"
BEGIN
MENUITEM "&CascadetShift+F5",
IDM_WINDOW_CASCADE
MENUITEM "&TiletShift+F4",
IDM_WINDOW_TILE
MENUITEM "Arrange &Icons",
IDM_WINDOW_ARRANGE
MENUITEM "Close &All",
IDM_WINDOW_CLOSEALL
END
END
MDIMENURECT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello",
IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle",
IDM_FILE_NEWRECT
MENUITEM "&Close",
IDM_FILE_CLOSE
MENUITEM SEPARATOR
MENUITEM "E&xit",
IDM_APP_EXIT
END
POPUP "&Window"
BEGIN
MENUITEM "&CascadetShift+F5",
IDM_WINDOW_CASCADE
MENUITEM "&TiletShift+F4",
IDM_WINDOW_TILE
MENUITEM "Arrange &Icons",
IDM_WINDOW_ARRANGE
MENUITEM "Close &All",
IDM_WINDOW_CLOSEALL
END
END
/
// Accelerator
MDIDEMO ACCELERATORS DISCARDABLE
BEGIN
VK_F4,
IDM_WINDOW_TILE,
VIRTKEY, SHIFT, NOINVERT
VK_F5,
IDM_WINDOW_CASCADE,
VIRTKEY, SHIFT, NOINVERT
END

 

RESOURCE.H (excerpts)

// Microsoft Developer Studio generated include file.
// Used by MDIDemo.rc
#define IDM_FILE_NEWHELLO
40001
#define IDM_FILE_NEWRECT
40002
#define IDM_APP_EXIT
40003
#define IDM_FILE_CLOSE
40004
#define IDM_COLOR_BLACK
40005
#define IDM_COLOR_RED
40006
#define IDM_COLOR_GREEN
40007
#define IDM_COLOR_BLUE
40008
#define IDM_COLOR_WHITE
40009
#define IDM_WINDOW_CASCADE
40010
#define IDM_WINDOW_TILE
40011
#define IDM_WINDOW_ARRANGE
40012
#define IDM_WINDOW_CLOSEALL
40013

 

MDIDEMO supports two types of extremely simple document windows: one displays "Hello, World!" in the center of its client area, and the other displays a series of random rectangles. (In the source code listings and identifier names, these are referred to as the Hello document and the Rect document.) Different menus are associated with these two types of document windows. The document window that displays "Hello, World!" has a menu that allows you to change the color of the text.

Three Menus

Let's turn first to the MDIDEMO.RC resource script. The resource script defines three menu templates used by the program.

The program displays the MdiMenuInit menu when no document windows are present. This menu simply allows creating a new document or exiting the program.

The MdiMenuHello menu is associated with the document window that displays "Hello, World!" The File submenu allows opening a new document of either type, closing the active document, and exiting the program. The Color submenu lets you set the text color. The Window submenu has options for arranging the document windows in a cascaded or tiled fashion, arranging the document icons, and closing all the windows. This submenu will also list all the document windows as they are created.

The MdiMenuRect menu is associated with the random rectangle document. This is the same as the MdiMenuHello menu except that it does not include the Color submenu.

As usual, the RESOURCE.H header file defines all the menu identifiers. In addition, the following three constants are defined in MDIDEMO.C:

#define INIT_MENU_POS
0
#define HELLO_MENU_POS
2
#define RECT_MENU_POS
1

These identifiers indicate the position of the Window submenu in each of the three menu templates. This information is needed by the program to inform the client window where the document list is to appear. Of course, the MdiMenuInit menu doesn't have a Window submenu, so I've indicated that the list should be appended to the first submenu (position 0). The list will never actually be viewed there, however. (You'll see why this is needed when I discuss the program later.)

The IDM_FIRSTCHILD identifier defined in MDIDEMO.C doesn't correspond to a menu item. This is the identifier that will be associated with the first document window in the list that will appear in the Window submenu. This identifier should be greater than all the other menu IDs.

Program Initialization

In MDIDEMO.C, WinMain begins by registering window classes for the frame window and the two child windows. The window procedures are calledFrameWndProc,HelloWndProc, and RectWndProc. Normally, different icons should be associated with these window classes. For the purpose of simplicity, I've used the standard IDI_APPLICATION icon for the frame and child.

Note that I've defined the hbrBackground field of the WNDCLASS structure for the frame window class to be the COLOR_APPWORKSPACE system color. This is not entirely necessary because the client area of the frame window is covered up by the client window, and the client window has this color anyway. However, using this color looks a little better when the frame window is first displayed.

The lpszMenuName field is set to NULL for each of these three window classes. For the Hello and Rect child window classes, this is normal. For the frame window class, I've chosen to indicate the menu handle in theCreateWindow function when creating the frame window.

The window classes for the Hello and Rect child windows allocate extra space for each window using a nonzero value as thecbWndExtra field of the WNDCLASS structure. This space will be used to store a pointer that will reference a block of memory (the size of the HELLODATA or RECTDATA structures defined near the top of MDIDEMO.C) used to store information unique to each document window.

Next, WinMain uses LoadMenu to load the three menus and save their handles in global variables. Three calls to theGetSubMenu function obtain handles to the Window submenu to which the document list will be appended. These are also saved in global variables. TheLoadAccelerators function loads the accelerator table.

A call to CreateWindow in WinMain creates the frame window. During the WM_CREATE processing inFrameWndProc, the frame window creates the client window. This involves another call toCreateWindow. The window class is set to MDICLIENT, which is the preregistered class for MDI client windows. Much of the support in Windows for MDI is encapsulated in the MDICLIENT window class. The client window procedure serves as an intermediary layer between the frame window and the various document windows. When calling CreateWindow to create the client window, the last argument must be set to a pointer to a structure of type CLIENTCREATESTRUCT. This structure has two fields,

  • hWindowMenu is the handle of the submenu to which the document list will be appended. In MDIDEMO, this ishMenuInitWindow, which was obtained duringWinMain. You'll see later how the menu is changed.

  • idFirstChild is the menu ID to be associated with the first document window in the document list. This is simply IDM_FIRSTCHILD.

Back in WinMain, MDIDEMO displays the newly created frame window and enters the message loop. The message loop differs a little from a normal loop: after obtaining a message from the message queue with a call toGetMessage, an MDI program passes the message to TranslateMDISysAccel (and toTranslateAccelerator if, like the MDIDEMO program, the program also has menu accelerators).

The TranslateMDISysAccel function translates any keystrokes that may correspond to the special MDI accelerators (Ctrl-F6, for example) into a WM_SYSCOMMAND message. If eitherTranslateMDISysAccel orTranslateAccelerator returns TRUE (indicating that a message was translated by one of these functions), do not callTranslateMessage andDispatchMessage.

Notice the two window handles passed to TranslateMDISysAccel and TranslateAccelerator: hwndClient and hwndFrame, respectively. TheWinMain function obtains thehwndClient window handle by callingGetWindow with the GW_CHILD argument.

Creating the Children

The bulk of FrameWndProc is devoted to processing WM_COMMAND messages that signal menu selections. As usual, the low word of thewParam parameter toFrameWndProc contains the menu ID number.

For menu ID values of IDM_FILE_NEWHELLO and IDM_FILE_NEWRECT, FrameWndProc must create a new document window. This involves initializing the fields of an MDICREATESTRUCT structure (most of which correspond toCreateWindow arguments) and sending the client window a WM_MDICREATE message withlParam set to a pointer to this structure. The client window then creates the child document window. (Another possibility is using theCreateMDIWindow function.)

Normally the szTitle field of the MDICREATESTRUCT structure would be the filename corresponding to the document. The style field can be set to the window styles WS_HSCROLL or WS_VSCROLL or both to include scroll bars in the document window. The style field can also include WS_MINIMIZE or WS_MAXIMIZE to initially display the document window in a minimized or maximized state.

The lParam field of the MDICREATESTRUCT structure provides a way for the frame window and the child window to share some variables. This field could be set to a pointer to a memory block containing a structure. During the WM_CREATE message in the child document window, lParam is a pointer to a CREATESTRUCT structure and thelpCreateParams field of this structure is a pointer to the MDICREATESTRUCT structure used to create the window.

On receipt of the WM_MDICREATE message, the client window creates the child document window and adds the title of the window to the bottom of the submenu specified in the MDICLIENTSTRUCT structure used to create the client window. When the MDIDEMO program creates its first document window, this is the File submenu of the MdiMenuInit menu. We'll see later how this document list gets moved to the Window submenu of theMdiMenuHello andMdiMenuRect menus.

Up to nine documents can be listed on the menu, each preceded by an underlined number from 1 to 9. If more than nine document windows are created, this list is followed by a More Windows item on the menu. This item invokes a dialog box with a list box that lists all the document windows. The maintenance of this document list is one of the nicest features of the Windows MDI support.

More Frame Window Message Processing

Let's continue with FrameWndProc message processing before turning our attention to the child document windows.

When you select Close from the File menu, MDIDEMO closes the active child window. It obtains the handle to the active child window by sending the client window a WM_MDIGETACTIVE message. If the child window responds affirmatively to a WM_QUERYENDSESSION message, then MDIDEMO sends the client window a WM_MDIDESTROY message to close the child window.

Processing the Exit option from the File menu requires only that the frame window procedure send itself a WM_CLOSE message.

Processing the Tile, Cascade, and Arrange Icons options from the Window submenu is a snap, requiring only that the WM_MDITILE, WM_MDICASCADE, and WM_MDIICONARRANGE messages be sent to the client window.

The Close All option is a little more complex. FrameWndProc calls EnumChildWindows, passing a pointer referencing the CloseEnumProc function. This function sends a WM_MDIRESTORE message to each child window, followed by a WM_QUERYENDSESSION and, possibly, a WM_MDIDESTROY message. This is not done for the icon title window, indicated by a non-NULL return value from GetWindow with the GW_OWNER argument.

You'll notice that FrameWndProc does not process any of the WM_COMMAND messages that signal one of the colors being selected from the Color menu. These messages are really the responsibility of the document window. For this reason,FrameWndProc sends all unprocessed WM_COMMAND messages to the active child window so that the child window can process those messages that pertain to its window.

All messages that the frame window procedure chooses not to process must be passed toDefFrameProc. This function replacesDefWindowProc in the frame window procedure. Even if a frame window procedure traps the WM_MENUCHAR, WM_SETFOCUS, or WM_SIZE messages, these also must be passed toDefFrameProc.

Unprocessed WM_COMMAND messages must also be passed to DefFrameProc. In particular,FrameWndProc does not process any of the WM_COMMAND messages resulting from the user selecting one of the documents from the list in the Window submenu. (ThewParam values for these options begin with IDM_FIRSTCHILD.) These messages are passed toDefFrameProc and processed there.

Notice that the frame window does not need to maintain a list of window handles of the document windows that it creates. If ever these handles are needed (such as when processing the Close All option from the menu), they can be obtained usingEnumChildWindows.

The Child Document Windows

Now let's look at HelloWndProc, which is the window procedure used for the child document windows that display "Hello, World!"

As with any window class used for more than one window, static variables defined in the window procedure (or any function called from the window procedure) are shared by all windows created based on that window class.

Data that is unique to each window must be stored using a method other than static variables. One such technique involves window properties. Another approach—the one I used—uses memory space reserved by defining a nonzero value in thecbWndExtra field of the WNDCLASS structure used to register the window class.

In MDIDEMO, I use this space to store a pointer that references a block of memory the size of the HELLODATA structure.HelloWndProc allocates this memory during the WM_CREATE message, initializes the two fields (which indicate the currently checked menu item and the text color), and stores the pointer usingSetWindowLong.

When processing a WM_COMMAND message for changing the text colors (recall that these messages originate in the frame window procedure),HelloWndProc usesGetWindowLong to obtain the pointer to the memory block containing the HELLODATA structure. Using this structure,HelloWndProc unchecks the checked menu item, checks the selected menu item, and saves the new color.

A document window procedure receives the WM_MDIACTIVATE message whenever the window becomes active or inactive (indicated by whether or notlParam holds the window's handle). You'll recall that the MDIDEMO program has three different menus: MdiMenuInit for when no documents are present, MdiMenuHello for when a Hello document window is active, and MdiMenuRect for when a Rect document window is active.

The WM_MDIACTIVATE message provides an opportunity for the document window to change the menu. IflParam contains the window's handle (meaning the window is becoming active),HelloWndProc changes the menu to MdiMenuHello. IflParam holds the handle of another window,HelloWndProc changes the menu to MdiMenuInit.

HelloWndProc changes the menu by sending a WM_MDISETMENU message to the client window. The client window processes this message by removing the document list from the current menu and appending it to the new menu. This is how the document list is transferred from the MdiMenuInit menu (which is in effect when the first document is created) to the MdiMenuHello menu. Do not use theSetMenu function to change a menu in an MDI application.

Another little chore involves the check marks on the Color submenu. Program options such as this should be unique to each document. For example, you should be able to set black text in one window and red text in another. The menu check marks should reflect the option chosen in the active window. For this reason, HelloWndProc unchecks the selected menu item when the window is becoming inactive and checks the appropriate item when the window is becoming active.

The wParam and lParam values of the WM_MDIACTIVATE message are the handles of the windows being deactivated and activated, respectively. The window procedure gets the first WM_MDIACTIVATE message withlParam set to the window's handle. The window procedure gets the last message withlParam set to another value when the window is destroyed. When the user switches from one document to another, the first document window receives a WM_MDIACTIVATE message withlParam set to the handle of the first window, at which time the window procedure sets the menu to MdiMenuInit. The second document window receives a WM_MDIACTIVATE message withlParam set to the handle of the second window, at which time the window procedure sets the menu to MdiMenuHello or MdiMenuRect as appropriate. If all the windows are closed, the menu is left as MdiMenuInit.

You'll recall that FrameWndProc sends the child window a WM_QUERYENDSESSION message when the user selects Close or Close All from the menu.HelloWndProc processes the WM_QUERYENDSESSION and WM_CLOSE messages by displaying a message box and asking the user whether the window can be closed. (In a real program, this message box might ask whether a file needed to be saved.) If the user indicates that the window should not be closed, the window procedure returns 0.

During the WM_DESTROY message, HelloWndProc frees the memory block allocated during the WM_CREATE message.

All unprocessed messages must be passed on to DefMDIChildProc (not DefWindowProc) for default processing. Several messages must be passed to DefMDIChildProc whether or not the child window procedure does something with them. These are WM_CHILDACTIVATE, WM_GETMINMAXINFO, WM_MENUCHAR, WM_MOVE, WM_SETFOCUS, WM_SIZE, and WM_SYSCOMMAND.

RectWndProc is fairly similar to HelloWndProc in much of the overhead involved, but it's a little simpler (that is, no menu options are involved and the window does not verify with the user whether it can be closed), so I needn't discuss it. But note that RectWndProc breaks after processing WM_SIZE, so the message is passed toDefMDIChildProc.

Cleaning Up

In WinMain, MDIDEMO uses LoadMenu to load the three menus defined in the resource script. Normally Windows will destroy a menu at the time the window to which the menu is attached is destroyed. That takes care of the Init menu. However, menus that are not attached to a window should be destroyed explicitly. For this reason, MDIDEMO callsDestroyMenu twice at the end ofWinMain to get rid of the Hello and Rect menus.

 

最后

以上就是壮观百合为你收集整理的Win32 Series - A Sample MDI ImplementationA Sample MDI Implementation的全部内容,希望文章能够帮你解决Win32 Series - A Sample MDI ImplementationA Sample MDI Implementation所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部