| Tenouk C & C++ | MFC Home | Separating the Document from Its View 2 | Separating the Document from Its View 4 | Download | Site Index |


 

 

 

 

 

Module 10b:

Separating the Document from Its View 3

 

 

 

 

 

 

 

This is a continuation from the previous module... Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:

  1. A More Advanced Document-View Interaction

  2. The CDocument::DeleteContents Function

  3. The CObList Collection Class

  4. Using the CObList Class for a First-In, First-Out List

  5. CObList Iteration: The POSITION Variable

  6. The CTypedPtrList Template Collection Class

  7. The Dump Context and Collection Classes

  8. The MYMFC16 Example

  9. Resource Requirements

  10. Toolbar

  11. Student Menu

  12. Edit Menu

  13. The IDD_MYMFC16_FORM Dialog Template

  14. Code Requirements

  15. CMymfc16App Class

  16. CMainFrame Class

  17. CStudent Class

  18. ClassWizard and CMymfc16Doc Class

 

 

 

A More Advanced Document-View Interaction

 

If you're laying the groundwork for a multi view application, the document-view interaction must be more complex than the simple interaction in example MYMFC15. The fundamental problem is this: the user edits in view #1, so view #2 (and any other views) must be updated to reflect the changes. Now you need the UpdateAllViews() and OnUpdate() functions because the document is going to act as the clearinghouse for all view updates. The development steps are shown here:

 

  1. In your derived document class header file (generated by AppWizard), declare your document's data members. If you want to, you can make these data members private and you can define member functions to access them or declare the view class as a friend of the document class.

  2. In your derived view class, use ClassWizard to override the OnUpdate() virtual member function. The application framework calls this function whenever the document data has changed for any reason. OnUpdate() should update the view with the current document data.

  3. Evaluate all your command messages. Determine whether each one is document-specific or view-specific. A good example of a document-specific command is the Clear All command on the Edit menu. Now map the commands to the appropriate classes.

  4. In your derived view class, allow the appropriate command message handlers to update the document data. Be sure these message handlers call the CDocument::UpdateAllViews function before they exit. Use the type-safe version of the CView::GetDocument member function to access the view's document.

  5. In your derived document class, allow the appropriate command message handlers to update the document data. Be sure that these message handlers call the CDocument::UpdateAllViews function before they exit.

 

The sequence of events for the complex document-view interaction is shown here.

 

Sequence

Description

Application starts

CMyDocument object constructed

CMyView object constructed

Other view objects constructed

View windows created

CMyView::OnCreate called (if mapped)

CDocument::OnNewDocument called

CView::OnInitialUpdate called

Calls CMyView::OnUpdate

Initializes the view

User executes view command

CMyView functions update CMyDocument data members

Call CDocument::UpdateAllViews

Other views' OnUpdate() functions called

User executes document command

CMyDocument functions update data  members

Call CDocument::UpdateAllViews

CMyView::OnUpdate called

Other views' OnUpdate() functions called

User exits application

View objects destroyed

CMyDocument object destroyed

 

Table 5.

 

The CDocument::DeleteContents Function

 

At some point, you'll need a function to delete the contents of your document. You could write your own private member function, but it happens that the application framework declares a virtual DeleteContents() function for the CDocument class. The application framework calls your overridden DeleteContents() function when the document is closed and as you'll see in the next module, at other times as well.

 

The CObList Collection Class

 

Once you get to know the collection classes, you'll wonder how you ever got along without them. The CObList class is a useful representative of the collection class family. If you're familiar with this class, it's easy to learn the other list classes, the array classes, and the map classes.

You might think that collections are something new, but the C programming language has always supported one kind of collection, the array. C arrays must be fixed in size, and they do not support insertion of elements. Many C programmers have written function libraries for other collections, including linked lists, dynamic arrays, and indexed dictionaries. For implementing collections, the C++ class is an obvious and better alternative than a C function library. A list object, for example, neatly encapsulates the list's internal data structures.

The CObList class supports ordered lists of pointers to objects of classes derived from CObject. Another MFC collection class, CPtrList, stores void pointers instead of CObject pointers. Why not use CPtrList instead? The CObList class offers advantages for diagnostic dumping, which you'll see in this chapter, and for serialization, which you'll see in the next chapter. One important feature of CObList is that it can contain mixed pointers. In other words, a CObList collection can hold pointers to both CStudent objects and CTeacher objects, assuming that both CStudent and CTeacher were derived from CObject.

 

