23.6 Navigating app, doc, and view

We've already discussed how MFC uses the Document-View architecture in Chapter 5: Software Design Patterns. This section fills in a little more information about how we can navigate among the app, the doc and the view.

In MFC you can always get there from here. But the pathways are a little oddly marked. Table 23.2 lists the code to use to get a pointer to the classes listed in the top row from inside a method belonging to class in the left column.

The ::AfxGetApp() is a global MFC function, by the way; we write the ':: ' in front of it to remind ourselves that it's a global function rather than being a method of any particular class.

The access methods in the fine print look kind of unpleasant. In reality, however, they are just using a standard kind of pattern for iterating through a CList, or linked list, of objects. We use one of these odd-looking methods in the CWinApp::animateAllDocs method discussed in Chapter 6: Animation. We don't use them all that often, though. We only print them here so that you know they exist. And they are convenient for occasionally walking through all possible documents or all possible views, for instance if you want to count all the open views that you have.

If all you need is a pointer to the currently active view, there is an easier way to get to it from either CWinApp or CDocument ; you can use the global ::AfxGetMainWnd() method to get the main window, downcast the CWnd* return value into a CMDIFrameWnd*, and then move down the window levels using the calls we'll discuss in the next section.

CView* activeview = 
    ((CMDIFrameWnd *)::AfxGetMainWnd())->MDIGetActive() 

The exact way that you write the parentheses for the cast is important. To be more formal and possibly more readable, you can instead use the C++ dynamic_cast operator and write lines like the following. To help you catch errors, the dynamic_cast operator returns a NULL pointer if the cast is for some reason impossible.

CMDIFrameWnd *pframewnd = 
ASSERT(pframewnd); //Is NULL if the cast failed. 
pframewnd ->MDIGetActive()->GetActiveView(); 

Walking through the open documents

In this subsection we'll give some details about how the CPopApp::animateAllDocs code is used to find all the open documents and call their stepDoc(dt) methods.

Windows uses 'MDI' to stand for 'multiple document interface'. A MDI program groups the open documents in terms of 'templates.' The way we find all open documents is to look at all the available templates, and look at all the associated documents for each of them.

Table 23.2. How MFC app, doc, and view objects can access each other.
[View Full Width]

You might wonder why we need to talk about templates at all? This is because it is possible for an MDI program to open different kinds of documents, and for each of these kinds there is a 'template' or, as MFC puts it, a CMultiDocTemplate. Your Visual Studio program, for instance, opens up *.cpp files in a text editor, but it opens up *.rc files with a two-pane view that has a tree view of the resources on the left and a WYSIWYG window of the currently selected resource on the right. And it opens up *.dsw workspace files in yet another way. So clearly the code for Visual Studio must involve several kinds of CMultiDocTemplate classes.

Our Pop programs don't do anything this sophisticated: they have only one kind of document template, a standard template which is constructed by the AppWizard-generated CPopApp::InitInstance code inside the Pop.cpp file. The documents of a given template type are stored as a linked list.

void CPopApp::animateAllDocs(Real dt) 
    CMultiDocTemplate* pSelectedTemplate; 
    CPopDoc* pDoc; 
    POSITION docpos; 
    POSITION pos = GetFirstDocTemplatePosition(); 
    if (pos == NULL) 
        return; //No doc template exists yet. 
    pSelectedTemplate = (CMultiDocTemplate*)GetNextDocTemplate(pos); 
        //Only look at first template 
    docpos = pSelectedTemplate->GetFirstDocPosition(); 
        //find first document 
    while(docpos != NULL) 
        //while there are documents left to process 
        pDoc = (CPopDoc*)pSelectedTemplate->GetNextDoc(docpos); /* 
            This retrieves the document at position docpos and then 
            increments docpos to the next position. */ 
        pDoc->stepDoc(dt); /* Will animate the *pDoc and 
            call UpdateAllViews to update the *pDoc's views. */ 

It's not important that you understand the iteration code, but it's worth glancing at. This type of code is common in MFC. The business about using a POSITION object to iterate through the collection is a standard technique used for iterating through CList template objects, which is how MFC implements linked lists.

We assume here that the first template we find is the only one to worry about. There's a similar sort of method for walking through all of a document's views, but we normally use the default CDocument::UpdateAllViews for that.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference