![]() |
The Document/View Architecture |
The Document/View architecture is the foundation used to create applications based on the Microsoft Foundation Classes library. It allows you to make distinct the different parts that compose a computer program including what the user sees as part of your application and the document a user would work on. This is done through a combination of separate classes that work as an ensemble. The parts that compose the Document/View architecture are a frame, one or more documents, and the view. Put together, these entities make up a usable application.
A view is the platform the user is working on to do his or her job. For example, while performing word processing, the user works on a series of words that compose the text. If a user is performing calculations on a spreadsheet application, the interface the user is viewing is made of small boxes called cells. Another user may be in front of a graphic document while drawing lines and other geometric figures. The thing the user is starring at and performing changes is called a view. The view also allows the user to print a document. To let the user do anything on an application, you must provide a view, which is an object based on the CView class. You can either directly use one of the classes derived from CView or you can derive your own custom class from CView or one of its child classes.
A document is similar to a bucket. It can be used to hold or carry water and that water can be retrieved when needed. For a computer application, a document holds the user's data. For example, after working on a text processor, the user may want to save the file. Such an action creates a document and this document must reside somewhere. In the same way, to use an existing file, the user must locate it, open it, and make it available to the application. These two jobs and many others are handled behind the scenes as a document. To create the document part of this architecture, you must derive an object from the CDocument class.
As its name suggests, a frame is a combination of the building blocks, the structure (in the English sense), and the borders of an item. A frame gives "physical" presence to a window. A frame defines the location of an object with regards to the Windows desktop. A frame provides borders to a window, borders that the user can grab to move, size, and resize the object. The frame is also a type of desk that holds the tools needed on an application. An application cannot exist without a frame. As we saw in previous lessons, to provide a frame to an application, you can derive a class from CFrameWnd.
To create an application, you obviously should start by providing a frame. This can be taken care of by deriving a class from CFrameWnd. Here is an example: class CMainFrame : public CFrameWnd
{
DECLARE_DYNCREATE(CMainFrame)
DECLARE_MESSAGE_MAP()
};
To give "physical" presence to the frame of an application, you can declare an OnCreate() method. Here is an example: class CMainFrame : public CFrameWnd
{
DECLARE_DYNCREATE(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
The easiest way you can implement this method is to call the parent class, CFrameWnd, to create the window. As we have seen in the past, if this method returns 0, the frame has been created. It returns -1, this indicates that the window has been destroyed. Therefore, you can create a frame as follows: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// Call the base class to create the window
if( CFrameWnd::OnCreate(lpCreateStruct) == 0)
return 0;
// else is implied
return -1;
}
To allow users to interact with your application, you should provide a document. To do this, you can derive a class from CDocument so you can take advantage of this class. If you do not plan to do anything with the document, you can just make it an empty class. Here is an example: class CExerciseDoc : public CDocument
{
DECLARE_DYNCREATE(CExerciseDoc)
DECLARE_MESSAGE_MAP()
};
Besides the few things we have learned so far, your next big decision may consist on the type of application you want to create. This is provided as a view. The most fundamental class of the view implementations in the MFC is CView. Because CView is an abstract class, you cannot directly use it in your application. You have two main alternatives. You can derive your own class based on CView (the CView class itself is based on CWnd) or you can use one of the many view classes that the MFC provides. The classes that are readily available to you are:
As we move on, we will study these classes as needed. Once you have a frame, a document, and a view, you can create an application, which, as we have learned so far, is done by deriving a class from CWinApp and overriding the InitInstance() method. In the InitInstance() implementation, you must let the compiler know how a document is created in your application. To do this, you must provide a sample document, called a template, that defines the parts that constitute a document for your particular type of application. This is done using a pointer variable declared from CDocTemplate or one of its derived classes.
The expression Single Document Interface or SDI refers to a document that can present only one view to the user. This means that the application cannot display more than one document at a time. If the user wants to view another type of document of the current application, he or she must another instance of the application. Notepad and WordPad are examples of SDI applications:
Notepad can be used to open only one document such as an HTML file, to view another file, the user would either replace the current document or launch another copy of Notepad. To create an SDI, Microsoft Visual C++ provides the MFC wizard which provides all the basic functionality that an application needs, including the resources and classes.
As mentioned earlier, after creating a frame, a document, and a view, you can create an application by deriving a class from CWinApp and overriding the virtual InitInstance() member function. In InitInstance(), you must provide a template for your type of application. This is done using a CDocTemplate type of object. To create an SDI, the CDocTemplate class provides the CSingleDocTemplate used to create an application that provides only one view. Therefore, you can declare a pointer variable to CSingleDocTemplate. Using this pointer and the new operator, use the CSingleDocTemplate constructor to provide the template. The syntax of the CSingleDocTemplate constructor is: CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass); The CSingleDocTemplate constructor needs the common identifier for the resources of your application. We saw in Lesson 3 that this can be done by using IDR_MAINFRAME as the common name of most or all main resources of an application. This is provided as the nIDResource argument. The second argument, pDocClass, is the name of the class you derived from CDocument, as mentioned earlier. The third argument, pFrameClass, if the frame class you derived from either CFrameWnd or one of its children. The pViewClass argument can be an MFC CView-derived class. It can also be a class you created based on CView. Each of these arguments must be passed as a pointer to CRuntimeClass. This can be taken care of by using the RUNTIME_CLASS macro. Its syntax is: RUNTIME_CLASS(ClassName); Each one of the classes you want to use must be provided as the ClassName argument. The RUNTIME_CLASS macro in turn returns a pointer to CRuntimeClass. To effectively use the RUNTIME_CLASS macro, you should make sure that the (each) class is created and implemented using the DECLARE_DYNAMIC, the DECLARE_DYNCREATE, or the DECLARE_SERIAL macros. To actually create the application so it can be displayed to the user, the CWinApp class is equipped with the AddDocTemplate() method. Therefore, After creating a template, pass the CSingleDocTemplate pointer to the CWinApp::AddDocTemplate() method. Its syntax is: void AddDocTemplate(CDocTemplate *pTemplate); Everything else is subject to how you want your application to provide a useful experience to the user.
|
|
|