Using the CObList Class for a First-In, First-Out List

 

One of the easiest ways to use a CObList object is to add new elements to the tail, or bottom, of the list and to remove elements from the head, or top, of the list. The first element added to the list will always be the first element removed from the head of the list. Suppose you're working with element objects of class CAction, which is your own custom class derived from CObject. A command-line program that puts five elements into a list and then retrieves them in the same sequence is shown here:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <afx.h>

#include <afxcoll.h>

 

class CAction : public CObject

{

private:

    int m_nTime;

public:

    // Constructor stores integer time value

    CAction(int nTime) { m_nTime = nTime; }

    void PrintTime() { TRACE("time = %d\n", m_nTime); }

};

 

int main()

{

    CAction* pAction;

    // action list constructed on stack

    CObList actionList;

    int i;

 

    // inserts action objects in sequence {0, 1, 2, 3, 4}

    for (i = 0; i < 5; i++)

    {

        pAction = new CAction(i);

        // no cast necessary for pAction

        actionList.AddTail(pAction);

    }

 

    // retrieves and removes action objects

    // in sequence {0, 1, 2, 3, 4}

    while (!actionList.IsEmpty())

    {  

        // cast required for return value

        pAction = (CAction*) actionList.RemoveHead();

        pAction->PrintTime();

        delete pAction;

    }

 

    return 0;

}

 

Here's what's going on in the program. First a CObList object, actionList, is constructed. Then the CObList::AddTail member function inserts pointers to newly constructed CAction objects. No casting is necessary for pAction because AddTail() takes a CObject pointer parameter and pAction is a pointer to a derived class. Next the CAction object pointers are removed from the list of the objects deleted. A cast is necessary for the returned value of RemoveHead() because RemoveHead() returns a CObject pointer that is higher in the class hierarchy than CAction. When you remove an object pointer from a collection, the object is not automatically deleted. The delete statement is necessary for deleting the CAction objects.

 

CObList Iteration: The POSITION Variable

 

Suppose you want to iterate through the elements in a list. The CObList class provides a GetNext() member function that returns a pointer to the "next" list element, but using it is a little tricky. GetNext() takes a parameter of type POSITION, which is a 32-bit variable. The POSITION variable is an internal representation of the retrieved element's position in the list. Because the POSITION parameter is declared as a reference (&), the function can change its value.

 

GetNext() does the following:

 

  1. It returns a pointer to the "current" object in the list, identified by the incoming value of the POSITION parameter.

  2. It increments the value of the POSITION parameter to the next list element.

 

Here's what a GetNext() loop looks like, assuming you're using the list generated in the previous example:

 

CAction* pAction;

POSITION pos = actionList.GetHeadPosition();

while (pos != NULL)

{

    pAction = (CAction*) actionList.GetNext(pos);

    pAction->PrintTime();

}

 

Now suppose you have an interactive Windows-based application that uses toolbar buttons to sequence forward and backward through the list one element at a time. You can't use GetNext() to retrieve the entry because GetNext() always increments the POSITION variable and you don't know in advance whether the user is going to want the next element or the previous element. Here's a sample view class command message handler function that gets the next list entry. In the CMyView class, m_actionList is an embedded CObList object and the m_position data member is a POSITION variable that holds the current list position.

 

CMyView::OnCommandNext()

{

    POSITION pos;

    CAction*  pAction;

 

    if ((pos = m_position) != NULL)

    {

        m_actionList.GetNext(pos);

        if (pos != NULL)

        { // pos is NULL at end of list

            pAction = (CAction*) m_actionList.GetAt(pos);

            pAction->PrintTime();

            m_position = pos;

        }

        else

        {

            AfxMessageBox("End of list reached");

        }

    }

}

 

GetNext() is now called first to increment the list position, and the CObList::GetAt member function is called to retrieve the entry. The m_position variable is updated only when we're sure we're not at the tail of the list.

 

The CTypedPtrList Template Collection Class

 

The CObList class works fine if you want a collection to contain mixed pointers. If, on the other hand, you want a type-safe collection that contains only one type of object pointer, you should look at the MFC library template pointer collection classes. CTypedPtrList is a good example. Templates are a relatively new C++ language element, introduced by Microsoft Visual C++ version 2.0. CTypedPtrList is a template class that you can use to create a list of any pointers to objects of any specified class. To make a long story short, you use the template to create a custom derived list class, using either CPtrList or CObList as a base class. To declare an object for CAction pointers, you write the following line of code:

 

CTypedPtrList<CObList, CAction*> m_actionList;

 

The first parameter is the base class for the collection, and the second parameter is the type for parameters and return values. Only CPtrList and CObList are permitted for the base class because those are the only two MFC library pointer list classes. If you are storing objects of classes derived from CObject, you should use CObList as your base class; otherwise, use CPtrList. By using the template as shown above, the compiler ensures that all list member functions return a CAction pointer. Thus, you can write the following code:

 

pAction = m_actionList.GetAt(pos); // no cast required

 

If you want to clean up the notation a little, use a typedef statement to generate what looks like a class, as shown here:

 

typedef CTypedPtrList<CObList, CAction*> CActionList;

 

Now you can declare m_actionList as follows:

 

CActionList m_actionList;

 

The Dump Context and Collection Classes

 

The Dump() function for CObList and the other collection classes has a useful property. If you call Dump() for a collection object, you can get a display of each object in the collection. If the element objects use the DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC macros, the dump will show the class name for each object.

The default behavior of the collection Dump() functions is to display only class names and addresses of element objects. If you want the collection Dump() functions to call the Dump() function for each element object, you must, somewhere at the start of your program, make the following call:

 

#ifdef _DEBUG

    afxDump.SetDepth(1);

#endif

 

Now the statement:

 

#ifdef _DEBUG

    afxDump << actionList;

#endif

 

Produces output such as this:

 

a CObList at $411832

with 4 elements

    a CAction at $412CD6

time = 0

    a CAction at $412632

time = 1

    a CAction at $41268E

time = 2

    a CAction at $4126EA

time = 3

 

If the collection contains mixed pointers, the virtual Dump() function is called for the object's class and the appropriate class name is printed.

 

The MYMFC16 Project Example

 

Run AppWizard to generate SDI \mfcproject\mymfc16 project. In the Step 6 page, change the view's base class to CFormView, as shown here.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

--------------------------------------------------------------------------------

AppWizard step 6 of 6, changing view's base class to CFormView.

 

Figure 16: AppWizard step 6 of 6, changing view's base class to CFormView.

 

The options and the default class names are shown here.

 

MYMFC16 project summary.

 

Figure 17: MYMFC16 project summary.

 

This SDI example improves on MYMFC15 in the following ways:

The MYMFC16 window, shown in Figure 18, looks a little different from the MYMFC15 window shown in Figure 1. The toolbar buttons are enabled only when appropriate. The Next (arrow-down graphic) button, for example, is disabled when we're positioned at the bottom of the list.

 

The MYMFC16 program in action.

 

Figure 18: The MYMFC16 program in action.

 

 

The toolbar buttons function as follows.

 

Button

Function

Retrieves the first student record

Retrieves the last student record

Retrieves the previous student record

Retrieves the next student record

Deletes the current student record

Inserts a new student record

 

Table 6: MYMFC16 new toolbar buttons

 

The Clear button in the view window clears the contents of the Name and Grade edit controls. The Clear All command on the Edit menu deletes all the student records in the list and clears the view's edit controls. This example deviates from the step-by-step format in the previous examples. Because there's now more code, we'll simply list selected code and the resource requirements. In the code listing figures, brown color code indicates additional code or other changes that you enter in the output from AppWizard and ClassWizard. The frequent use of TRACE statements lets you follow the program's execution in the debugging window.

 

Resource Requirements

 

The file mymfc16.rc defines the application's resources as follows.

 

Toolbar

 

The toolbar was created by erasing the Edit Cut, Copy, and Paste tiles (fourth, fifth, and sixth from the left) and replacing them with six new patterns.

 

Creating new toolbar buttons for MYMFC16 project.

 

Figure 19: Creating new toolbar buttons for MYMFC16 project.

 

The Flip Vertical command (on the Image menu) was used to duplicate some of the tiles.

 

Toolbar’s button editor utility under Image menu.

 

Figure 20: Toolbar’s button editor utility under Image menu.

 

The mymfc16.rc file defines the linkage between the command IDs and the toolbar buttons.

 

