The EX16B Example

This second SDI example improves on EX16A 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 CEx16bView 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 EX16B window, shown in Figure 16-3, looks a little different from the EX16A window shown in Figure 16-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.

click to view at full size.

Figure 16-3. The EX16B program in action.

The toolbar buttons function as follows.

ButtonFunction
click to view at full size. 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

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 listing figures, boldface 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 ex16b.rc defines the application's resources as follows.

Toolbar

The toolbar (visible in Figure 16-3) 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. The Flip Vertical command (on the Image menu) was used to duplicate some of the tiles. The ex16b.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.

Edit Menu

On the Edit menu, the clipboard menu items are replaced by the Clear All menu item. See step 2 for an illustration of the Edit menu.

The IDD_STUDENT Dialog Template

The IDD_STUDENT dialog template, shown here, is similar to the EX16A dialog shown in Figure 16-1 except that the Enter pushbutton has been replaced by the Clear pushbutton.

The following IDs identify the controls.

ControlID
Name edit controlIDC_NAME
Grade edit controlIDC_GRADE
Clear pushbuttonIDC_CLEAR

The controls' styles are the same as for the EX16A program.

Code Requirements

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

Header FileSource Code FileClassesDescription
ex16b.hex16b.cppCEx16bAppApplication class (from AppWizard)
  CAboutDlgAbout dialog
MainFrm.hMainFrm.cppCMainFrameSDI main frame
StuDoc.hStuDoc.cppCStudentDocStudent document
StuView.hStuView.cpp CStudentViewStudent form view (derived from CFormView)
Student.hStudent.cppCStudentStudent record (similar to EX16A)
StdAfx.hStdAfx.cppIncludes the standard precompiled headers 

CEx16bApp

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

CMainFrame

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

CStudent

This is the code from EX16A, except for the following line added at the end of Student.h:

typedef CTypedPtrList<CObList, CStudent*> CStudentList;

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

#include <afxtempl.h>

CStudentDoc

AppWizard originally generated the CStudentDoc class. Figure 16-4 shows the code used in the EX16B example.

STUDOC.H

// StuDoc.h : interface of the CStudentDoc class // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_STUDOC_H__4D011047_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_) #define AFX_STUDOC_H__4D011047_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "student.h" class CStudentDoc : public CDocument { protected: // create from serialization only     CStudentDoc();     DECLARE_DYNCREATE(CStudentDoc) // Attributes public:     CStudentList* GetList() {         return &m_studentList;     } // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CStudentDoc)     public:     virtual BOOL OnNewDocument();     virtual void Serialize(CArchive& ar);     virtual void DeleteContents();     //}}AFX_VIRTUAL // Implementation public:     virtual ~CStudentDoc(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected:     //{{AFX_MSG(CStudentDoc)     afx_msg void OnEditClearAll();     afx_msg void OnUpdateEditClearAll(CCmdUI* pCmdUI);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() private:     CStudentList m_studentList; }; ////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations //  immediately before the previous line. #endif // !defined(AFX_STUDOC_H__4D011047_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_)

STUDOC.CPP