#include <afxext.h> // For CEditView
#include "resource.h"
class CExerciseApp : public CWinApp
{
BOOL InitInstance();
DECLARE_MESSAGE_MAP()
};
class CMainFrame : public CFrameWnd
{
DECLARE_DYNCREATE(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
class CExerciseDoc : public CDocument
{
DECLARE_DYNCREATE(CExerciseDoc)
DECLARE_MESSAGE_MAP()
};
class CExerciseView : public CEditView
{
DECLARE_DYNCREATE(CExerciseView)
DECLARE_MESSAGE_MAP()
};
|
#include <afxwin.h>
#include "Exercise.h"
BEGIN_MESSAGE_MAP(CExerciseApp, CWinApp)
END_MESSAGE_MAP()
BOOL CExerciseApp::InitInstance()
{
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CExerciseDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CExerciseView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// -- Frame Map -- //
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()
// -- Document Map -- //
IMPLEMENT_DYNCREATE(CExerciseDoc, CDocument)
BEGIN_MESSAGE_MAP(CExerciseDoc, CDocument)
END_MESSAGE_MAP()
// -- View Map --
IMPLEMENT_DYNCREATE(CExerciseView, CEditView)
BEGIN_MESSAGE_MAP(CExerciseView, CEditView)
END_MESSAGE_MAP()
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// Call the base class to create the window
if( CFrameWnd::OnCreate(lpCreateStruct) == 0)
return 0;
return -1;
}
CExerciseApp theApp;
|

|
SDI Improvements |
|
SDI Improvements: The Application |
To make your programming experience a little faster and efficient, the framework provides many other features for each class used in an application.
The Application: The programs we will create in this book use classes of the Microsoft Foundation Classes (MFC) library. MFC classes are created is various libraries called DLLs. In order to use MFC objects in your application as opposed to non-MFC objects, you must let the compiler know. This is done by specifying that you want to Use MFC In A Shared DLL, as we have done so far. Additionally, if you want your windows to have a 3-D appearance, call the Enable3dControls() method. If you do not want the 3-D appearance, call the Enable3dControlsStatic() method. The best way to deal with this is to ask the compiler to check if you had allowed using MFC in a shared DLL or not, and then tell the compiler which of these two functions to execute. This is done using a #ifdef preprocessor in your InitInstance() method. Here is an example:
#include <afxwin.h>
class CSimpleFrame : public CFrameWnd
{
public:
CSimpleFrame()
{
// Create the window's frame
Create(NULL, "Windows Application");
}
};
class CSimpleApp : public CWinApp
{
public:
BOOL InitInstance();
};
BOOL CSimpleApp::InitInstance()
{
#ifdef _AFXDLL
Enable3dControls( );
#else
Enable3dControlsStatic();
#endif
CSimpleFrame *Tester = new CSimpleFrame ();
m_pMainWnd = Tester;
Tester->ShowWindow(SW_SHOW);
Tester->UpdateWindow();
return TRUE;
}
CSimpleApp theApp;
To provide your application the ability to create a new document, the CWinApp class provides the OnFileNew() method. Its syntax is:
afx_msg void OnFileNew();
To use this method, create a menu item identified as ID_FILE_NEW. You should also create a prompt for it so the menu item can be added to the string table. This menu item is traditionally and obviously added to the File menu. After creating this menu item, in the message table of the application's source, invoke the CWinApp::OnFileNew() method using the ON_COMMAND() macro. This can be done as follows:
BEGIN_MESSAGE_MAP(CExoApp, CWinApp)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
END_MESSAGE_MAP()
CWinApp also provides an application the ability to easily open a document. This is done using the OnFileOpen() method. In the same way, it can help with printing a document. Here is a summary:
| Menu ID | CWinApp Message Map | |
| ID_FILE_NEW | ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) | |
| ID_FILE_OPEN | ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) | |
| ID_FILE_PRINT_SETUP | ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup |
One of the last minute assignment you may need to perform when the user is closing an application is to check if the displayed document is "dirty", that is, if the document has been changed since it was last accessed. To help with this, simply create a menu item identified as ID_APP_EXIT and set a caption accordingly, such as the Exit menu we created in the previous Practical Learning section. It is always helpful to add a prompt to a menu item.
These command messages are implemented in the CWinApp class and can be helpful for your application. If their behavior does not fulfill your goal, you can write your own intended implementation of these menu items.
When using an application over and over, sometimes a user may want to open the last accessed document or at least see a list of the last documents opened on an application. To provide this functionality, create a menu item called ID_FILE_MRU_FILE1 and set its prompt to a string such as Recent File. This menu item is usually added to the File menu above the Exit or quit. The actual list of recent files is stored in an INI file that accompanies your application. To make this list available, you must call the LoadStdProfileSettings() method of the CWinApp class in your InitInstance() method. The syntax of this method is:
void LoadStdProfileSettings(UINT nMaxMRU = _AFX_MRU_COUNT);
By default, this allows the list to display up to four names of documents. This method takes one argument as the number of document names to be displayed in the list. If you do not want the default of 4, specify the nMaxMRU value to your liking.
|
|

