The document / view structure is the most distinctive and difficulty part of the MFC, which involves different objects such as applications, document templates, documents, view, MDI frame windows, MDI sub-windows, if not known How to associate, it may make mistakes, which is difficult to compile with a horizontal document / view program. For example, when I started VC programming, I added two document templates for the application. Two template public a document class, but the view is different. It is expected that when a template's document changes, call UpdateAllViews after calling UpdateAllViews. Updating another document template, the result is of course not, the reason is that there is no in-depth understanding of the MFC's document / view structure. The best way to understand is to read the source code of the MFC. Here is my notes: (1) The contact between the application object and the document template:
First, there is a CDOCManager pointer type in the application object, which maintains a cptrlist type linked list: M_TempateList, which is a protection member in CDOCManager. The cwinapp :: adddoctemplate function is called in the InitInstance function, which is actually called the addDocTemplate function to the M_PDOCManager to add a template pointer to the linked list m_templatelist. CWINApp provides access to the GetFirstDoctemplatePosition and getNextDocTemplate function to implement the M_TemplateList Link list (actually calling the CDOCManager related functions).
The most common function provided by CWINAPP in file operation is the new-forfilenew and ONFILEOPEN, which is also the same name function that calls the CDOCManager class. For new, when there is only one document template, it will create a blank file; if there are multiple document templates, it will have a dialog prompt to select the document type. Its source code is as follows:
Void cdocmanager :: OnfileNew ()
{
IF (m_templatelist.isempty ())
{
.......
Return;
}
// Take the first document template's pointer
CDOCTemplate * ptemplate = (cdoctemplate *) m_templatelist.gethead ();
IF (M_TemplateList.getCount ()> 1)
{
// If more than one document template, the dialog prompts the user to choose
CNEWTYPEDLG DLG (& M_TemplateList);
INT NID = DLG.DOMODAL ();
IF (NID == IDOK)
PTEMPLATE = DLG.M_PSelectedTemplate;
Else
Return; // none - Cancel Operation
}
......
// When the parameter is NULL, openocument file will create a file.
PTEMPLATE-> OpenDocumentFile (NULL);
}
open a file:
Void cdocmanager :: onfileopen ()
{
/ / Open file dialog box
Cstring newname;
IF (! doPROMPTFILENAME (newname, afx_ids_openfile,
OFN_HIDEREADOSLY | OFN_FILEMUSTEXIST, TRUE, NULL)
Return; // Open Cancelle
AFXGetApp () -> OpenDocumentFile (newName); // actually also calls the same name function}
(2) Document template and documentation:
From the above, it is seen from the application object to the new and open of the file to rely on the OpenDocumentFile function of the document template. The MFC template class is used to contact the document class, view classes, and frameworks, and in its constructor requires information about these three:
CDOCTemplate (uint nidResource, cruntimeclass * pdocclass, cruntimeclass * pframeclass, cruntimeclass * pViewClass);
The constructor uses the latter three parameters to assign a value for its three CRUNTIMECLASS * types:
m_pdocclass = pdocclass;
m_pframeclass = pframeclass;
m_pviewclass = pViewClass;
Document templates are divided into single document templates and multi-document templates. The implementation of these two templates is different. In addition to the above three members, there are different but very important member variables that are different from each other. For multi-document templates: cptrlist m_doclist;, single document template: cdocument * m_ponlydoc ;. They all have a member function addDocument, assignment operations, respectively, and in their parent class's cdoctemplate assigns the m_pdoctemplate variable of the document it added to the template's own address:
Void CDOCTemplate :: AddDocument (cDocument * pdoc)
{
Ask_VALID (PDOC);
Assert (pdoc-> m_pdockemplate == null);
PDOC-> m_pdoctemplate = this;
}
Since the single document template can only have a document, it just maintains a pointer to the template that points to himself: m_ponlydoc, the addocument function is to assign this member:
Void csingledoctemplate :: addocument (cdocument * pdoc)
{
......
CDOCTemplate :: AddDocument (PDOC);
m_ponlydoc = pdoc;
} Since the multi-document template can have multiple documents, it is to maintain a linked list that contains all the documents that it open, so its AddDocument is:
Void CMULTIDOCTEMPLATE :: AddDocument (cdocument * pdoc)
{
......
CDOCTemplate :: AddDocument (PDOC);
m_doclist..addtail (PDOC);
} Template passes the M_PonlyDoc (single document) or remembers the pointer of all the templates you have, and enables access to the documents it own through the getFirstDocPosition and GetNextDoc function, and make the document remember the document template. Pointer, while documentation provides a getDonommentemplate () function to get the template it belongs.
The call to the AddDocument function is mainly in another member function CreateNewDocument, its role is to create a new document:
CDocument * cdoctemplate :: CreateNewDocument ()
{
IF (m_pdoccoas == null)
{
......
}
CDocument * pdocument = (cdocument *) m_pdocclass-> createObject (); ......
AddDocument (PDocument);
Return PDocument;
}
The CreateNewDocument function mainly uses the function creteObject of the runtime pointer of the document class, and uses AddDocument to assign its pointer to the relevant member, and stay after being used.
The ONFileNew and ONFileOpen functions of the application use the OpenDocumentFile function of the template and most of this function when actually programmed. In the MSDN document says that when the parameter does not open the file when the parameter is not null, otherwise, use the CreateNewDocument function mentioned above to create a new document, then how is it implemented?
CDocument * CSINGLEDOCTEMPLATE :: OpenDocumentfile (LPCTSTSTR LPSZPATHNAME,
Bool Bmakevisible)
{
CDocument * pdocument = null;
CframeWnd * pframe = null;
Bool bcreated = false; // => doc and frame created
Bool bwasmodified = false;
// If there is already an open document, you will ask if you save a file.
IF (m_ponlydoc! = null)
{
pDocument = m_ponlydoc;
IF (! pDocument-> savemodified ())
Return NULL;
Pframe = (cframewnd *) AFXGETMAINWND ();
......
}
// Create a new file
Else
{
PDocument = cretenewdocument ();
Assert (pframe == null);
Bcreated = true;
}
......
// If you create a document for the first time, you have to create a frame window.
IF (pframe == null)
{
Assert (bcreated);
// Create Frame - SET As Main Document Frame
Bool bautodelete = pDocument-> m_bautodelete;
PDocument-> m_bautodelete = false;
Pframe = CreateNewFrame (PDocument, NULL);
PDocument-> m_bautodelete = bautodelete;
......
}
IF (LPSZPATHNAME == NULL)
{
/ / Set the default title for the new document
SetDefaultTitle (PDocument);
......
// Time to overload OnNewDocument to initialize some data, if false is returned, indicating the initialization loss // defeated, destroy the window.
IF (! pDocument-> onnewdocument ())
{
......
IF (BCREATED)
Pframe-> destroyWindow (); // Will Destroy Document
Return NULL;
}
}
Else
{
CWAITCURSOR WAIT;
// Open an existing document
BWASMODIFIED = pDocument-> ismodified ();
PDocument-> setmodifiedflag (false);
// onopendocument function Reinitializes document object if (! PDocument-> onopendocument (lpszpathname))
{
IF (BCREATED)
{
// New documentation
Pframe-> destroyWindow ();
}
Else if (! pdocument-> ismodified ())
{
// Document is not modified, restore the modification mark of the original document
PDocument-> setmodifiedflag (bwasmodified);
}
Else
{
// Modify the original document
SetDefaultTitle (PDocument);
IF (! pDocument-> onnewdocument ())
{
TRACE0 ("Error: Onnewdocument Faled After Trying to Open A Document - Trying to Continue./N");
}
}
Return null; // Open failed
}
PDocument-> setPathname (lpszpathname);
}
Cwinthread * pthread = AfxgetThread ();
IF (bcreated && pthread-> m_pmainwnd == NULL)
{
Pthread-> m_pmainwnd = pframe;
}
InitialUpdateFrame (pframe, pdocument, bmakevisible);
Return PDocument;
}
The following is the implementation of OpenDocumentFile of a multi-document template
CDocument * CMULTIDEMPLATE :: OpenDocumentFile (LPCTSTR LPSZPATHNAME,
Bool Bmakevisible)
{
// Newly create a document object
CDocument * pdocument = cretenewdocument ();
......
Bool bautodelete = pDocument-> m_bautodelete;
PDocument-> m_bautodelete = false;
CframeWnd * Pframe = CreateNewFrame (PDocument, NULL);
PDocument-> m_bautodelete = bautodelete;
......
IF (LPSZPATHNAME == NULL)
// When it is new
{
SetDefaultTitle (PDocument);
// Avoid Creating Temporary Compound File When Starting Up Invisible
IF (! bmakevisible)
PDocument-> m_bembedded = true;
IF (! pDocument-> onnewdocument ())
{
Pframe-> destroyWindow ();
Return NULL;
}
m_nuntitledcount ;
}
Else
{
/ / Open an existing file
CWAITCURSOR WAIT;
IF (! pDocument-> onopendocument (lpszpathname))
{
// User Has Be aleted to what failed in onopendocument
Trace0 ("cdocument :: onopendocument returned false./N);
Pframe-> destroyWindow ();
Return NULL;
PDocument-> setPathname (lpszpathname);
}
InitialUpdateFrame (pframe, pdocument, bmakevisible);
Return PDocument;
}
From the above OpenDocumentFile function, use the CreateNewDocument object to make the document object with the template object, using the CreateNewFrame function to make the frame window with documents, view, and templates:
CframeWnd * CDOCTemplate :: CreateNewFrame (cdocument * pdoc, cframewnd * pother)
{
IF (PDOC! = NULL)
Ask_VALID (PDOC);
AskERT (m_nidResource! = 0); // must have resource ID
CcreateContext context;
Context.m_pcurrentframe = Pother;
CONTEXT.M_PCURRENTDOC = PDOC;
CONTEXT.M_PNEWVIEWCLASS = m_pviewclass;
Context.m_pnewdoctemplate = this;
IF (m_pframeclass == null)
{
......
}
CframeWnd * pframe = (cframewnd *) m_pframeclass-> createObject ();
IF (pframe == null)
{
......
Return NULL;
}
Ask_KINDOF (CFrameWnd, Pframe);
IF (context.m_pnewviewclass == null)
Trace0 ("Warning: Creating Frame with no default view./n");
IF (! pframe-> loadingframe (m_nidresource,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTILE, // DEFAULT FRAME STYLES
Null, & context)
{
......
Return NULL;
}
Return Pframe;
}
Summary: Use your own data structure in the template to maintain your own document objects and provide the getFirstDocPosition and getNextDoc functions to access access to these documents. So, in an application with multiple document templates, even if each template uses the same type of document class, each new or open document is not shared between these document templates.