// StuDoc.cpp : implementation of the CStudentDoc class // #include "stdafx.h" #include "ex16b.h" #include "StuDoc.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////////////////////////////////////////////////////////// // CStudentDoc IMPLEMENT_DYNCREATE(CStudentDoc, CDocument) BEGIN_MESSAGE_MAP(CStudentDoc, CDocument)     //{{AFX_MSG_MAP(CStudentDoc)     ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll)     ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR_ALL, OnUpdateEditClearAll)     //}}AFX_MSG_MAP END_MESSAGE_MAP() ////////////////////////////////////////////////////////////////////// // CStudentDoc construction/destruction CStudentDoc::CStudentDoc() {     TRACE("Entering CStudentDoc constructor\n"); #ifdef _DEBUG     afxDump.SetDepth(1); // Ensure dump of list elements #endif // _DEBUG } CStudentDoc::~CStudentDoc() { } BOOL CStudentDoc::OnNewDocument() {     TRACE("Entering CStudentDoc::OnNewDocument\n");     if (!CDocument::OnNewDocument())         return FALSE;     // TODO: add reinitialization code here     // (SDI documents will reuse this document)     return TRUE; } ////////////////////////////////////////////////////////////////////// // CStudentDoc serialization void CStudentDoc::Serialize(CArchive& ar) {     if (ar.IsStoring())     {         // TODO: add storing code here     }     else     {         // TODO: add loading code here     } } ////////////////////////////////////////////////////////////////////// // CStudentDoc diagnostics #ifdef _DEBUG void CStudentDoc::AssertValid() const {     CDocument::AssertValid(); } void CStudentDoc::Dump(CDumpContext& dc) const {     CDocument::Dump(dc);     dc << "\n" << m_studentList << "\n"; } #endif //_DEBUG ////////////////////////////////////////////////////////////////////// // CStudentDoc commands void CStudentDoc::DeleteContents()  { #ifdef _DEBUG     Dump(afxDump); #endif     while (m_studentList.GetHeadPosition()) {         delete m_studentList.RemoveHead();     } } void CStudentDoc::OnEditClearAll()  {     DeleteContents();     UpdateAllViews(NULL); } void CStudentDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI)  {     pCmdUI->Enable(!m_studentList.IsEmpty()); }
Figure 16-4. The CStudentDoc class listing.

ClassWizard and CStudentDoc

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

Object IDMessageMember Function
ID_EDIT_CLEAR_ALLCOMMANDOnEditClearAll
ID_EDIT_CLEAR_ALLON_UPDATE_COMMAND_UIOnUpdateEditClearAll

Data Members

The document class provides for an embedded CStudentList object, the m_studentList data member, which holds pointers to CStudent objects. The list object is constructed when the CStudentDoc object is constructed, and it is destroyed at program exit. CStudentList is a typedef for a CTypedPtrList for CStudent pointers.

Constructor

The document constructor sets the depth of the dump context so that a dump of the list causes dumps of the individual list elements.

GetList

The inline GetList function helps isolate the view from the document. The document class must be specific to the type of object in the list—in this case, objects of the class CStudent. A generic list view base class, however, can use a member function to get a pointer to the list without knowing the name of the list object.

DeleteContents

The DeleteContents function is a virtual override function that is called by other document functions and by the application framework. Its job is to remove all student object pointers from the document's list and to delete those student objects. An important point to remember here is that SDI document objects are reused after they are closed. DeleteContents also dumps the student list.

Dump

AppWizard generates the Dump function skeleton between the lines #ifdef _DEBUG and #endif. Because the afxDump depth was set to 1 in the document constructor, all the CStudent objects contained in the list are dumped.

CStudentView

Figure 16-5 shows the code for the CStudentView class. This code will be carried over into the next two chapters.

STUVIEW.H

// StuView.h : interface of the CStudentView class // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_STUVIEW_H__4D011049_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_) #define AFX_STUVIEW_H__4D011049_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CStudentView : public CFormView { protected:     POSITION      m_position; // current position in document list     CStudentList* m_pList;    // copied from document protected: // create from serialization only     CStudentView();     DECLARE_DYNCREATE(CStudentView) public:     //{{AFX_DATA(CStudentView)     enum { IDD = IDD_STUDENT };     int     m_nGrade;     CString m_strName;     //}}AFX_DATA // Attributes public:     CStudentDoc* GetDocument(); // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CStudentView)     public:     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);     protected:     virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support     virtual void OnInitialUpdate(); // called first time after construct     virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);     //}}AFX_VIRTUAL // Implementation public:     virtual ~CStudentView(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif protected:     virtual void ClearEntry();     virtual void InsertEntry(POSITION position);     virtual void GetEntry(POSITION position); // Generated message map functions protected:     //{{AFX_MSG(CStudentView)     afx_msg void OnClear();     afx_msg void OnStudentHome();     afx_msg void OnStudentEnd();     afx_msg void OnStudentPrev();     afx_msg void OnStudentNext();     afx_msg void OnStudentIns();     afx_msg void OnStudentDel();     afx_msg void OnUpdateStudentHome(CCmdUI* pCmdUI);     afx_msg void OnUpdateStudentEnd(CCmdUI* pCmdUI);     afx_msg void OnUpdateStudentDel(CCmdUI* pCmdUI);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; #ifndef _DEBUG  // debug version in StuView.cpp inline CStudentDoc* CStudentView::GetDocument()    { return (CStudentDoc*)m_pDocument; } #endif ////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations //  immediately before the previous line. #endif // !defined(AFX_STUVIEW_H__4D011049_7E1C_11D0_8FE0_00C04FC2A0C2__INCLUDED_)

STUVIEW.CPP

// StuView.cpp : implementation of the CStudentView class // #include "stdafx.h" #include "ex16b.h" #include "StuDoc.h" #include "StuView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__ ; #endif ////////////////////////////////////////////////////////////////////// // CStudentView IMPLEMENT_DYNCREATE(CStudentView, CFormView) BEGIN_MESSAGE_MAP(CStudentView, CFormView)     //{{AFX_MSG_MAP(CStudentView)     ON_BN_CLICKED(IDC_CLEAR, OnClear)     ON_COMMAND(ID_STUDENT_HOME, OnStudentHome)     ON_COMMAND(ID_STUDENT_END, OnStudentEnd)     ON_COMMAND(ID_STUDENT_PREV, OnStudentPrev)     ON_COMMAND(ID_STUDENT_NEXT, OnStudentNext)     ON_COMMAND(ID_STUDENT_INS, OnStudentIns)     ON_COMMAND(ID_STUDENT_DEL, OnStudentDel)     ON_UPDATE_COMMAND_UI(ID_STUDENT_HOME, OnUpdateStudentHome)     ON_UPDATE_COMMAND_UI(ID_STUDENT_END, OnUpdateStudentEnd)     ON_UPDATE_COMMAND_UI(ID_STUDENT_PREV, OnUpdateStudentHome)     ON_UPDATE_COMMAND_UI(ID_STUDENT_NEXT, OnUpdateStudentEnd)     ON_UPDATE_COMMAND_UI(ID_STUDENT_DEL, OnUpdateStudentDel)     //}}AFX_MSG_MAP END_MESSAGE_MAP() ////////////////////////////////////////////////////////////////////// // CStudentView construction/destruction CStudentView::CStudentView()     : CFormView(CStudentView::IDD) {     TRACE("Entering CStudentView constructor\n");     //{{AFX_DATA_INIT(CStudentView)     m_nGrade = 0;     m_strName = _T("");     //}}AFX_DATA_INIT     m_position = NULL; } CStudentView::~CStudentView() { } void CStudentView::DoDataExchange(CDataExchange* pDX) {     CFormView::DoDataExchange(pDX);     //{{AFX_DATA_MAP(CStudentView)     DDX_Text(pDX, IDC_GRADE, m_nGrade);     DDV_MinMaxInt(pDX, m_nGrade, 0, 100);     DDX_Text(pDX, IDC_NAME, m_strName);     DDV_MaxChars(pDX, m_strName, 20);     //}}AFX_DATA_MAP } BOOL CStudentView::PreCreateWindow(CREATESTRUCT& cs) {     // TODO: Modify the Window class or styles here by modifying     //  the CREATESTRUCT cs     return CFormView::PreCreateWindow(cs); } void CStudentView::OnInitialUpdate()  {     TRACE("Entering CStudentView::OnInitialUpdate\n");     m_pList = GetDocument()->GetList();     CFormView::OnInitialUpdate(); } ////////////////////////////////////////////////////////////////////// // CStudentView diagnostics #ifdef _DEBUG void CStudentView::AssertValid() const {     CFormView::AssertValid(); } void CStudentView::Dump(CDumpContext& dc) const {     CFormView::Dump(dc); } CStudentDoc* CStudentView::GetDocument() // non-debug version is inline {     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CStudentDoc)));     return (CStudentDoc*)m_pDocument; } #endif //_DEBUG ////////////////////////////////////////////////////////////////////// // CStudentView message handlers void CStudentView::OnClear()  {     TRACE("Entering CStudentView::OnClear\n");     ClearEntry(); } void CStudentView::OnUpdate(CView* pSender, LPARAM lHint,                             CObject* pHint)  {     // called by OnInitialUpdate and by UpdateAllViews     TRACE("Entering CStudentView::OnUpdate\n");     m_position = m_pList->GetHeadPosition();     GetEntry(m_position); // initial data for view } void CStudentView::OnStudentHome()  {     TRACE("Entering CStudentView::OnStudentHome\n");     // need to deal with list empty condition     if (!m_pList->IsEmpty()) {         m_position = m_pList->GetHeadPosition();         GetEntry(m_position);     } } void CStudentView::OnStudentEnd()  {     TRACE("Entering CStudentView::OnStudentEnd\n");     if (!m_pList->IsEmpty()) {         m_position = m_pList->GetTailPosition();         GetEntry(m_position);     } } void CStudentView::OnStudentPrev()  {     POSITION pos;     TRACE("Entering CStudentView::OnStudentPrev\n");     if ((pos = m_position) != NULL) {         m_pList->GetPrev(pos);         if (pos) {             GetEntry(pos);       m_position = pos;         }     } } void CStudentView::OnStudentNext()  {     POSITION pos;     TRACE("Entering CStudentView::OnStudentNext\n");     if ((pos = m_position) != NULL) {         m_pList->GetNext(pos);         if (pos) {             GetEntry(pos);             m_position = pos;         }     } } void CStudentView::OnStudentIns()  {     TRACE("Entering CStudentView::OnStudentIns\n");     InsertEntry(m_position);     GetDocument()->SetModifiedFlag();     GetDocument()->UpdateAllViews(this); } void CStudentView::OnStudentDel()  {     // deletes current entry and positions to next one or head     POSITION pos;     TRACE("Entering CStudentView::OnStudentDel\n");     if ((pos = m_position) != NULL) {         m_pList->GetNext(pos);         if (pos == NULL) {             pos = m_pList->GetHeadPosition();             TRACE("GetHeadPos = %ld\n", pos);             if (pos == m_position) {                 pos = NULL;             }         }         GetEntry(pos);         CStudent* ps = m_pList->GetAt(m_position);         m_pList->RemoveAt(m_position);         delete ps;         m_position = pos;         GetDocument()->SetModifiedFlag();         GetDocument()->UpdateAllViews(this);     } } void CStudentView::OnUpdateStudentHome(CCmdUI* pCmdUI)  {     // called during idle processing and when Student menu drops down     POSITION pos;     // enables button if list not empty and not at home already     pos = m_pList->GetHeadPosition();     pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } void CStudentView::OnUpdateStudentEnd(CCmdUI* pCmdUI)  {     // called during idle processing and when Student menu drops down     POSITION pos;     // enables button if list not empty and not at end already     pos = m_pList->GetTailPosition();     pCmdUI->Enable((m_position != NULL) && (pos != m_position)); } void CStudentView::OnUpdateStudentDel(CCmdUI* pCmdUI)  {     // called during idle processing and when Student menu drops down     pCmdUI->Enable(m_position != NULL); } void CStudentView::GetEntry(POSITION position) {     if (position) {         CStudent* pStudent = m_pList->GetAt(position);         m_strName = pStudent->m_strName;         m_nGrade = pStudent->m_nGrade;     }     else {         ClearEntry();     }     UpdateData(FALSE); } void CStudentView::InsertEntry(POSITION position) {     if (UpdateData(TRUE)) {         // UpdateData returns FALSE if it detects a user error         CStudent* pStudent = new CStudent;         pStudent->m_strName = m_strName;         pStudent->m_nGrade = m_nGrade;         m_position = m_pList->InsertAfter(m_position, pStudent);     } } void CStudentView::ClearEntry() {     m_strName = "";     m_nGrade = 0;     UpdateData(FALSE);     ((CDialog*) this)->GotoDlgCtrl(GetDlgItem(IDC_NAME)); }

Figure 16-5. The CStudentView class listing.

ClassWizard and CStudentView

ClassWizard was used to map the CStudentView Clear pushbutton notification message as follows.

Object IDMessageMember Function
IDC_CLEARBN_CLICKEDOnClear

Because CStudentView is derived from CFormView, ClassWizard supports the definition of dialog data members. The variables shown here were added with the Add Variables button.

Control IDMember VariableCategoryVariable Type
IDC_GRADEm_nGradeValue int
IDC_NAMEm_strNameValue CString

The minimum value of the m_nGrade data member was set to 0, and its maximum value was set to 100. The maximum length of the m_strName data member was set to 20 characters.

ClassWizard maps toolbar button commands to their handlers. Here are the commands and the handler functions to which they were mapped.

Object IDMessageMember Function
ID_STUDENT_HOMECOMMANDOnStudentHome
ID_STUDENT_ENDCOMMANDOnStudentEnd
ID_STUDENT_PREVCOMMANDOnStudentPrev
ID_STUDENT_NEXTCOMMANDOnStudentNext
ID_STUDENT_INSCOMMANDOnStudentIns
ID_STUDENT_DELCOMMANDOnStudentDel

Each command handler has built-in error checking.

The following update command UI message handlers are called during idle processing to update the state of the toolbar buttons and, when the Student menu is painted, to update the menu items.

