![]() |
|||||||||||||||||
|
|
|||||||||||||||||
|
|
|||||||||
|
Borland C++
Turbo Assembler |
Teach Yourself Borland C++ 4.5 in 21 Days, Second Edition, Chapter 21
Chapter 21 MDI Windows
The MDI Application Features and Components
When you maximize an MDI child window, it occupies the area defined by the client area of the MDI frame window. When you minimize an MDI child window, the icon of that window appears at the bottom area of the MDI frame window. Note: The MDI frame window has a menu that manipulates the MDI child windows and their contents. The MDI child windows cannot have a menu, but they may contain controls. In any other respect, you can think of an MDI child window as an instance of TFrameWindow or its descendants. Basics of Building an MDI Application
The TMDIClient class focuses on the underlying management of MDI child windows. The TMDIChild class offers the functionality for the MDI child windows. At this stage you might ask, Do I typically derive descendants for all three classes to create MDI applications? The answer is no. You normally need to derive descendants only for the TMDIFrame and TMDIChild classes. The functionality of the TMDIClient class is adequate for most MDI-compliant applications. The TMDIFrame Class
class _OWLCLASS TMDIFrame : virtual public TFrameWindow {
public:
TMDIFrame(const char far* title,
TResId menuResId,
TMDIClient& clientWnd = *new TMDIClient,
TModule* module = 0);
TMDIFrame(HWND hWindow, HWND clientHWnd, TModule* module = 0);
//
// override virtual functions defined by TFrameWindow
//
BOOL SetMenu(HMENU);
TMDIClient* GetClientWindow();
//
// find & return the child menu of an MDI frame's (or anyone's) menu
// bar.
//
static HMENU FindChildMenu(HMENU);
protected:
//
// call ::DefFrameProc() instead of ::DefWindowProc()
//
LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
private:
//
// hidden to prevent accidental copying or assignment
//
TMDIFrame(const TMDIFrame&);
TMDIFrame& operator=(const TMDIFrame&);
DECLARE_RESPONSE_TABLE(TMDIFrame);
DECLARE_STREAMABLE(_OWLCLASS, TMDIFrame, 1);
}; The TMDIFrame class has public, protected, and private members. The MDI frame window class has three constructors, one of which is private. The first constructor creates a class instance by specifying the title, associated menu resource ID, and reference to the associated MDI client window. The second constructor creates a class instance from an existing non-OWL window. The third constructor, which is declared private, creates an instance of class TMDIFrame using another existing instance. The class TMDIFrame declares the public member functions SetMenu, GetClientWindow, and FindChildMenu. The function SetMenu looks for the MDI submenu in the new menu bar and updates member ChildMenuPos if the menu is found. The function searches for the MDI submenu in the menu bar and updates the position in the MDI window's top-level menu of the child window submenu. The function GetClientWindow returns a pointer to the associated MDI client window. The function FindChildMenu searches for the child menu of an MDI frame's menu bar. The class TMDIFrame declares the single protected member function DefWindowProc. This function overrides the inherited function TWindow::DefWindowProc and invokes the Windows API function DefFrameProc. The API function provides the default processing for any incoming Windows message that is not handled by the MDI frame window. Building MDI Frame Windows The constructor of the descendant of TMDIFrame (call it the application frame class) can, in many cases, simply invoke the parent class constructor. This invocation occurs if the steps taken by the parent class are adequate for creating the MDI frame window instance. In the case where you want to modify the behavior of the application frame class, you need to include the required statements. Such statements might assign initial values to data members declared in the application frame class. The SetupWindow member function invokes the InitClientWindow to create the TMDIClient instance. You can modify the SetupWindow function to, for example, automatically create the first child MDI window. The TMDIClient Class
class _OWLCLASS TMDIClient : public virtual TWindow {
public:
LPCLIENTCREATESTRUCT ClientAttr;
TMDIClient(TModule* module = 0);
~TMDIClient();
virtual BOOL CloseChildren();
TMDIChild* GetActiveMDIChild();
//
// member functions to arrange the MDI children
//
virtual void ArrangeIcons();
virtual void CascadeChildren();
virtual void TileChildren(int tile = MDITILE_VERTICAL);
//
// override member functions defined by TWindow
//
BOOL PreProcessMsg(MSG& msg);
BOOL Create();
virtual TWindow* CreateChild();
//
// constructs a new MDI child window object. By default, constructs
// an instance of TWindow as an MDI child window object
//
// will almost always be overridden by derived classes to construct
// an instance of a user-defined TWindow derived class as an MDI
// child window object
//
virtual TMDIChild* InitChild();
protected:
char far* GetClassName();
//
// menu command handlers & enabler
//
void CmCreateChild()
{ CreateChild(); } // CM_CREATECHILD
void CmTileChildren()
{ TileChildren(); } // CM_TILECHILDREN
void CmTileChildrenHoriz()
{ TileChildren(MDITILE_HORIZONTAL); } // CM_TILECHILDREN
void CmCascadeChildren()
{ CascadeChildren(); } // CM_CASCADECHILDREN
void CmArrangeIcons()
{ ArrangeIcons(); } // CM_ARRANGEICONS
void CmCloseChildren()
{ CloseChildren(); } // CM_CLOSECHILDREN
void CmChildActionEnable(TCommandEnabler& commandEnabler);
LRESULT EvMDICreate(MDICREATESTRUCT far& createStruct);
private:
friend class TMDIFrame;
TMDIClient(HWND hWnd, TModule* module = 0);
//
// hidden to prevent accidental copying or assignment
//
TMDIClient(const TMDIClient&);
TMDIClient& operator =(const TMDIClient&);
DECLARE_RESPONSE_TABLE(TMDIClient);
DECLARE_STREAMABLE(_OWLCLASS, TMDIClient, 1);
}; The class TMDIClient declares a public constructor and destructor. The MDI client class declares a number of member functions that handle Windows and menu command messages for activating an MDI child window; arranging the MDI child icons; cascading and tiling MDI children; closing MDI children; and creating an MDI child window. These message response functions use sibling member functions. Table 21.1 shows the predefined menu ID constants and the TMDIClient member functions that respond to them. Table 21.1. The predefined menu command messages for manipulating MDI children. Responding TMDIClient Action Menu ID Constant Member Function Tile CM_TILECHILDREN CmTileChildren Tile Horizon CM_TILECHILDRENHORIZ CmTileChildrenHoriz Cascade CM_CASCASDECHILDREN CmCascadeChildren Arrange Icons CM_ARRANGEICONS CmArrangeIcons Close All CM_CLOSECHILDREN CmCloseChildren There are a number of member functions in the class TMDIClient that you may want to modify when you create class descendants. The list of such member functions includes CreateChild, SetupWindow, CanClose, and CloseChildren. These functions enable you to modify how to create, set up, and close MDI children. The MDI Child Window Class
class _OWLCLASS TMDIChild : virtual public TFrameWindow {
public:
TMDIChild(TMDIClient& parent,
const char far* title = 0,
TWindow* clientWnd = 0,
BOOL shrinkToClient = FALSE,
TModule* module = 0);
TMDIChild(HWND hWnd, TModule* module = 0);
~TMDIChild() {}
//
// override method defined by TWindow
//
BOOL PreProcessMsg(MSG& msg);
protected:
void Destroy(int retVal = 0);
void PerformCreate(int menuOrId);
LRESULT DefWindowProc(UINT msg, WPARAM wParam, LPARAM lParam);
void EvMDIActivate(HWND hWndActivated,
HWND hWndDeactivated);
private:
//
// hidden to prevent accidental copying or assignment
//
TMDIChild(const TMDIChild&);
TMDIChild& operator =(const TMDIChild&);
DECLARE_RESPONSE_TABLE(TMDIChild);
DECLARE_STREAMABLE(_OWLCLASS, TMDIChild, 1);
}; The class TMDIChild declares three constructors (one of which is private) and a destructor. The first constructor enables you to create a class instance by specifying the parent MDI client window, MDI child window title, the client window, and whether or not the MDI child window shrinks to fit the client area. The second constructor creates a class instance using an existing non-OWL MDI child window. The third constructor, which is declared private, creates a TMDIChild class instance using an existing instance. The MDI child window class declares the single public member function PreProcessMsg. This function preprocesses the Windows messages sent to the MDI child windows. The class TMDIChild offers a set of protected functions that create, destroy, and activate MDI child windows. In addition, the class provides its own version of function DefWindowProc to handle default Windows message processing. Building MDI Child Windows
Note: The keyboard handler must not be enabled. It actually causes the reverse effect in the MDI children and antagonizes the proper operations of the MDI application.
Managing MDI Messages SimpleText Viewer Compile and run the application. Experiment with creating MDI children. Notice that the text in odd-numbered MDI child windows is static, whereas the text in even-numbered windows can be edited. We implemented this feature to illustrate how to create a simple form of text viewer and text editor (with no Save option, to keep the example short). Try to tile, cascade, maximize, and minimize these windows. Also test closing individual MDI child windows as well as closing all of the MDI children. Let's examine the code that implements this simple MDI application. Listing 21.1 shows the contents of the MDI1.DEF definition file. Listing 21.2 shows the source code for the MDI1.H header file. This file declares the command message constants and a control ID constant. Listing 21.3 contains the script for the MDI1.RC resource file. The file defines the menu resource required by the MDI frame window. The menu has two menu items, Exit and MDI Children. The latter menu item is a pop-up menu with several options. The commands, except the option Count Children, use predefined command message constants. Listing 21.4 shows the source code for the MDI1.CPP program file. Figure 21.1. A sample session with the MDI1.EXE program.
Listing 21.1. The contents of the MDI1.DEF definition file. 1: NAME MDI1 2: DESCRIPTION `An OWL Windows Application' 3: EXETYPE WINDOWS 4: CODE PRELOAD MOVEABLE DISCARDABLE 5: DATA PRELOAD MOVEABLE MULTIPLE 6: HEAPSIZE 10247: STACKSIZE 8192 Listing 21.2. The source code for the MDI1.H header file. 1: #define CM_COUNTCHILDREN 101 2: #define ID_TEXT_EDIT 1023: #define IDM_COMMANDS 400 Listing 21.3. The script for the MDI1.RC resource file. 1: #include <windows.h> 2: #include <owl\window.rh> 3: #include <owl\mdi.rh> 4: #include "mdi1.h" 5: IDM_COMMANDS MENU LOADONCALL MOVEABLE PURE DISCARDABLE 6: BEGIN 7: MENUITEM "E&xit", CM_EXIT 8: POPUP "&MDI Children" 9: BEGIN 10: MENUITEM " c&reate", CM_CREATECHILD 11: MENUITEM "&Cascade", CM_CASCADECHILDREN 12: MENUITEM "&Tile", CM_TILECHILDREN 13: MENUITEM "Arrange &Icons", CM_ARRANGEICONS 14: MENUITEM " c&lose All", CM_CLOSECHILDREN 15: MENUITEM " c&ount Children", CM_COUNTCHILDREN 16: END17: END Listing 21.4. The source code for the MDI1.CPP program file.
1: /*
2: Program to illustrate simple MDI windows
3: */
4: #include <owl\mdi.rh>
5: #include <owl\applicat.h>
6: #include <owl\framewin.h>
7: #include <owl\mdi.h>
8: #include <owl\static.h>
9: #include <owl\edit.h>
10: #include <owl\scroller.h>
11: #include "mdi1.h"
12: #include <stdio.h>
13: #include <string.h>
14:
15: const MaxWords = 100;
16: const WordsPerLine = 12;
17: const NumWords = 10;
18: char* Words[NumWords] = { "The ", "friend ", "saw ", "the ",
19: "girl ", "drink ", "milk ", "boy ",
20: " cake ", "bread " };
21:
22: BOOL ExpressClose = FALSE;
23: int NumMDIChild = 0;
24: int HighMDIindex = 0;
25:
26: class TWinApp : public TApplication
27: {
28: public:
29: TWinApp() : TApplication() {}
30:
31: protected:
32: virtual void InitMainWindow();
33: };
34:
35: class TAppMDIChild : public TMDIChild
36: {
37: public:
38: // pointer to the edit box control
39: TEdit* TextBox;
40: TStatic* TextTxt;
41:
42: TAppMDIChild(TMDIClient& parent, int ChildNum);
43:
44: protected:
45:
46: // handle closing the MDI child window
47: virtual BOOL CanClose();
48: };
49:
50: class TAppMDIClient : public TMDIClient
51: {
52: public:
53:
54: TAppMDIClient() : TMDIClient() {}
55:
56: protected:
57:
58: // create a new child
59: virtual TMDIChild* InitChild();
60:
61: // close all MDI children
62: virtual BOOL CloseChildren();
63:
64: // handle the command for counting the MDI children
65: void CMCountChildren();
66:
67: // handle closing the MDI frame window
68: virtual BOOL CanClose();
69:
70: // declare response table
71: DECLARE_RESPONSE_TABLE(TAppMDIClient);
72: };
73:
74: DEFINE_RESPONSE_TABLE1(TAppMDIClient, TMDIClient)
75: EV_COMMAND(CM_COUNTCHILDREN, CMCountChildren),
76: END_RESPONSE_TABLE;
77:
78: TAppMDIChild::TAppMDIChild(TMDIClient& parent, int ChildNum)
79: : TMDIChild(parent),
80: TFrameWindow(&parent),
81: TWindow(&parent)
82: {
83: char s[1024];
84:
85: // set the scrollers in the window
86: Attr.Style |= WS_VSCROLL | WS_HSCROLL;
87: // create the TScroller instance
88: Scroller = new TScroller(this, 200, 15, 10, 50);
89:
90: // set MDI child window title
91: sprintf(s, "%s%i", "MDI Child #", ChildNum);
92: Title = _fstrdup(s);
93:
94: // randomize the seed for the random-number generator
95: randomize();
96:
97: // assign a null string to the variable s
98: strcpy(s, "");
99: // build the list of random words
100: for (int i = 0; i < MaxWords; i++) {
101: if (i > 0 && i % WordsPerLine == 0)
102: strcat(s, "\r\n");
103: strcat(s, Words[random(NumWords)]);
104: }
105: // create a static text object in the child window if the
106: // ChildNum variable stores an odd number. Otherwise,
107: // create an edit box control
108: if (ChildNum % 2 == 0) {
109: // create the edit box
110: TextBox = new TEdit(this, ID_TEXT_EDIT, s,
111: 10, 10, 300, 400, 0, TRUE);
112: // remove borders and scroll bars
113: TextBox->Attr.Style &= ~WS_BORDER;
114: TextBox->Attr.Style &= ~WS_VSCROLL;
115: TextBox->Attr.Style &= ~WS_HSCROLL;
116: }
117: else
118: // create static text
119: TextTxt = new TStatic(this, -1, s, 10, 10, 300, 400,
120: strlen(s));
121: }
122:
123: BOOL TAppMDIChild::CanClose()
124: {
125: // return TRUE if the ExpressClose member of the
126: // parent MDI frame window is TRUE
127: if (ExpressClose == TRUE) {
128: NumMDIChild--;
129: return TRUE;
130: }
131: else
132: // prompt the user and return the prompt result
133: if (MessageBox(" close this MDI window?",
134: "Query", MB_YESNO | MB_ICONQUESTION) == IDYES) {
135: NumMDIChild--;
136: return TRUE;
137: }
138: else
139: return FALSE;
140: }
141:
142: TMDIChild* TAppMDIClient::InitChild()
143: {
144: ++NumMDIChild;
145: return new TAppMDIChild(*this, ++HighMDIindex);
146: }
147:
148: BOOL TAppMDIClient::CloseChildren()
149: {
150: BOOL result;
151: // set the ExpressClose flag
152: ExpressClose = TRUE;
153: // invoke the parent class CloseChildren() member function
154: result = TMDIClient::CloseChildren();
155: // clear the ExpressClose flag
156: ExpressClose = FALSE;
157: NumMDIChild = 0;
158: HighMDIindex = 0;
159: return result;
160: }
161:
162: // display a message box that shows the number of children
163: void TAppMDIClient::CMCountChildren()
164: {
165: char msgStr[81];
166:
167: sprintf(msgStr, "There are %i MDI child windows", NumMDIChild);
168: MessageBox(msgStr, "Information", MB_OK | MB_ICONINFORMATION);
169: }
170:
171: BOOL TAppMDIClient::CanClose()
172: {
173: return MessageBox(" close this application?", "Query",
174: MB_YESNO | MB_ICONQUESTION) == IDYES;
175: }
176:
177: void TWinApp::InitMainWindow()
178: {
179: MainWindow = new TMDIFrame("Simple MDI Text Viewer",
180: TResId(IDM_COMMANDS),
181: *new TAppMDIClient);
182: }
183:
184: int OwlMain(int /* argc */, char** /*argv[] */)
185: {
186: TWinApp app;
187: return app.Run();
188: }
189:
The program in Listing 21.4 declares a set of global constants used in generating the random text in each MDI child window. The global array of pointer Words contains the program's somewhat restricted vocabulary. The listing also declares the global variables ExpressClose, NumMDIChild, and HighMDIindex. These variables provide a simple solution for sharing information between the descendants of branched-out OWL classes. The variable ExpressClose assists in closing all of the child MDI windows in one swoop. The variable NumMDIChild maintains the actual number of MDI child windows. The variable HighMDIindex stores the index of the last MDI child window created. The program listing declares three classes: the application class, TWinApp, in line 26; the MDI client class, TAppMDIClient, in line 50; and the MDI child window class, TAppMDIChild, in line 35. We will discuss these classes in order. The code for the application class looks very much like the ones in previous programs, with one exception. The InitMainWindow member function, defined in lines 177 to 182, creates an instance of the stock MDI frame class, TMDIFrame. The TMDIFrame constructor call has the following arguments: title of the application; the name of the menu resource, COMMANDS; and the pointer to the dynamically allocated instances of TAppMDIClient. The TAppMDIClient class declares a constructor and a group of protected member functions. The member functions are as follows: 1. The member function InitChild (defined in lines 142 to 146) initializes an MDI child window. The function increments the global variable NumMDIChild and then returns a dynamically allocated instance of TAppMDIChild. The arguments of creating this instance are *this (a reference to the object itself) and ++HighMDIindex. The second argument pre- increments the global variable HighMDIindex, which keeps track of the highest index for an MDI child window. 2. The member function CloseChildren (defined in lines 148 to 160) alters the behavior of the inherited CloseChildren function. The new version performs the following tasks: n Assigns TRUE to the global variable ExpressClose (see line 152). Invokes the parent class version of CloseChildren and stores the result of that function call in the local variable result. Assigns FALSE to the variable ExpressClose in line 156. Assigns 0 to the global variable NumMDIChild in line 157. Assigns 0 to the global variable HighMDIindex in line 157. This task resets the value in variable HighMDIindex when you close all of the MDI child windows. Returns the value stored in the variable result. 3. The member function CMCountChildren (defined in lines 163 to 168) responds to the Windows command message CM_COUNTCHILDREN generated by the menu option Count Children. The function displays the number of MDI child windows in a message dialog box. The function first builds the string msgStr to contain the formatted image of the global variable NumMDIChild. Then, the function invokes the member function MessageBox to display the sought information. 4. The virtual member function CanClose (defined in lines 171 to 175) prompts you to confirm closing the MDI-compliant application. The MDI child window class, TAppMDIChild, declares the TextBox and TextTxt data members, a constructor, and the CanClose member function. The member TextBox is the pointer to the TEdit instance created to store the random text in one kind of the MDI child windows. The member TextTxt is the pointer to the TStatic instance created to store random text in the other kind of MDI child windows. The TAppMDIChild constructor (defined in lines 78 to 121) performs a variety of tasks, as follows:
The CanClose member function regulates closing an MDI child window. When you close such a window using the Close option in its own system menu, the function requires your confirmation. If the request to close comes from the Close All menu command in the parent window, the MDI child window closes without confirmation. The function decrements the global variable NumMDIChild in two cases: first, when the global variable ExpressClose is TRUE; and second, when the function MessageBox, which prompts you to confirm closing the window, returns IDYES. Revised Text Viewer
The application menu adds a new pop-up menu item, Current MDI Child. This menu item has options that work on the current MDI child window. The commands enable you to clear, convert to uppercase, convert to lowercase, or rewrite the characters in the MDI child window. The new pop-up menu shows how you can manipulate MDI children with custom menus. Compile and run the application. Create a few MDI children and use their pushbutton controls to toggle the case of characters in these windows. Also use the Current MDI Child commands to further manipulate the text in the currently active MDI child window. Try to close the MDI children with the Can Close check box marked and unmarked. Only the MDI children with the Can Close control checked close individually. Use the Close All option in the MDI Children pop-up menu and watch all of the MDI children close, regardless of the check state of the Can Close control. Figure 21.2 shows a sample session with the MDI2.EXE program. Listing 21.5 shows the contents of the MDI2.DEF definition file. Listing 21.6 shows the source code for the MDI2.H header file. The file contains the constants for the menu commands and the control IDs. Listing 21.7 contains the script for the MDI2.RC resource file and shows the resource for the expanded menu. Listing 21.8 contains the source code for the MDI2.CPP program file. Figure 21.2. A sample session with the MDI2.EXE program. Listing 21.5. The contents of the MDI2.DEF definition file. 1: NAME MDI2 2: DESCRIPTION `An OWL Windows Application' 3: EXETYPE WINDOWS 4: CODE PRELOAD MOVEABLE DISCARDABLE 5: DATA PRELOAD MOVEABLE MULTIPLE 6: HEAPSIZE 10247: STACKSIZE 8192 Listing 21.6. The source code for the MDI2.H header file. 1: #define CM_COUNTCHILDREN 101 2: #define CM_CLEAR 102 3: #define CM_UPPERCASE 103 4: #define CM_LOWERCASE 104 5: #define CM_RESET 105 6: #define ID_TEXT_EDIT 106 7: #define ID_CANCLOSE_CHK 107 8: #define ID_UPPERCASE_BTN 108 9: #define ID_LOWERCASE_BTN 10910: #define IDM_COMMANDS 400 Listing 21.7. The script for the MDI2.RC resource file. 1: #include <windows.h> 2: #include <owl\window.rh> 3: #include <owl\mdi.rh> 4: #include "mdi2.h" 5: IDM_COMMANDS MENU LOADONCALL MOVEABLE PURE DISCARDABLE 6: BEGIN 7: MENUITEM "E&xit", CM_EXIT 8: POPUP "&MDI Children" 9: BEGIN 10: MENUITEM " c&reate", CM_CREATECHILD 11: MENUITEM "&Cascade", CM_CASCADECHILDREN 12: MENUITEM "&Tile", CM_TILECHILDREN 13: MENUITEM "Arrange &Icons", CM_ARRANGEICONS 14: MENUITEM " c&lose All", CM_CLOSECHILDREN 15: MENUITEM " c&ount Children", CM_COUNTCHILDREN 16: END 17: POPUP "&Current MDI Child" 18: BEGIN 19: MENUITEM "&Clear", CM_CLEAR 20: MENUITEM "&Uppercase", CM_UPPERCASE 21: MENUITEM "&Lowercase", CM_LOWERCASE 22: MENUITEM "&Reset", CM_RESET 23: END24: END Listing 21.8. The source code for the MDI2.CPP program file.
1: /*
2: Program to demonstrate MDI windows with controls
3: */
4: #include <owl\mdi.rh>
5: #include <owl\applicat.h>
6: #include <owl\framewin.h>
7: #include <owl\button.h>
8: #include <owl\edit.h>
9: #include <owl\checkbox.h>
10: #include <owl\scroller.h>
11: #include <owl\mdi.h>
12: #include "mdi2.h"
13: #include <stdio.h>
14: #include <string.h>
15:
16: // declare constants for sizing and spacing the controls
17: // in the MDI child window
18: const Wbtn = 50 * 3;
19: const Hbtn = 30;
20: const BtnHorzSpacing = 20;
21: const BtnVertSpacing = 10;
22: const Wchk = 200 * 3;
23: const Hchk = 20;
24: const ChkVertSpacing = 10;
25: const Wbox = 400 * 3;
26: const Hbox = 200 * 3;
27:
28: // declare the constants for the random text that appears
29: // in the MDI child window
30: const MaxWords = 200;
31: const WordsPerLine = 10;
32: const NumWords = 10;
33: const BufferSize = 1024;
34: char AppBuffer[BufferSize];
35: char* Words[NumWords] = { "The ", "friend ", "saw ", "the ",
36: "girl ", "drink ", "milk ", "boy ",
37: " cake ", "bread " };
38:
39:
40: BOOL ExpressClose = FALSE;
41: int NumMDIChild = 0;
42: int HighMDIindex = 0;
43:
44: class TWinApp : public TApplication
45: {
46: public:
47: TWinApp() : TApplication() {}
48:
49: protected:
50: virtual void InitMainWindow();
51: };
52:
53: class TAppMDIChild : public TMDIChild
54: {
55: public:
56:
57:
58: TAppMDIChild(TMDIClient& parent, int ChildNum);
59:
60: protected:
61:
62: TEdit* TextBox;
63: TCheckBox* CanCloseChk;
64:
65: // handle the UpperCase button
66: void HandleUpperCaseBtn()
67: { CMUpperCase(); }
68:
69: // handle the LowerCase button
70: void HandleLowerCaseBtn()
71: { CMLowerCase(); }
72:
73: // handle clear the active MDI child
74: void CMClear()
75: { TextBox->Clear(); }
76:
77: // handle converting the text of the active
78: // MDI child to uppercase
79: void CMUpperCase();
80:
81: // handle converting the text of the active
82: // MDI child to lowercase
83: void CMLowerCase();
84:
85: // handle resetting the text of the active MDI child
86: void CMReset();
87:
88: // reset the text in an MDI child window
89: void InitText();
90:
91: // handle closing the MDI child window
92: virtual BOOL CanClose();
93:
94: // declare response table
95: DECLARE_RESPONSE_TABLE(TAppMDIChild);
96: };
97:
98: DEFINE_RESPONSE_TABLE1(TAppMDIChild, TMDIChild)
99: EV_COMMAND(ID_UPPERCASE_BTN, HandleUpperCaseBtn),
100: EV_COMMAND(ID_LOWERCASE_BTN, HandleLowerCaseBtn),
101: EV_COMMAND(CM_CLEAR, CMClear),
102: EV_COMMAND(CM_UPPERCASE, CMUpperCase),
103: EV_COMMAND(CM_LOWERCASE, CMLowerCase),
104: EV_COMMAND(CM_RESET, CMReset),
105: END_RESPONSE_TABLE;
106:
107: class TAppMDIClient : public TMDIClient
108: {
109: public:
110:
111: TAppMDIClient() : TMDIClient() {}
112:
113: protected:
114:
115: // create a new child
116: virtual TMDIChild* InitChild();
117:
118: // close all MDI children
119: virtual BOOL CloseChildren();
120:
121: // handle the command for counting the MDI children
122: void CMCountChildren();
123:
124: // handle closing the MDI frame window
125: virtual BOOL CanClose();
126:
127: // declare response table
128: DECLARE_RESPONSE_TABLE(TAppMDIClient);
129: };
130:
131: DEFINE_RESPONSE_TABLE1(TAppMDIClient, TMDIClient)
132: EV_COMMAND(CM_COUNTCHILDREN, CMCountChildren),
133: END_RESPONSE_TABLE;
134:
135: TAppMDIChild::TAppMDIChild(TMDIClient& parent, int ChildNum)
136: : TMDIChild(parent),
137: TFrameWindow(&parent),
138: TWindow(&parent)
139: {
140: char s[41];
141: int x0 = 10;
142: int y0 = 10;
143: int x = x0;
144: int y = y0;
145:
146: // set the scrollers in the window
147: Attr.Style |= WS_VSCROLL | WS_HSCROLL;
148: // create the TScroller instance
149: Scroller = new TScroller(this, 200, 15, 10, 50);
150:
151: // set MDI child window title
152: sprintf(s, "%s%i", " child #", ChildNum);
153: Title = _fstrdup(s);
154:
155: // create the push button controls
156: new TButton(this, ID_UPPERCASE_BTN, "->UpperCase",
157: x, y, Wbtn, Hbtn, TRUE);
158: x += Wbtn + BtnHorzSpacing;
159: new TButton(this, ID_LOWERCASE_BTN, "->LowerCase",
160: x, y, Wbtn, Hbtn, FALSE);
161:
162: x = x0;
163: y += Hbtn + BtnVertSpacing;
164: CanCloseChk = new TCheckBox(this, ID_CANCLOSE_CHK, " can Close",
165: x, y, Wchk, Hchk, NULL);
166: y += Hchk + ChkVertSpacing;
167: InitText();
168: // create the edit box
169: TextBox = new TEdit(this, ID_TEXT_EDIT, AppBuffer,
170: x, y, Wbox, Hbox, 0, TRUE);
171: // remove borders and scroll bars
172: TextBox->Attr.Style &= ~WS_BORDER;
173: TextBox->Attr.Style &= ~WS_VSCROLL;
174: TextBox->Attr.Style &= ~WS_HSCROLL;
175: }
176:
177: void TAppMDIChild::CMUpperCase()
178: {
179: TextBox->GetText(AppBuffer, BufferSize);
180: strupr(AppBuffer);
181: TextBox->SetText(AppBuffer);
182: }
183:
184: void TAppMDIChild::CMLowerCase()
185: {
186: TextBox->GetText(AppBuffer, BufferSize);
187: strlwr(AppBuffer);
188: TextBox->SetText(AppBuffer);
189: }
190:
191: void TAppMDIChild::CMReset()
192: {
193: InitText();
194: TextBox->SetText(AppBuffer);
195: }
196:
197: BOOL TAppMDIChild::CanClose()
198: {
199: // return TRUE if the ExpressClose member of the
200: // parent MDI frame window is TRUE
201: if (ExpressClose == TRUE) {
202: NumMDIChild--;
203: return TRUE;
204: }
205: else
206: // do not close the MDi child window if the Can Close is
207: // not checked
208: if (CanCloseChk->GetCheck() == BF_UNCHECKED)
209: return FALSE;
210: else {
211: NumMDIChild--;
212: return TRUE;
213: }
214: }
215:
216: void TAppMDIChild::InitText()
217: {
218: // randomize the seed for the random-number generator
219: randomize();
220:
221: // assign a null string to the buffer
222: AppBuffer[0] = `\0';
223: // build the list of random words
224: for (int i = 0;
225: i < MaxWords && strlen(AppBuffer) <= (BufferSize - 10);
226: i++) {
227: if (i > 0 && i % WordsPerLine == 0)
228: strcat(AppBuffer, "\r\n");
229: strcat(AppBuffer, Words[random(NumWords)]);
230: }
231: }
232:
233: TMDIChild* TAppMDIClient::InitChild()
234: {
235: ++NumMDIChild;
236: return new TAppMDIChild(*this, ++HighMDIindex);
237: }
238:
239: BOOL TAppMDIClient::CloseChildren()
240: {
241: BOOL result;
242: // set the ExpressClose flag
243: ExpressClose = TRUE;
244: // invoke the parent class CloseChildren() member function
245: result = TMDIClient::CloseChildren();
246: // clear the ExpressClose flag
247: ExpressClose = FALSE;
248: NumMDIChild = 0;
249: HighMDIindex = 0;
250: return result;
251: }
252:
253: // display a message box that shows the number of children
254: void TAppMDIClient::CMCountChildren()
255: {
256: char msgStr[81];
257:
258: sprintf(msgStr, "There are %i MDI children", NumMDIChild);
259: MessageBox(msgStr, "Information", MB_OK | MB_ICONINFORMATION);
260: }
261:
262: BOOL TAppMDIClient::CanClose()
263: {
264: return MessageBox(" close this application?",
265: "Query", MB_YESNO | MB_ICONQUESTION) == IDYES;
266: }
267:
268: void TWinApp::InitMainWindow()
269: {
270: MainWindow = new TMDIFrame("Simple MDI Text Viewer (version 2)",
271: TResId(IDM_COMMANDS),
272: *new TAppMDIClient);
273: }
274:
275: int OwlMain(int /* argc */, char** /*argv[] */)
276: {
277: TWinApp app;
278: return app.Run();
279:
}
The program in Listing 21.8 declares two sets of constants. The first set is used for sizing and spacing the controls of each MDI child window. The second set of constants is used to manage the random text. The program also declares variable AppBuffer as a single 1KB text buffer. We chose to make the buffer global instead of a class data member mainly to reduce the buffer space--the application classes need only one shared buffer at any time. The program listing also declares the global variables ExpressClose, NumMDIChild, and HighMDIindex--another set of components carried over from the program in file MDI1.CPP. The new application maintains the same three classes described in the last program. However, the MDI child class has different members in this program. The new members manage the response to the control notification messages as well as the Current MDI Child menu command messages. The TAppMDIChild constructor (defined in lines 135 to 175) performs the following tasks:
The member function CMUpperCase (defined in 177 lines to 182) responds to the command message emitted by the UpperCase command. The function copies the text in the MDI child window to the application's buffer, converts the characters in the buffer to uppercase, and then writes the buffer back to the MDI child window. The member function CMLowerCase (defined in lines 184 to 189) responds to the command message emitted by the Lowercase command. The function performs similar steps to those in CMUpperCase--except the text is converted into lowercase. The member function CanClose (defined in lines 197 to 214) responds to the WM_CLOSE message emitted by the Close option in the system menu available in each MDI child window. If the MDI frame window's ExpressClose variable is TRUE, the function decrements the global variable NumMDIChild and then returns TRUE. Otherwise, the function returns FALSE if the Can Close check box is unchecked, or it decrements the global variable NumMDIChild and then returns FALSE if the control is not checked. The member function InitText (defined in lines 233 to 237) is an auxiliary routine that fills the application buffer with random text. The function creates up to MaxWords words or enough that the buffer limit is closely reached (within 10 bytes). Checking the number of characters in the buffer ensures that the program does not corrupt the memory while attempting to add MaxWords words to the buffer. The member functions HandleUpperCaseBtn and HandleLowerCase respond to the notification messages sent by the pushbuttons of an MDI child window. These functions perform the same tasks of CMUpperCase and CMLowerCase, respectively. Therefore, the notification response functions call their respective command-message response member functions. The member function CMClear (defined in lines 74 and 75) responds to the command message emitted by the Clear command in the Current MDI Child menu item. The function simply invokes the TextBox->Clear() function call. The member function CMReset (defined in lines 191 to 195) responds to the command message emitted by the Reset command in the Current MDI Child menu item. The function calls the InitText member function to create a new batch of random text and then copies the buffer's text to the edit control of the MDI child window. Note: The Current MDI Child pop-up menu has four options that manipulate the currently active MDI child window. The command messages emitted by these options are handled by the MDI child window instances and not the MDI frame instance--which is what a window instance normally does regarding its own menu commands. This order of handling the command messages is preferred and makes use of the fact that the menu-based messages do reach the currently active MDI child window first. You can rewrite the program such that the functions CMClear, CMUpperCase, CMLowerCase, and CMReset appear as member functions of class TAppMDIFrame. Summary
Q Should each MDI child window have an ID? A Yes. Associating each MDI child window with an ID gives you more control over managing these windows, especially if they vary in relevance. Thus, you can use the ID to exclude special MDI child windows from collective operations. Q Can I hide MDI child windows? A Yes, you can use the inherited member function Twindow::ShowWindow to show and hide one MDI child window or more. The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to understand the quiz and exercise answers before continuing on to the bonus chapters. Answers are provided in Appendix A, "Answers." 1. True or false? MDI child windows can have their own menus. 2. True or false? MDI child windows can be moved outside the area of the frame window. 3. True or false? The MFC library supports nested MDI child windows. 4. True or false? This is the last quiz question in this book! 1. Experiment with the expanding vocabulary of programs MDI1.EXE and MDI2.EXE. 2. Add a control that inserts the date and time in MDI child windows of program MDI1.EXE. Return to C++ Bibliography |
|
|
|
|||||
| Made in Borland® Copyright© 1994-2002 Borland Software Corporation. All rights reserved. Report Piracy, Legal Notices, Privacy Policy Last Modified Tuesday, 05-Feb-2002 10:07:06 EST |
|||||