- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Using Document Templates
Document templates provide the framework that MFC uses to bind documents, views, and frames together. In fact, it is the document template that will create new documents and views for your application. Document templates generally come in two flavors: CSingleDocTemplate for SDI apps and CMultiDocTemplate for MDI apps. You generally won't work with CSingleDocTemplate objects as much as the MDI varieties, so you mostly will look at CMultiDocTemplate objects here.
Many similarities exist between the two types of templates, however. They are created with the same parameters and perform many of the same operations. The big difference in their operations stems from the fact that MDI applications may have several child frames in the main frame, whereas SDI apps have only one frame.
The EmpList Sample Project
This section provides code from the EmpList sample project, which is located on the CD-ROM that accompanies this book. EmpList is an MDI project built using MFC AppWizard. EmpList uses two types of views:
- CEmpListView is derived from the CListView class. This view displays a list of names and email addresses in a list view control. This is the application's initial view. You can add and remove items from the view by using the Edit menu.
- CEmpFormView is derived from the CFormView class and displays a list of names and email addresses in a list view control embedded in the form view. Pushbutton controls simplify the task of adding and removing items.
The EmpList project illustrates techniques that are useful when dealing with multiple views for a single document:
- Creating different types of document templates that describe the document view relationships in your application
- Creating a specific type of view on demand
- Synchronizing data between different views
- Optimizing updates between the document and views
Creating a Document Template
Now let's look at how to create a document template. Here is an example of the code from InitInstance() generated by the MFC AppWizard when you create an MDI app:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_MYAPPTYPE,
RUNTIME_CLASS(CMyAppDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMyAppView));
AddDocTemplate(pDocTemplate);
This code fragment declares a pointer to CMultiDocTemplate and uses new to create a template object on the heap. Template objects should remain in memory for as long as your application exists, even if you do not use the pointers directly after your call to AddDocTemplate(). You don't need to delete the document template; the MFC framework deletes the template at the appropriate time.
The constructor for CMultiDocTemplate is called with four parameters, including a resource ID and class information structures (provided by the RUNTIME_CLASS macro) for your document, frame, and view classes.
After the document template is created, AddDocTemplate() is called to register the template with MFC. You'll learn more about what this means later, but let's take a closer look at what the parameters passed to the CMultiDocTemplate constructor really mean.
The class information for the document, view, and frame types is used whenever MFC is told to create a new document or view. If you want to use different combinations of these classes in your application, you should create a separate template for each combination.
The first parameter is a shared resource ID, and it's really many different parameters in one. In the project that MFC AppWizard will generate, you will notice that both an icon and a menu are created with this ID. When a new frame window is created, it is associated with this icon and menu. You will see the icon in the main frame if you minimize a view window. The menu is attached to the main frame when the view is active.
The shared resource ID allows you to have different menus and icons for different document or view types. If you have defined multiple document templates, you can use the same resource ID for each template or use a different ID for each template. If you do specify a new resource ID but do not define a new menu with this resource ID, the main menu remains the same as the last active view.
The Document String Resource
In addition to being used for icons and menu resources, the resource ID passed to the CMultiDocTemplate constructor is the ID of a string resource, which can be edited in the String Table Editor. This string rolls seven different parameters into one, each separated by a newline symbol (\n). From within your application, you can use the CDocTemplate::GetDocString() function to access the individual elements. This function takes a CString reference, which will hold the returned string, and an index, which is defined by an enum type in CDocTemplate. Table 4.1 lists the enum values and their meanings.
Table 4.1. Resource String Parameters
| enum Value | Purpose |
| windowTitle | Name that appears in the application's title bar. |
| docName | Root for the default filename. MFC appends 1,2,3… to this when new documents are created. |
| fileNewName | Name of this document type. If several document types are defined, this is the name that is presented to users when they create a new document. If this portion of the string is blank, it is not available to users. |
| filterName | Description of file type and wildcard filter—for example, Dave's files (*.dav) |
| filterExt | Extension for files of this type. This should have no asterisk (*) but should have a period (.)—for example, .dav. |
| regFileTypeId | Internal document type used for registration with Windows. |
| regFileTypeName | Registry document type used by OLE. This is exposed to the user. |
Unfortunately, no function is provided to set these values; you must enter them in the String Table Editor in the order listed, separated by the newline character (\n). This means that you cannot change these parameters at runtime. The string resource created by the MFC AppWizard contains the strings entered in the Advanced Settings dialog box of Step 4 of the wizard. The last two parameters are used by OLE servers.
The document string resource always contains a newline character for each item in the enumeration. If a particular value isn't used, several newlines may appear next to each other.
After you create the document template and call AddDocTemplate(), your application is set up to use the default functionality MFC provides for creating new documents and views. Next, you'll see how you can use additional document templates to create multiple views for each document.
Using Multiple Document Templates
As discussed in "Using Document Templates," earlier in this chapter, the document template associates a set of view, frame, and document classes. To use multiple documents or views, you must create a document template for each combination of document view classes you intend to create.
When multiple documents or views are used by an MFC application, it's a good idea to store pointers to the document templates in the application's CWinApp-derived class. For example, in the EmpList project, two document templates are created; pointers to these document template instances are kept in the CEmpListApp class for use later, when a view must be created on demand.
Listing 4.1 contains the relevant portion of the CEmpListApp::InitInstance function. I have removed the code generated by the AppWizard from the listing, but you will find the full source code for the project on the accompanying CD-ROM.
Example 4.1. Creating Additional Document Templates for EmpList
BOOL CEmpListApp::InitInstance()
{
// Wizard-generated code omitted...
m_pListTemplate = new CMultiDocTemplate(IDR_EMPLISTYPE,
RUNTIME_CLASS(CEmpListDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CEmpListView));
m_pFormTemplate = new CMultiDocTemplate(IDR_EMPFORMTYPE,
RUNTIME_CLASS(CEmpListDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CEmpFormView));
AddDocTemplate(m_pListTemplate);
AddDocTemplate(m_pFormTemplate);
return TRUE;
}
As discussed earlier, the document template pointers are deleted by the MFC framework when the application is closed. The code in Listing 4.1 causes the MFC framework to use CEmpListView as the default view class. By caching the document template pointers, the application can create either type of view on demand.
Creating a Different View for a Document
If your application has a document open and you would like to open a different sort of view to it, you can do so by calling the CreateNewFrame() member of a document template that relates the document type to a new view class. The CreateNewFrame() function takes a pointer to the existing document object and an optional pointer to an existing frame window. If you use CreateNewFrame(), you should be sure to call CDocTemplate::InitialUpdateFrame() to initialize the window. This function takes a pointer to the new frame and a pointer to the existing document. You can see an example of this in Listing 4.2, which is taken from the EmpList sample application on the CD-ROM.
Example 4.2. Creating New Views on Demand in EmpList
void CMainFrame::OnWindowForm()
{
CMDIChildWnd* pActiveChild = MDIGetActive();
if(pActiveChild)
{
CDocument* pDoc = pActiveChild->GetActiveDocument();
if(pDoc)
{
CEmpListApp* pApp = (CEmpListApp*)AfxGetApp();
ASSERT_POINTER(pApp, CEmpListApp);
CDocTemplate* pTemplate;
CFrameWnd* pFrame;
VERIFY(pTemplate = pApp->GetFormTemplate());
pFrame = pTemplate->CreateNewFrame(pDoc, pActiveChild);
if(pFrame)
{
pTemplate->InitialUpdateFrame(pFrame, pDoc);
}
}
}
}
void CMainFrame::OnWindowList()
{
CMDIChildWnd* pActiveChild = MDIGetActive();
if(pActiveChild)
{
CDocument* pDoc = pActiveChild->GetActiveDocument();
if(pDoc)
{
CEmpListApp* pApp = (CEmpListApp*)AfxGetApp();
ASSERT_POINTER(pApp, CEmpListApp);
CDocTemplate* pTemplate;
CFrameWnd* pFrame;
VERIFY(pTemplate = pApp->GetListTemplate());
pFrame = pTemplate->CreateNewFrame(pDoc, pActiveChild);
if(pFrame)
{
pTemplate->InitialUpdateFrame(pFrame, pDoc);
}
}
}
}
In Listing 4.2, the GetFormTemplate and GetListTemplate functions are called. These two functions are part of the CEmpListApp class and simply return pointers to the document templates created earlier in Listing 4.1.
The document templates are used to create new views by calling their CreateNewFrame member functions. After the new views are created, the InitialUpdateFrame function is called to force the view to be updated. This update causes the view to synchronize itself with its associated document, keeping all views synchronized.
CDocument::OnChangedViewList()
Whenever a new view is attached to a document, the framework calls the document's OnChangedViewList() function. You can override this function in your application if you need to do anything special when a view is added or deleted from the document's view list. The default implementation of OnChangedViewList() closes the document if no views remain in the document's view list.
UpdateAllViews()
When the data in your document changes, you generally will want to update all the views attached to the document. You can do this by using the UpdateAllViews() function of your document object. UpdateAllViews() takes a pointer to the view that generated the change as its first parameter. This function runs through the document's list of views, calling the OnUpdate() function of each view, with the exception of the view that generated the change. If you want to update all the views, specify NULL as the first parameter to UpdateAllViews().
If you are supporting multiple views, you probably don't want to update every view for each change to the document. It's usually very easy for the active view to update itself without waiting for the change to be reflected through the document class. In the EmpList project, for example, each view is responsible for updating its own user interface when changing the document. Immediately updating the view makes the user interface appear to be more responsive. If the view were to wait for the document to be updated first, the user interface could be noticeably slower in some situations.
Listing 4.3 contains some source code from the EmpList project that is used to add a new employee to the document.
Example 4.3. Adding a New Employee in EmpList
void CEmpFormView::OnAdd()
{
CDlgEmp dlg;
if(dlg.DoModal() == IDOK)
{
CEmployee emp;
emp.m_strEmail = dlg.m_strEmail;
emp.m_strName = dlg.m_strName;
// Add the item to the list view control
int nItem = m_list.GetItemCount();
AddEmployeeToList(nItem, emp);
// Add the item to the document
CEmpListDoc* pDoc = GetDocument();
ASSERT_POINTER(pDoc, CEmpListDoc);
if(pDoc)
pDoc->SetEmployee(emp, this);
}
}
Note that in Listing 4.3, the view passes a pointer to itself to the document class. The document class uses the view pointer as a parameter when calling UpdateAllViews, preventing the view from being updated.
Optionally, you can pass a long and/or a pointer to a CObject to UpdateAllViews(). These are passed on to the view's OnUpdate() function and can be defined to be anything you want. Generally, you will want to use these if you can somehow optimize your update routines based on the changes that were made.
In EmpList, the update hint consists of a pointer to the structure shown here:
enum EmpListHintType{ hintNone, hintRemove, hintAdd } ;
struct EmpListHint
{
EmpListHint(EmpListHintType t, int i, CEmployee e)
: type(t), item(i), emp(e) { } ;
EmpListHintType type;
int item;
CEmployee emp;
} ;
This code enables a view to add or delete any item from the view without requesting more information from the document.
Accessing Views from Your Document
Occasionally, you might want to search through the list of views associated with a document in order to do something with them. If UpdateAllViews() doesn't meet your needs, MFC provides an alternative. Here is an example:
void CMyDocument::UpdateSomeViews()
{
POSITION pos = GetFirstViewPosition();
while(pos != NULL)
{
CView* pView = GetNextView(pos);
if(myCondition) pView->OnUpdate(NULL, 0, NULL);
}
} // end UpdateSomeViews()
This example declares a POSITION object used to walk through the list of views. The value of pos is set to NULL by the GetNextView function when the end of the list is reached. The call to GetFirstViewPosition() sets up the list of views for browsing with the GetNextView() call. You then can do whatever you want with the view pointer that is returned. You should note that I used the generic CView pointer in this example, however. In the real world, you could cast the return value of GetNextView() to your view type, although you need to be careful with this if you support multiple view types for the document.
Working with Frames | Next Section

Account Sign In
View your cart