Object IDMessageMember Function
ID_STUDENT_HOMEUPDATE_COMMAND_UI OnUpdateStudentHome
ID_STUDENT_ENDUPDATE_COMMAND_UI OnUpdateStudentEnd
ID_STUDENT_PREVUPDATE_COMMAND_UI OnUpdateStudentHome
ID_STUDENT_NEXTUPDATE_COMMAND_UI OnUpdateStudentEnd
ID_STUDENT_DELUPDATE_COMMAND_UI OnUpdateCommandDel

For example, this button,

which retrieves the first student record, is disabled when the list is empty and when the m_position variable is already set to the head of the list. The Previous button is disabled under the same circumstances, so it uses the same update command UI handler. The End and the Next buttons share a handler for similar reasons. Because a delay sometimes occurs in calling the update command UI functions, the command message handlers must look for error conditions.

Data Members

The m_position data member is a kind of cursor for the document's collection. It contains the position of the CStudent object that is currently displayed. The m_pList variable provides a quick way to get at the student list in the document.

OnInitialUpdate

The virtual OnInitialUpdate function is called when you start the application. It sets the view's m_pList data member for subsequent access to the document's list object.

OnUpdate

The virtual OnUpdate function is called both by the OnInitialUpdate function and by the CDocument::UpdateAllViews function. It resets the list position to the head of the list, and it displays the head entry. In this example, the UpdateAllViews function is called only in response to the Edit Clear All command. In a multiview application, you might need a different strategy for setting the CStudentView m_position variable in response to document updates from another view.

Protected Virtual Functions

The following three functions are protected virtual functions that deal specifically with CStudent objects:

GetEntry

InsertEntry

ClearEntry

You can transfer these functions to a derived class if you want to isolate the general-purpose list-handling features in a base class.

Testing the EX16B Application

Fill in the student name and grade fields, and then click this button

to insert the entry into the list. Repeat this action several times, using the Clear pushbutton to erase the data from the previous entry. When you exit the application, the debug output should look similar to this:

a CStudentDoc at $4116D0 m_strTitle = Untitled m_strPathName =  m_bModified = 1 m_pDocTemplate = $4113F1 a CObList at $411624 with 4 elements     a CStudent at $412770 m_strName = Fisher, Lon m_nGrade = 67     a CStudent at $412E80 m_strName = Meyers, Lisa m_nGrade = 80     a CStudent at $412880 m_strName = Seghers, John m_nGrade = 92     a CStudent at $4128F0 m_strName = Anderson, Bob m_nGrade = 87


Programming Microsoft Visual C++
Programming Microsoft Visual C++
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 332

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net