| Caption | ID | Prompt |
| &New\tCtrl+N | ID_FILE_NEW | Create a new document |
| &Open...\tCtrl+O | ID_FILE_OPEN | Open an existing document |
| - | ||
| P&rint Setup... | ID_FILE_PRINT_SETUP | Change the printer and printing options |
| - | ||
| Recent file | ID_FILE_MRU_FILE1 | Open this file |
| - | ||
| E&xit | ID_APP_EXIT | Quit the application; prompt the save document |
BEGIN_MESSAGE_MAP(CExerciseApp, CWinApp)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
BOOL CExerciseApp::InitInstance()
{
#ifdef _AFXDLL
Enable3dControls( );
#else
Enable3dControlsStatic();
#endif
LoadStdProfileSettings(6);
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CExerciseDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CExerciseView));
AddDocTemplate(pDocTemplate);
CCommandLineInfo cmdInfo;
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
|
|
SDI Improvements: The Document |
The document is actually the object that holds the contents of a file. Based on this role, it is its responsibility to validate the creation of a new file or to store a file that is being saved. To perform these tasks and others, the CDocument class provides various methods you can conveniently add to your application or add and customize their behavior.
Earlier, we saw that, to give the user a convenient means of creating a new document, you can add an ID_FILE_NEW menu identifier and connect it to your application class in the InitInstance() method. Clicking this menu item only allows to initiate the action, the document that is the base of file contents must be aware and validate this action. When a user decides to create a new document or when the application opens and is about to create a new document, you may want to make sure that there is no existing document or you may want to delete the existing one. To take care of this, the CDocument class provides the virtual OnNewDocument() method. Its syntax is:
virtual BOOL OnNewDocument();
When a new file is about to be created, this method is called to initiate it. If everything goes fine and the file is created, the OnNewDocument() method returns TRUE. If the file cannot be initialized, this method returns FALSE or 0. This method, which really behaves like an event, does not create a new file. It is launched when a new file is going to be created and allows you to make it happen or to prevent the creation of a new file.
When a new file has been created, it displays as empty. Such a document is referred to as "clean".
We also saw earlier that, to help the user open an existing document, you can create a menu item identified as ID_FILE_OPEN and associate it with the CWinApp::OnFileOpen() method in your InitInstance() method. This time also, the menu item only provides a convenient way to perform the action. It makes the document available to the application and not to the document. Once a user has initiated the action of opening an existing file, you may want to check that the document not only exists but also can be opened and make its contents available to the user. This job can be handled by the OnOpenDocument() virtual method of the CDocument class. Its syntax is:
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
This method usually results from the user clicking File -> Open... on the main menu, communicating a desire to open a file. When this action is initiated, the OnOpenDocument() method retrieves the path of the file as the lpszPathName argument. If the path is valid, that is, if the file exists, you can then check it or perform a last minute task before the file is opened. For example you can use this method to decide how the contents of the file will be displayed or dealt with by the document. You can also use to prevent the user from opening any file or to prevent the user from opening any file at all. You can also use this method to allow the user to open a type of file that your CDocument-derived class would not expect.
If the user has opened an existing file but has not (yet) changed anything in the document, the file is also called "clean". As we will learn eventually, some files can be changed and some do not allow modification. If a document allows the user to change it, he or she can manipulate it as necessary, including adding, deleting, or moving items. Once a user has changed anything on the document, the file is referred to as "dirty". You may want to keep track of such change(s) so you would know eventually if the document needs to be saved. To help you with this, the CDocument class provides the SetModifiedFlag() method. Its syntax is:
void SetModifiedFlag(BOOL bModified = TRUE);
To mark a document as clean or dirty, call the SetModifiedFlag() method. If you pass the bModified argument as TRUE, the document has been changed. Since the TRUE constant is its default value, you can also call the method simply as SetModifiedFlag(). To specify that the document is clean, pass the argument as FALSE. You can call this method whenever you judge necessary. For example, if the user saves the document while working on it but makes another change, you can mark it clean when it has just been saved and mark it dirty if the user changes anything again. At any time, you can check whether the document is dirty or clean using the CDocument::IsModified() method. Its syntax is:
BOOL IsModified();
This method simply checks the document to find out if it has been modified since the last time it was accessed. If the document has been modified, this method would return TRUE. If the document is clean, it returns FALSE.
Another action the user can perform on a document is to send it electronically to an email recipient. To allow the user to send the current document as an email attachment, first an email client (such as MS Outlook) must be installed on the user's computer. Therefore, add a menu item IDentified as ID_FILE_SEND_MAIL. Then, in the message table of your document implementation, add the following two macros:
BEGIN_MESSAGE_MAP(CTestDoc, CDocument)
ON_COMMAND(ID_FILE_SEND_MAIL, OnFileSendMail)
ON_UPDATE_COMMAND_UI(ID_FILE_SEND_MAIL, OnUpdateFileSendMail)
END_MESSAGE_MAP
Once a user has finished using a document, he or she would
need to close it, which is done by either clicking the System Close button
or using the main menu (File -> Exit). If the user clicks the System Close
button
,
the application would need to close the frame. At this time, the document would
call the OnCloseDocument() method. Its syntax is:
virtual void OnCloseDocument();
You can use this method (which really behave as an event) to decide what to do before a frame is closed.
When the user decides to close an application, the document class checks the file to know whether the file is "dirty". If the file is dirty, you may want to ask the user to save or not save the document. As we saw earlier with the ID_APP_EXIT pre-configured menu, the framework can check this setting for you. Also, while using a file, the user may want to save it. If the user is working on a document that was opened from a drive, the document may be saved immediately behind the scenes. If the user is working on a brand new document and decides to save it, you may want to check first if this is possible and what needs to be done in order to save the document.
To help the user save a document, you can create a menu item. Using the Document/View architecture, add a menu item with the identifier ID_FILE_SAVE in the IDR_MAINFRAME common resource name. It is that simple. This menu is usually positioned under File with a caption of &Save.
If the user wants to save a document with a different name and/or a different location, which is usually done by clicking File -> Save As... from the main menu, create a menu item with the ID_FILE_SAVE_AS identifier. This menu item is usually placed under Save in the File main menu. If the user is working on a new document (that has not been saved previously), or if the user working on a document that is marked as Read-Only, or if the user decides to close the application after working on a new document, and if the user decides to save the document, the action would initiate the File -> Save As action.
When the user has decided to save a document, if the document is dirty, a message box would display to the user for a decision. If the user decides to save the document, the CDocument class provides the OnSaveDocument() method to validate the action. The syntax of this method is:
virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
To save a document, the user must specify where the document would reside. This is communicated as the lpszPathName argument). In reality, as its name suggests, this method is not used to save a document. It allows you to validate the action or desire to save a document. For example, if the user clicks File -> Save on the main menu or if the user is attempting to close a dirty document, you can use this method to check what is going or to deny saving the file.
Serialization is the ability to store data on a drive. Therefore, to actually save a file, the CObject class provides the Serialize() method to its children. To store data of the file, its information is held by a class called CArchive. When saving a file, a CArchive object is passed to the Serialize method as reference, which modifies its value.
CArchive is used to either save the current document or open an existing one. To take care of each, it uses two methods (ReadObject() and WriteObject()). These methods are actually implemented using the extraction operators (>> and <<). Whenever you need to perform serialization in an application, add a method called Serialize() to your document class and pass it a CArchive object reference. The syntax of this method is:
virtual void Serialize(CArchive& ar);
The implementation of this method may depend on the document.
|
|

| Caption | ID | Prompt |
| &New\tCtrl+N | ID_FILE_NEW | Create a new document |
| &Open...\tCtrl+O | ID_FILE_OPEN | Open an existing document |
| &Save\tCtrl+S | ID_FILE_SAVE | Save the current document |
| Save &As... | ID_FILE_SAVE_AS | Save the current document with a new name or location |
| - | ||
| P&rint Setup... | ID_FILE_PRINT_SETUP | Change the printer and printing options |
| - | ||
| Recent file | ID_FILE_MRU_FILE1 | Open this file |
| - | ||
| E&xit | ID_APP_EXIT | Quit the application; prompt the save document |
class CExerciseDoc : public CDocument
{
DECLARE_DYNCREATE(CExerciseDoc)
virtual BOOL OnNewDocument();
DECLARE_MESSAGE_MAP()
};
|
...
BOOL CExerciseDoc::OnNewDocument()
{
return CDocument::OnNewDocument();
}
CExerciseApp theApp;
|
|
SDI Improvements: The Frame |
The CWnd::OnCreate() method is used to create a window and it is usually meant to do this using its default configured features. Therefore, anything you want to display on the frame when the application displays, you can do so when creating the application. Therefore, the frame is typically used to create and display the toolbar(s), dialog bar(s), and status bar.
After the frame has been created, if you want to modified something on it, you can do so after it has been created but before it is displayed to the user. To do this, you can use the PreCreateWindow() method of the CWnd class. Its syntax is:
virtual void PreCreateWindow(CREATESTRUCT& cs);
This method takes a reference to CREATESTRUCT class, modifies and returns it with the new characteristics. For example, you can use this method to remove the Minimize and the Maximize system buttons on the title bar as follows:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style &= ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
return CFrameWnd::PreCreateWindow(cs);
}
|
|
class CMainFrame : public CFrameWnd
{
DECLARE_DYNCREATE(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
DECLARE_MESSAGE_MAP()
};
|
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// cs.style &= ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
cs.cx = 450; // Change the width
cs.cy = 350; // Change the height
return CFrameWnd::PreCreateWindow(cs);
}
|
|
SDI Improvements: The View |
As mentioned already, the object that holds a file's data is the document which is an object of CDocument type. To make this document available to the user to view it, in your view derived class, you should declare a pointer to the document class. The syntax used is:
CDocument* GetDocument() const;
When implementing this method, simply ask the application to return a pointer to the document class used in your application. This can be done by casting the global m_pDocument variable to your document class:
CExerciseDoc* CExerciseView::GetDocument()
{
return (CExerciseDoc*)m_pDocument;
}
You can use this GetDocument() method in your view class to access the document. For example, the view class can access the contents of the class
If you want to allow the user to print a document, you can add a menu item identified as ID_FILE_PRINT. Then, in the message table of the view class, use the ON_COMMAND macro to associate it to the view parent class of your derived class. You can use this same approach to allow the user to preview document. The identifier for this action is called ID_FILE_PRINT_PREVIEW.
|
Overview of the Multiple Document Interface (MDI) |
An application is referred to as Multiple Document Interface, or MDI, if the user can open more than one document in the application without closing it. to provide this functionality, the application provides a parent frame that acts as the main frame of the computer program. Inside of this frame, the application allows creating views that each uses its own frame, making it distinct from the other. Here is Microsoft Word 97 displaying as a classic MDI application:

When using an MDI application, a user can create a document, open another while the first is still displaying, and even create new documents or open additional ones. The documents are stacked in a Z-order axis of a 3-D coordinate system. There are two ways to display the documents of an MDI. If one document is maximized, all documents are maximized. In this case, the main menu of the application would display the system buttons on its right, which creates two ranges of system buttons:

If no document is maximized, each document can assume one of two states: minimized or restored. While the child documents are confined to the borders of the main frame, if a document is or gets minimized, it would display its button inside the main frame. If a document is not minimized, it would display inside the main frame either with its original size or the size the user had given it:

There are two main ways the user can access the different documents of an MDI. If they are maximized (remember that if the user maximizes one document, all the others get maximized also), the menu of the main frame, which we always call the main menu in this book, has an item called Window. When the Window menu item is accessed, it would display a list of the currently opened documents. The user can then select from that list:

The separation of the parent and the child frames allows the user to close one child document or all documents. This would still leave the main frame of the application but the user cannot use it as a document. Still, the main frame allows the user to either create a new document or open an existing one.
To manage the differentiation between the parent and its children, the application uses two different menus: one for the main frame and one for a (or each) child. Both menus use the same menu bar, meaning that the application cannot display two frame menus at one time. It displays either the parent menu or a (the) child menu. When at least one document is displaying, the menu applies to the document. If no document is displaying, the main frame is equipped with a simplified menu with limited but appropriate operations. The user can only create a new document, only open an existing document, or simply close the application.
|
Creating a Multiple Document Interface |
We have mentioned that an MDI is an application made of a parent frame and at least one child. Therefore, to create an MDI, because it requires a frame of its own, you should first create a series of resource objects that share a common name as IDR_MAINFRAME. A basic application should have an icon and a menu. The icon can be designed any way you like and you are recommended to create one made of a 32x32 and a 16x16 versions. The menu, since it will be used only when no document is available, should provide functionality that does not process a document. It should allow the user to create a new document, to open an existing document, and to quit the application. Such a menu can have the following items:

As done since Lesson 3 (when we studied resources), these two resources are sufficient to create an application since you can call the CFrameWnd::LoadFrame() method. To create a frame for a Multiple Document Interface, you must derive your frame class from CMDIFrameWnd:
BOOL CExercise1App::InitInstance()
{
// Create a frame for the window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
CCommandLineInfo cmdInfo;
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
The above code allows only creating a window frame for the parent window. To allow the user to interact with the computer through your MDI, you must provide a template for a document. The child document must have its own frame, distinct from that of the parent. To provide this frame, you have two main alternatives. You can directly use the CMDIChildWnd class or you can derive your own class from CMDIChildWnd:
class CChildFrame : public CMDIChildWnd
{
DECLARE_DYNCREATE(CChildFrame)
DECLARE_MESSAGE_MAP()
};
As opposed to a Single Document Interface application, to create a Multiple Document Interface (MDI) application, you use the CMultiDocTemplate class which, like the CSingleDocTemplate class, is derived from CDocTemplate. Therefore, you must declare a pointer variable to CMultiDocTemplate using the new operator, then use its constructor to initialize the template. The syntax of the CMultiDocTemplate constructor is:
CMultiDocTemplate(UINT nIDResource,
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass);
To make a document different from the parent, you must create an additional series of resources that share a common name other than IDR_MAINFRAME. The most basic resources you should create are a menu and an icon. There are two main types of MDI applications.
One kind of application may use only one particular category of documents such as only text-based. Because text-based documents can include ASCII text files, rich text documents (RTF), HTML files, script-based documents (JavaScript, VCScript, Perl, PHP, etc), etc, such an application may be configured to open only that type of document. To create such an MDI application, in the constructor of the CMultiDocTemplate object that you are using, specify a CView derived class (CEditView, CListView, etc) or your own class you derived from CView or one of its children. Here is an example:
BOOL CMultiEdit1App::InitInstance()
{
CMultiDocTemplate* pDocEdit;
pDocEdit = new CMultiDocTemplate(
IDR_EDITTYPE,
RUNTIME_CLASS(CMultiEdit1Doc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CEditView));
AddDocTemplate(pDocEdit);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
CCommandLineInfo cmdInfo;
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
In this case, the user can create and/or open as many text files as the computer memory would allow.
Another type of MDI application you can create would allow the user to create or open more than one type of document. To provide this functionality, you must specify a template for each type. Again, there are various types of techniques you can use. You can ask the user to select the type of document he or she wants to create when the application starts:

To do this, you can create a template for each type of document using a CDocMultiDocTemplate constructor for each:
BOOL CMultiEdit1App::InitInstance()
{
CMultiDocTemplate* pDocEdit;
pDocEdit = new CMultiDocTemplate(
IDR_EDITTYPE,
RUNTIME_CLASS(CMultiEdit1Doc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CEditView));
AddDocTemplate(pDocEdit);
CMultiDocTemplate* pDocForm;
pDocForm = new CMultiDocTemplate(
IDR_FORMTYPE,
RUNTIME_CLASS(CMultiEdit1Doc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CEmplRecords));
AddDocTemplate(pDocForm);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
CCommandLineInfo cmdInfo;
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
You can also display an empty main frame when the application starts and let the user create a document based on the available types from the main menu. Here is such a menu from Borland Image Editor:

As you can see, creating an MDI is not necessarily too difficult but it can involve a few more steps than an SDI. Therefore, whenever we need an MDI, unless specified otherwise, we ill use the AppWizard.
|
The Visual C++ AppWizard |
Microsoft Visual C++ provides various wizards to assist you with performing some tasks. Particularly, to create an application, it offers the AppWizard. AppWizard offers a convenient step-by-step series of choices to create various types of applications such as an SDI or an MDI (it offers many other choices of applications).
|
An SDI With AppWizard |
To get help in creating a single document interface application, you can use the Visual C++' AppWizard. to do this, from the New Project dialog box (MSVC .Net) or the Projects property page of the New dialog box (MSVC), select MFC Application or MFC AppWizard (exe), specify the name and location, then click OK. On the next dialog box, you must specify the Application Type as Single Document. One of the decisions you must make is to specify the view of application you are creating, that is, you must select CView or one of its children as the base class and click Finish. MFC's AppWizard offers various options to create a starting application as complete as possible. We will view these when needed.
|
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |

| From now on, when you are asked to create an SDI, refer to this section. |
|
An MDI With AppWizard |
To reduce the amount of work involved with creating an MDI, you can use the MFC's AppWizard. To do this, in the MFC AppWizard, select the Multiple Document radio button.
If you want to create an application that would use a single type of document, select the desired base class.
If you want to create a Windows Explorer type of application, you should select a Single Document application type and select the project style as Windows Explorer. Then, in the Generated Classes section or the MFC AppWizard Step 6, specify a class for the left frame and another base class for the right frame. A classic example would have TreeView class for the left pane and a CListView class for the right side.
If you have created an SDI, you can change it into a multi-view application by adding a CView-derived class to the application. For example, in MSVC 6, if you had created an SDI based on a tree view and you want to create a second view as a form for the right frame, right-click the first node in Class View and click New Form.
|
|
||
| Previous | Copyright © 2003-2004 FunctionX, Inc. | Next |
|
|
||