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:
|
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:
The sequence of events for the complex document-view interaction is shown here.
|
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:
It returns a pointer to the "current" object in the list, identified by the incoming value of the POSITION parameter.
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.
--------------------------------------------------------------------------------
Figure 16: AppWizard step 6 of 6, changing view's base class to CFormView.
The options and the default class names are shown here.
Figure 17: MYMFC16 project summary.
This SDI example improves on MYMFC15 in the following ways:
Instead of a single embedded CStudent object, the document now contains a list of CStudent objects. Now you see the reason for using the CStudent class instead of making m_strName and m_nGrade data members of the document.
Toolbar buttons allow the user to sequence through the list.
The application is structured to allow the addition of extra views. The Edit Clear All command is now routed to the document object, so the document's UpdateAllViews() function and the view's OnUpdate() function are brought into play.
The student-specific view code is isolated so that the CMymfc16View class can later be transformed into a base class that contains only general-purpose code. Derived classes can override selected functions to accommodate lists of application-specific objects.
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.
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.
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.
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.
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.
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.
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 pre-compiled 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.
Figure 24: Creating and adding new files, Student.h and Student.cpp (for new class) 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. |
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:
MSDN MFC 9.0 class library online documentation - latest version.
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multi-byte character set: Story and program examples.