Student Menu

 

Having menu options that correspond to the new toolbar buttons isn't absolutely necessary. ClassWizard allows you to map toolbar button commands just as easily as menu commands. However, most applications for Microsoft Windows have menu options for all commands, so users generally expect them.

 

New menu and its items for MYMFC16, in this example just for completeness.

 

Figure 21: New menu and its items for MYMFC16, in this example just for a completeness.

 

Edit Menu

 

On the Edit menu, the clipboard menu items are replaced by the Clear All menu item. See previous project example for an illustration of the Edit menu.

 

Adding and modifying new Edit menu item, Clear All.

 

Figure 22: Adding and modifying new Edit menu item, Clear All.

 

The IDD_MYMFC16_FORM Dialog Template

 

The IDD_MYMFC16_FORM dialog template, shown here, is similar to the MYMFC15 dialog shown in Figure 1 except that the Enter pushbutton has been replaced by the Clear pushbutton.

 

Modifying properties and adding new items to the IDD_MYMFC16_FORM dialog.

 

Figure 23: Modifying properties and adding new items to the IDD_MYMFC16_FORM dialog.

 

The following IDs identify the controls.

 

Control

ID

The dialog template

IDD_MYMFC16_FORM

Name edit control

IDC_NAME

Grade edit control

IDC_GRADE

Clear pushbutton

IDC_CLEAR

 

Table 7.

 

The controls' styles are the same as for the MYMFC15 program which the Styles properties are: Style = Child; Border = None and that Visible is unchecked.

 

Code Requirements

 

Here's a list of the files and classes in the MYMFC16 example.

 

Header File

Source Code File

Classes

Description

mymfc16.h

mymfc16.cpp

CMymfc16App

Application class (from AppWizard)

-

-

CAboutDlg

About dialog

MainFrm.h

MainFrm.cpp

CMainFrame

SDI main frame

mymfc16Doc.h

mymfc16Doc.cpp

CMymfc16Doc

Student document

mymfc16View.h

mymfc16View.cpp

CMymfc16View

Student form view (derived from CFormView)

Student.h

Student.cpp

CStudent

Student record (similar to MYMFC15)

StdAfx.h

StdAfx.cpp

Includes the standard precompiled headers

 

 

Table 8.

 

CMymfc16App Class

 

The files mymfc16.cpp and mymfc16.h are standard AppWizard output.

 

CMainFrame Class

 

The code for the CMainFrame class in MainFrm.cpp is standard AppWizard output.

 

CStudent Class

 

This is the code from MYMFC15. Insert new header (Student.h) and source (Student.cpp) files by using the Project, Add To Project and New… menu as shown below. Then, copy the MYMFC15 Student.h and Student.cpp codes and paste into the MYMFC16 Student.h and Student.cpp files respectively.

 

Creating and adding new files, Student.h and Student.cpp (for new class) to the project.

 

Figure 24: Creating and adding new files, Student.h and Student.cpp (for new class) to the project.

 

Creating and adding Student.cpp file to the project.

 

Figure 25: Creating and adding Student.cpp file to the project.

 

Next add the following line at the end of Student.h:

 

typedef CTypedPtrList<CObList, CStudent*> CStudentList;

 

 

Listing 11.

 

The use of the MFC template collection classes requires the following statement in StdAfx.h:

 

#include <afxtempl.h>

 

 

Listing 12.

 

ClassWizard and CMymfc16Doc

 

The Edit Clear All command is handled in the document class. The following message handlers were added through ClassWizard.

 

Object ID

Message

Member Function

ID_EDIT_CLEAR_ALL

COMMAND

OnEditClearAll()

ID_EDIT_CLEAR_ALL

ON_UPDATE_COMMAND_UI

OnUpdateEditClearAll()

 

Table 9.

 

Adding a message handler for the Edit Clear All command in the document class.

 

Figure 26: Adding a message handler for the Edit Clear All command in the document class.

 

Continue on next module...part 4.

 

 

 

 

Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. Porting & Migrating your older programs.

  4. MSDN Library

  5. DCOM at MSDN.

  6. COM+ at MSDN.

  7. COM at MSDN.

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 

 

 


 

| Tenouk C & C++ | MFC Home | Separating the Document from Its View 2 | Separating the Document from Its View 4 | Download | Site Index |