?? app_four.html
字號:
<HTML><LINK HREF="style.css" REL="STYLESHEET" TYPE="text/css"><HEAD><TITLE>App Part 4: Multiple Document Interface</TITLE></HEAD><BODY><FONT SIZE="-1">[ <A HREF="./index.html">contents</A>| <A HREF="http://www.winprog.org/">#winprog</A>]</FONT><HR><H1>App Part 4: Multiple Document Interface</H1><P>Example: app_four</P><IMG SRC="images/app_four.gif" ALT="[images/app_four.gif]" ALIGN="right"><H2>MDI Overview</H2>First a bit of background... Every window has a <I>Client Area</I>, this iswhere most programs draw images, place controls etc... the Client Area is notseperate from the window itself, it is simply a smaller specialised regionof it. Sometimes a window can be all client area, and nothing else, sometimesthe client area is smaller to make room for menus, titles, scrollbars, etc...<P>In MDI terms, your main window is called the Frame, this is probably theonly window you would have in a SDI (Single Document Interface) program.In MDI there is an additional window, called the <I>MDI Client Window</I>which is a child of your Frame window.Unlike the <I>Client Area</I> it is a complete and seperate window all on it'sown, it has a client area of it's own and probably a few pixels for a border.You never directly handle messages for the MDI Client, it is done by thepre-defined windows class <CODE>"MDICLIENT"</CODE>. You can communicate withand manipulate the MDI Client and the windows it contains through messages.<P>When it comes to the windows which actually display your document orwhatever your program displays, you send a message to the MDI Client to tell it to create a newwindow of the type you've specified. The new window is created as a child ofthe MDI Client, not of your Frame window. This new window is an MDI Child.The MDI Child is a child of the MDI Client, which in turn is a child of theMDI Frame (Getting dizzy yet?). To make matters worse, the MDI Child willprobably have child windows of its own, for instance the edit control in theexample program for this section. <P>You are responsable for writing two (or more) Window Procedures. One, just likealways, for your main window(the Frame). And one more for the MDI Child. You may alsohave more than one type of Child, in which case, you'll want a seperate windowprocedure for each type. <P>If I've thoroughly confused you now talking about MDI Clients and things, this diagram may clear things up a little better:<P ALIGN="CENTER"><IMG SRC="images/mdi_diagram.gif" ALT="[images/mdi_diagram.gif]" BORDER="1"></P><H2>Getting Started with MDI</H2>MDI requires a few subtle changes throughout a program, so please read through this section carefully... chances are that if your MDI program doesn't work or has strange behaviourit's because you missed one of the alterations from a regular program. <H3>MDI Client Window</H3>Before we create our MDI window we need to make a change to the default message processing that goes on in our Window Procedure... since we're creating a Frame window that willhost an MDI Client, we need to change the <CODE>DefWindowProc()</CODE> call to <CODE>DefFrameProc()</CODE> which addsspecialized message handling for Frame Windows,<PRE CLASS="SNIP"> default: return DefFrameProc(hwnd, g_hMDIClient, msg, wParam, lParam);</PRE>The next step is to create the MDI Client window itself, as a child of our frame window.We do this in <CODE>WM_CREATE</CODE> as usual...<PRE CLASS="SNIP"> CLIENTCREATESTRUCT ccs; ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 2); ccs.idFirstChild = ID_MDI_FIRSTCHILD; g_hMDIClient = CreateWindowEx(WS_EX_CLIENTEDGE, "mdiclient", NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwnd, (HMENU)IDC_MAIN_MDI, GetModuleHandle(NULL), (LPVOID)&ccs);</PRE>The menu handle is the handle to the popup menu that the MDI client will add items torepresenting each window that is created, allowing the user to select the window they want to activate from the menu, we'll add functionality shortly to handle this case. In this exampleit's the 3rd popup (index 2) since I've added Edit and Window to the menu after File.<P><CODE>ccs.idFirstChild</CODE> is a number to use as the first ID for the itemsthe Client adds to the Window menu... you want this to be easilydistinguishable from your own menu identifiers so you can handle your menu commandsand pass the Window menu commands to <CODE>DefFrameProc()</CODE> for processing. In theexample I specify an identifier defined as <CODE>50000</CODE>, high enough that I know noneof my menu command id's will be above it.<P>Now to get this menu to work properly we need to add some special handling to our <CODE>WM_COMMAND</CODE> handler:<PRE CLASS="SNIP"> case WM_COMMAND: switch(LOWORD(wParam)) { case ID_FILE_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; // ... handle other regular IDs ... // Handle MDI Window commands default: { if(LOWORD(wParam) >= ID_MDI_FIRSTCHILD) { DefFrameProc(hwnd, g_hMDIClient, msg, wParam, lParam); } else { HWND hChild = (HWND)SendMessage(g_hMDIClient, WM_MDIGETACTIVE,0,0); if(hChild) { SendMessage(hChild, WM_COMMAND, wParam, lParam); } } } } break;</PRE><P>I've added a <CODE>default:</CODE> case which will catch all commands that I didn't processdirectly and do a check to see if the value is greater than or equal to <CODE>ID_MDI_FIRSTCHILD</CODE>.If it is, then the user has clicked on one of the Window menu items and we sendthe message on to <CODE>DefFrameProc()</CODE> for processing. <P>If it isn't one of the WindowIDs then I get the handle to the active child window and forward the messageto it for processing. This allows you to delegate responsibility to the Childwindows for performing certain actions, and allows different child windowsto handle commands in different ways if so desired. In the example I onlyhandle commands that are global to the program in the Frame window procedure,and send the commands which affect a certain document or child window on to thechild window itself for processsing.<P>Since we're building on the last example, the code to size the MDI client is the same asthe code to resize the edit control in the last example, that takes into account the sizeand position of the tool and status bars so they don't overlap the MDI client window.<P>We also need to modify our message loop a little...<PRE CLASS="SNIP"> while(GetMessage(&Msg, NULL, 0, 0)) { if (!TranslateMDISysAccel(g_hMDIClient, &Msg)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } }</PRE>We've added an extra step (<CODE>TranslateMDISysAccel()</CODE>), that checks for the pre-defined accelerator keys, Ctrl+F6 which swtiches to the next window, Ctrl+F4 which closes the Childand so on. If you don't add in this check you will annoy your users by notproviding the standard behaviour they've gotten used to, or you'll have toimplement it manually.<H3>Child Window Class</H3>In addition to the main window of the program (the Frame window) we need to create newwindow classes for each type of child window we want. For example you might have oneto display text, and one to display a picture or graph. In this example we'll only be creating one child type, which will be just like the editor program in the previousexamples.<PRE CLASS="SNIP">BOOL SetUpMDIChildWindowClass(HINSTANCE hInstance){ WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = MDIChildWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = g_szChildClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(0, "Could Not Register Child Window", "Oh Oh...", MB_ICONEXCLAMATION | MB_OK); return FALSE; } else return TRUE;}</PRE>This is basically identical to registering our regular frame window, there are no particularlyspecial flags here for use with MDI. We've set the menu as NULL, and the window procedureto point to the child window procedure which we will write next.<H3>MDI Child Procedure</H3>The window procecure for an MDI child is much like any other with a few small exceptions.First of all, default messages are passed to <CODE>DefMDIChildProc()</CODE> instead of <CODE>DefWindowProc()</CODE>.<P>In this particular case, we also want to disable the Edit and Window menu's when they aren't needed (just because it's a nice thing to do), so we handle <CODE>WM_MDIACTIVEATE</CODE>and enable or disable them depending on if our window is getting activated or not. If youhave multiple types of child window, this is where you could put code to completely change themenu or toolbar or make alterations to other aspects of the program to reflect the actionsand commands that are specific to the type of window being activated.<P>To be even more complete, we can disable the Close and Save File menu items as well, since they aren't
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -