The MFC document/view classes are a powerful way for creating applications. I see many questions in the forums about how these classes work and ways to modify/extend their standard behaviour. Here I would like to present some of the knowledge I have of this architecture and ways it can be modified and extended to do what you want it to do.

Some of the MFC classes and the way they work are very well documented in the MSDN, but others are completely missing. The only way to learn about these is to dig directly into the MFC source code and see how they do their job. I will also be presenting some additional documentation on these classes here.

The first thing we need to understand about the MFC doc/view architecture is the way that the classes used to control it relate to each other. Below is a basic diagram of how these are arranged:

This is a simplistic view of the framework as it does not take into account the different base class view types etc. which the framework supports.

In general it can be seen that we have a single CWinApp class object. This holds a CDocManager object which is used by the MFC to handle all the CDocTemplate objects that you registered with the framework. The CWinApp object also creates a CMainFrame object which is the main window of your application. Every time you open/create a document in your application, a CDocument object of the right type will be created. A pointer to this object will be stored in a list under the corresponding CDocTemplate object which was used to register that document type. When you open a document, the CMainFrame creates a CChildFrame object (an MDI window) which will be used to display the documents view. A CView object will be created as child inside the CChildFrame window and a pointer to the view will also be stored in the view list for the CDocument object just opened.

If you create additional views for your CDocument, additional CChildFrame objects will be created with the correct view type residing within. A pointer to this new view will also be added to the relevant CDcouments objects view list.

The CDocManager class is one of MFC's undocumented helper classes. The MFC uses it to manage the list of CDocTemplate/CMultiDocTemplate objects in your application. There is only ever one of these objects in your MFC application. It can be found in CWinApp and is accessed through the m_pDocManager member variable. It is accessed through a pointer to allow you to substitute your own CDocManager derived class for the MFC one if you need to, but to do that, you have to create and set the m_pDocManager variable before MFC creates its own one. This is done in the following function:

So as long as you create and assign your own CDocManager derived object before the first call to AddDocTemplate() in your CWinApp::InitInstance() derived function, yours will be used automatically by the MFC code. Note that the CDocManager object is deleted for you by the MFC code in the CWinApp destructor:

One of the reasons that CDocManager is undocumented is that it is an MFC helper class not usually intended to be changed by the average user. It could be removed or changed any time in the future as MFC is developed further.

So let's take a look at the functions available in the CDocManager class (from AFXWIN.H):

So what do each of these functions do? Well, if you look at the code, you can see that in general they implement the actual functionality of the CWinApp class' function calls with the same name/prototype, so going to the standard CWinApp help for the same function name can give you a good idea of what the function actually does. I have also documented these to the best of my knowledge/experience below:

CDocManager()

The constructor for the class. This does not do anything special as all the member variables know how to construct themselves.

virtualvoid AddDocTemplate(CDocTemplate* pTemplate)

A function you should at least be partially familiar with. When you call CWinAPP::AddDocTemplate in your CWinApp::InitInstance() derived function, this call gets passed directly on to the CDocManager object. The list of CDocTemplate objects is held by this class. The next two functions can be used to get a pointer to any of the registered CDocTemplate objects.

virtual POSITION GetFirstDocTemplatePosition() const

Because the list of CDocTemplate objects is held by the CDocManager object, some way of iterating this list needs to be available, so this function and the next allow you to iterate through the list of registered CDocTemplate objects in your application.

This is the actual implementation of the CWinApp::RegisterShellFileTypes() function. The MSDN gives a good description of what this function actually does.

void UnregisterShellFileTypes()

It will undo the results of the above function. No help is available in MSDN about this function for CWinApp.

virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName)

This function will iterate through the list of CDocTemplate objects in your application and see whether the file extension of the file matches those for the CDocTemplate object. If a match is found and the file is not already open in your application, then a new document of the right type is created and the file opened. The file extension used to match document types is defined in your applications string table. If you have registered your CDocTemplate object as follows:

Then the string table entry under IDR_CONTEXTTYPE will have the default file extension contained in it in one of the fields. You usually get to define the first document types file extension during the AppWizard project creation phase...

virtual BOOL SaveAllModified()

This function is again the actual implementation of the CWinApp::SaveAllModified() function. Normally called when the user wants to terminate the current application session, it iterates through all the CDocTemplate objects and calls the SaveAllModified() function on each. These in turn iterate through all their own open documents and call the CDocument::SaveModified() function. If any of these functions return non-zero (usually because the user cancelled the operation through a message box), then the application will not close.

virtualvoid CloseAllDocuments(BOOL bEndSession)

This function, which is very similar to SaveAllModified() causes all open documents to be closed. Usually used by the frame window when the application is being closed, it could still be used to close all open documents.

virtualint GetOpenDocumentCount()

This function implements the CWinApp::GetOpenDocumentCount() function. It returns the total number of all documents open, for all CDocTemplate objects in your application.

The default implementation of CWinApp::DoPromptFileName(), it prompts the user to enter a filename for a new document that is being saved for the first time. It uses the standard filter supplied for each registered CDocTemplate object and also adds the default *.* filter. This is a good one to override if you want to change the basic behaviour of your application, for example, if you were writing a Graphics application that needed to be able to save a picture in multiple file formats, here is where you could get the correct file filter list setup.

virtual BOOL OnDDECommand(LPTSTR lpszCommand)

This function implements the DDE commands to a mainframe usually by Windows Explorer. This is for commands such as to open/print an existing file.

virtualvoid OnFileNew()

The actual implementation of the CWinApp::OnFileNew(), this function checks the number of CDocTemplate objects registered with the framework. If more than one, a dialog box will be displayed with a ListBox containing the names of the document types (from the string table). Once the user has selected a template type, the framework calls OpenDocumentFile(NULL) on the selected template to create a new empty document of the required type. By default, if you only have one CDocTemplate object registered with the system, then it will automatically create a document of that type.

If you need to create new empty documents of a specific type without the framework displaying the selection dialog box, you are going to have to call CDocTemplate::OpenDocumentFile(NULL) on the correct CDocTemplate object yourself.

virtualvoid OnFileOpen()

int GetDocumentCount()

This is a protected member function, so you will not be able to call it directly. It seems to be an exact copy of the GetOpenDocumentCount() function, so you don't need to use it anyway.

The CDocTemplate class handles the list of open documents and the classes that will be used to implement its functionality. It also lets an MDI/SDI interface know the default menu and accelerator keys used by the mainframe when the selected document type is active. First let's see how a CDocTemplate object is registered in the InitInstance() function:

windowTitle - This entry is only used by the MFC when you register/de-register your document type with the shell.

docName - The default name that will be given to any new documents of this type created, appended with a number.

fileNewName - If you have more than one document template registered with your application, and a user selects New..., by default the MFC displays a Select document type dialog. This is the name listed by the dialog for this document type in the dialog box.

If you have more than one CDocTemplate object registered with the framework, when the user clicks ID_FILE_NEW, by default the framework displays a "Select document type" dialog box:

In cases such as these, you need to get the correct CDocTemplate object pointer and call OpenDocumentFile(NULL> on it. A good way of doing this would be to define an enum which lists all the document types in the order they are registered and add a function to the CDocManager class, by inheriting your own from it:

If you need multiple file extensions to be recognized by a document type, you need to extend the CMultiDocTemplate class to recognize these extra extensions. There is one virtual function you need to change.

Share

About the Author

A research and development programmer working for a pharmaceutical instrument company for the past 17 years.

I am one of those lucky people who enjoys his work and spends more time than he should either doing work or reseaching new stuff. I can also be found on playing DDO on the Cannith server (Send a tell to "Maetrim" who is my current main)

I am also a keep fit fanatic, doing cross country running and am seriously into [url]http://www.ryushinkan.co.uk/[/url] Karate at this time of my life, training from 4-6 times a week and recently achieved my 1st Dan after 6 years.