Programming Microsoft Visual C++

Before you look at the MFC library code for MDI applications, you should be familiar with the operation of Microsoft Windows MDI programs. Take a close look at Visual C++ now. It's an MDI application whose "multiple documents" are program source code files. Visual C++ is not the most typical MDI application, however, because it collects its documents into projects. It's better to examine Microsoft Word or, better yet, a real MFC library MDI application—the kind that AppWizard generates.

A Typical MDI Application, MFC Style

This chapter's example, EX18A, is an MDI version of EX17A. Run the EX17A example to see an illustration of the SDI version after the user has selected a file. Now look at the MDI equivalent, as shown in Figure 18-1.

Figure 18-1. The EX18A application with two files open and the Window menu shown.

The user has two separate document files open, each in a separate MDI child window, but only one child window is active—the lower window, which lies on top of the other child window. The application has only one menu and one toolbar, and all commands are routed to the active child window. The main window's title bar reflects the name of the active child window's document file.

The child window's minimize box allows the user to reduce the child window to an icon in the main window. The application's Window menu (shown in Figure 18-1) lets the user control the presentation through the following items.

Menu ItemAction
New WindowOpens as an additional child window for the selected document
CascadeArranges the existing windows in an overlapped pattern
TileArranges the existing windows in a nonoverlapped, tiled pattern
Arrange IconsArranges minimized windows in the frame window
(document names) Selects the corresponding child window and brings it to the top

If the user closes both child windows (and opens the File menu), the application looks like Figure 18-2.

Figure 18-2. EX18A with no child windows.

The File menu is different, most toolbar buttons are disabled, and the window caption does not show a filename. The only choices the user has are to start a new document or to open an existing document from disk.

Figure 18-3 shows the application when it first starts up and a new document is created. The single child window has been maximized.

Figure 18-3. EX18A with initial child window.

The single, empty child window has the default document name Ex18a1. This name is based on the Doc Type Name (Ex18a) that you selected in the Advanced Options dialog after clicking the Advanced button in Step 4 of AppWizard. The first new file is Ex18a1, the second is Ex18a2, and so forth. The user normally chooses a different name when saving the document.

An MFC library MDI application, like many commercial MDI applications, starts up with a new, empty document. (Visual C++ is an exception.) If you want your application to start up with a blank frame, you can modify the argument to the ProcessShellCommand call in the application class file, as shown in example EX18A.

For Win32 Programmers

Starting with version 3.0, Windows directly supports MDI applications. The MFC library builds on this Windows support to create an MDI environment that parallels the SDI environment. In a Win32 MDI application, a main application frame window contains the menu and a single client window. The client window manages various child windows that correspond to documents. The MDI client window has its own preregistered window class (not to be confused with a C++ class) with a procedure that handles special messages such as WM_MDICASCADE and WM_MDITILE. An MDI child window procedure is similar to the window procedure for an SDI main window.

In the MFC library, the CMDIFrameWnd class encapsulates the functions of both the main frame window and the MDI client window. This class has message handlers for all the Windows MDI messages and thus can manage its child windows, which are represented by objects of class CMDIChildWnd.

The MDI Application Object

You're probably wondering how an MDI application works and what code makes it different from an SDI application. Actually, the startup sequences are pretty much the same. An application object of a class derived from class CWinApp has an overridden InitInstance member function. This InitInstance function is somewhat different from the SDI InitInstance function, starting with the call to AddDocTemplate.

The MDI Document Template Class

The MDI template construction call in InitInstance looks like this:

CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_EX18ATYPE, RUNTIME_CLASS(CStudentDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CStudentView)); AddDocTemplate(pDocTemplate);

Unlike the SDI application you saw in Chapter 17, an MDI application can use multiple document types and allows the simultaneous existence of more than one document object. This is the essence of the MDI application.

The single AddDocTemplate call shown above permits the MDI application to support multiple child windows, each connected to a document object and a view object. It's also possible to have several child windows (and corresponding view objects) connected to the same document object. In this chapter, we'll start with only one view class and one document class. You'll see multiple view classes and multiple document classes in Chapter 20.

When your application is running, the document template object maintains a list of active document objects that were created from the template. The CMultiDocTemplate member functions GetFirstDocPosition and GetNextDoc allow you to iterate through the list. Use CDocument::GetDocTemplate to navigate from a document to its template.

The MDI Frame Window and the MDI Child Window

The SDI examples had only one frame window class and only one frame window object. For SDI applications, AppWizard generated a class named CMainFrame, which was derived from the class CFrameWnd. An MDI application has two frame window classes and many frame objects, as shown in the table below. The MDI frame-view window relationship is shown in Figure 18-4.

Base ClassAppWizard-Generated ClassNumber of ObjectsMenu and Control BarsContains a ViewObject Constructed
CMDIFrameWndCMainFrame1 onlyYesNoIn application class's InitInstance function
CMDIChildWndCChildFrame1 per child window NoYesBy application framework when a new child window is opened

Figure 18-4. The MDI frame-view window relationship.

In an SDI application, the CMainFrame object frames the application and contains the view object. In an MDI application, the two roles are separated. Now the CMainFrame object is explicitly constructed in InitInstance, and the CChildFrame object contains the view. AppWizard generates the following code:

CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame;

(code calls ProcessShellCommand to create child frame)

pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow();

The application framework can create the CChildFrame objects dynamically because the CChildFrame runtime class pointer is passed to the CMultiDocTemplate constructor.

The MDI InitInstance function sets the CWinApp data member m_pMainWnd to point to the application's main frame window. This means you can access m_pMainWnd through the global AfxGetApp function anytime you need to get your application's main frame window.

