MDI Support
In addition to SDI applications, MFC supports MDI applications. In this section, we'll look at MDI applications and see how they read and write their document files. For a long time, MDI applications were the preferred MFC library program style. It's the MFC Application Wizard default, and most of the sample programs that come with Visual C++ are MDI applications.In addition, you'll learn the similarities and differences between SDI and MDI applications, and you'll learn how to convert an SDI application to an MDI application. Be sure that you thoroughly understand the SDI application described earlier in this chapter before you attack the MDI application in this section.Before you look at the MFC library code for MDI applications, you should be familiar with the operation of Windows MDI programs. Take a close look at Visual C++ .NET now. It's an MDI application whose "multiple documents" are program source code files. Visual C++ .NET 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 the MFC Application Wizard generates.
A Typical MDI Application, MFC Style
Ex16b is an MDI version of Ex16a. Figure 16-4 shows the Ex16b program in use.

Figure 16-4: The Ex16b application with two files open.
The user has two separate document files open, each in a separate MDI child window. Only one child window is active at a time. 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 16-4) lets the user control the presentation through the following commands.
Menu Command | Action |
---|---|
New Window | Opens as an additional child window for the selected document |
Cascade | Arranges the existing windows in an overlapped pattern |
Tile | Arranges the existing windows in a nonoverlapped, tiled pattern |
Arrange Icons | Arranges minimized windows in the frame window |
(document names) | Selects the corresponding child window and brings it to the top |
The menus and toolbars in an MDI application are dynamic. When all the windows in an MDI application are closed, the File menu changes, most toolbar buttons are disabled, and the window caption does not show a filename. The only choice the user has is to start a new document or to open an existing document from disk.As the user creates new files, the empty child window gets the default document name Ex16b1. This name is based on the Doc Type Name (Ex16b) selected in the Document Template Strings page of the MFC Application Wizard. The first new file is Ex16b1, the second is Ex16b2, 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++ .NET 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 Ex16b.
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_EX16BTYPE,
RUNTIME_CLASS(CEx16bDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CEx16b));
AddDocTemplate(pDocTemplate);
Unlike Ex16a, 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 in the previous example 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 18.
Note | 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, the MFC Application Wizard 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 following table. The MDI frame–view window relationship is shown in Figure 16-5.
Base Class | MFC Application Wizard– Generated Class | Number of Objects | Menu and Control Bars | Contains a View | Object Constructed |
---|---|---|---|---|---|
CMDIFrameWnd | CMainFrame | 1 only | Yes | No | In application class's InitInstance function |
CMDIChildWnd | CChildFrame | 1 per child window | No | Yes | By application framework when a new child window is opened |

Figure 16-5: 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. The MFC Application Wizard generates the following code:
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;![]()
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.
Note | 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 (such as Ex16b, described later in this chapter) has two separate string and menu resources, identified by the IDR_MAINFRAME and IDR_EX16BTYPE 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
"Ex16b" // application window caption
IDR_EX16BTYPE
"\n" // (not used)
"Ex16b\n" // root for default document name
"Ex16b\n" // document type name
"Ex16b Files (*.16b)\n" // document type description and filter
".16b\n" // extension for documents of this type
"Ex16b.Document\n" // Registry file type ID
"Ex16b.Document" // Registry file type description
Note | The resource compiler won't accept the string concatenations as shown here. If you examine the ![]() |
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_EX16BTYPE string support embedded launch and drag and drop.
Creating an Empty Document
The CWinApp::OnFileNew function enables you to create an empty document. 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:
Constructs a document object but does not attempt to read data from disk.
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_EX16BTYPE menu replaces the IDR_MAINFRAME menu. IDR_EX16BTYPE also identifies an icon resource that is used when the child window is minimized within the frame.
Constructs a view object. Also creates the view window but does not show it.
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.
Calls the virtual OnNewDocument member function for the document object.
Calls the virtual OnInitialUpdate member function for the view object.
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.
Note | 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:
Constructs a child frame object (of class CChildFrame). Also creates the child frame window but does not show it.
Constructs a view object. Also creates the view window but does not show it.
Establishes connections between the new view object and the existing document and main frame objects.
Calls the virtual OnInitialUpdate member function for the view object.
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.
Note | 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 command for each document type. Code the command message handlers as shown here, 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 Document Template Strings page of the MFC Application Wizard to specify the filename extension.