The Main Frame and Document Template Resources

An MDI application (EX18A, as described later in this chapter) has two separate string and menu resources, identified by the IDR_MAINFRAME and IDR_EX18ATYPE constants. The first resource set goes with the empty main frame window; the second set goes with the occupied main frame window. Here are the two string resources with substrings broken out:

IDR_MAINFRAME "ex18a" // application window caption IDR_EX18ATYPE "\n" // (not used) "Ex18a\n" // root for default document name "Ex18a\n" // document type name "Ex18a Files (*.18a)\n" // document type description and filter ".18a\n" // extension for documents of this type "Ex18a.Document\n" // Registry file type ID "Ex18a Document" // Registry file type description

The resource compiler won't accept the string concatenations as shown above. If you examine the ex18a.rc file, you'll see the substrings combined in one long string.

The application window caption comes from the IDR_MAINFRAME string. When a document is open, the document filename is appended. The last two substrings in the IDR_EX18ATYPE string support embedded launch and drag and drop.

Creating an Empty Document—The CWinApp::OnFileNew Function

The MDI InitInstance function calls OnFileNew (through ProcessShellCommand), as did the SDI InitInstance function. This time, however, the main frame window has already been created. OnFileNew, through a call to the CMultiDocTemplate function OpenDocumentFile, now does the following:

  1. Constructs a document object but does not attempt to read data from disk.

  2. Constructs a child frame window object (of class CChildFrame). Also creates the child frame window but does not show it. In the main frame window, the IDR_EX18ATYPE menu replaces the IDR_MAINFRAME menu. IDR_EX18ATYPE also identifies an icon resource that is used when the child window is minimized within the frame.

  3. Constructs a view object. Also creates the view window but does not show it.

  4. Establishes connections among the document, the main frame, and view objects. Do not confuse these object connections with the class associations established by the call to AddDocTemplate.

  5. Calls the virtual OnNewDocument member function for the document object.

  6. Calls the virtual OnInitialUpdate member function for the view object.

  7. Calls the virtual ActivateFrame member function for the child frame object to show the frame window and the view window.

The OnFileNew function is also called in response to the File New menu command. In an MDI application, OnFileNew performs exactly the same steps as it does when called from InitInstance.

Some functions listed above are not called directly by OpenDocumentFile but are called indirectly through the application framework.

Creating an Additional View for an Existing Document

If you choose the New Window command from the Window menu, the application framework opens a new child window that is linked to the currently selected document. The associated CMDIFrameWnd function, OnWindowNew, does the following:

  1. Constructs a child frame object (of class CChildFrame). Also creates the child frame window but does not show it.

  2. Constructs a view object. Also creates the view window but does not show it.

  3. Establishes connections between the new view object and the existing document and main frame objects.

  4. Calls the virtual OnInitialUpdate member function for the view object.

  5. Calls the virtual ActivateFrame member function for the child frame object to show the frame window and the view window.

Loading and Storing Documents

In MDI applications, documents are loaded and stored the same way as in SDI applications but with two important differences: a new document object is constructed each time a document file is loaded from disk, and the document object is destroyed when the child window is closed. Don't worry about clearing a document's contents before loading—but override the CDocument::DeleteContents function anyway to make the class portable to the SDI environment.

Multiple Document Templates

An MDI application can support multiple document templates through multiple calls to the AddDocTemplate function. Each template can specify a different combination of document, view, and MDI child frame classes. When the user chooses New from the File menu of an application with multiple templates, the application framework displays a list box that allows the user to select a template by name as specified in the string resource (document type substring). Multiple AddDocTemplate calls are not supported in SDI applications because the document, view, and frame objects are constructed once for the life of the application.

When your application is running, the application object keeps a list of active document template objects. The CWinApp member functions GetFirstDocTemplatePosition and GetNextDocTemplate allow you to iterate through the list of templates. These functions, together with the CDocTemplate member functions GetFirstDocPosition and GetNextDoc, allow you to access all of the application's document objects.

If you don't want the template list box, you can edit the File menu to add a New menu item for each document type. Code the command message handlers as shown below, using the document type substring from each template.

void CMyApp::OnFileNewStudent() { OpenNewDocument("Studnt"); } void CMyApp::OnFileNewTeacher() { OpenNewDocument("Teachr"); }

Then add the OpenNewDocument helper function as follows:

BOOL CMyApp::OpenNewDocument(const CString& strTarget) { CString strDocName; CDocTemplate* pSelectedTemplate; POSITION pos = GetFirstDocTemplatePosition(); while (pos != NULL) { pSelectedTemplate = (CDocTemplate*) GetNextDocTemplate(pos); ASSERT(pSelectedTemplate != NULL); ASSERT(pSelectedTemplate->IsKindOf( RUNTIME_CLASS(CDocTemplate))); pSelectedTemplate->GetDocString(strDocName, CDocTemplate::docName); if (strDocName == strTarget) { // from template's // string resource pSelectedTemplate->OpenDocumentFile(NULL); return TRUE; } } return FALSE; }

Explorer Launch and Drag and Drop

When you double-click on a document icon for an MDI application in Windows Explorer, the application launches only if it was not running already; otherwise, a new child window opens in the running application for the document you selected. The EnableShellOpen call in the application class InitInstance function is necessary for this to work. Drag and drop works much the same way in an MDI application as it does in an SDI application. If you drag a file from Windows Explorer to your MDI main frame window, the program opens a new child frame (with associated document and view) just as if you'd chosen the File Open command. As with SDI applications, you must use the AppWizard Step 4 Advanced button to specify the filename extension.

Категории