Tutorial: Building an Address Book II Program




To visit my site

HOME] Consulting Design Maintenance Project Testing Training Turnkey Java C++ SQL HTML JavaScript ]


To contact us

© 2002 - 2016 All Rights Reserved Total Application Works




Comments
  1. 12/10/2005 - From: Jeff K.
    The tutorial just help me a lot, thank you for that, 5 * from me. Hope to keep tutorial like that as MFC code is hard to read by beginner.

Introduction

This tutorial is intended for people who have some experience with C++. In this example, we will build an Address Book application that uses persistent data. In order for you to follow this tutorial, you will need a copy of the Microsoft Visual C++ compiler. In addition, this tutorial assumes that you have worked through the tutorial for the first version of the AddressBook application. A copy of this application can be found at My Stuff: Addressbook application

In the first version of this application, user options were presented using DOS menu concepts. In this version, user options will be presented using GUI (Graphical User Interface).


top

Requirements

Following are the requirements for the application we are tasked to build.

  1. Build an Address Book C++ .exe application.
  2. The application should be based on:
    • All class definitions in header files.
    • The class implementations in a .cpp file.
    • The file I/O methods from the first Address Book app.
  3. A main dialog that shows the following in a Listbox:
    • record ID
    • Last name
    • First name
    • Telephone number
  4. A main dialog that also shows buttons that allow the user to
    • Exit
    • Add
    • Update
    • Delete
    • Refresh
    • Cancel

      Exit Add Update Delete Refresh Cancel


  5. The data in the class definition for the main dialog should include:
    • recordID
    • firstName
    • lastName
    • address
    • city
    • state
    • zip
    • telephone
    • fax
    • email
  6. The behavior (methods) should include:
    • AddRec - Add a new record to the file
    • UpdateRec - Get data to update a record
    • DeleteRec - Delete a record from the file
    • DisplayData - Display all records in the file
  7. Create a file to maintain the persistent data. To maintain this file, create the following behaviors:
    • Read - Read a record from the file
    • Write - Write a record to the file
  8. If the file that maintains the persistent data is empty, create data to initialize the file.
  9. Create dialogs to:
    • Add - Gets user information to add a new record
    • Update - Gets user information to update a record
    • Delete - Gets user information to delete a record

How Do We Get Started?

We will use the Microsoft Visual C++ App Wizard to create our application. The Microsoft Visual C++ App Wizard will present some options to you and based on your responses, it will generate a skeleton of an application. The resulting skeletal application will have basic functionality and it will be up to us to add the capability specified by the requirements. The following will take you through the steps to build the Address Book application.

Building the Address Book app

To create our Address Book app, using the App Wizard, we will proceed with the following steps.

  1. Start Microsoft Visual C++
  2. Click on menu option File... and then click on New.
  3. In the left column, select MFC AppWizard (exe).
  4. In the right column and text box entitled Project Name, enter Addressbook.
  5. Accept the remaining defaults and then click on Ok.
  6. On the Step 1 dialog, we will select Dialog based as the type of application we want to create, and then click Next.



  7. On the Step 2 of 4 dialog, deselect ActiveX Controls and then click on Next.



  8. On the Step 3 of 4 dialog, accept the defaults and then click on Next.



  9. On the Step 4 of 4 dialog, accept the defaults and then click on Finish.



  10. On the New Project Information dialog, click on OK.
  11. At this point, we see the AddressBook dialog in the Edit window.



  12. On the second blue bar (title bar), where Addressbook_V is shown, right click on the title bar. This will cause the Properties Dialog to be displayed.



    • In the box next to Caption, change Addressbook to Address Book.
    • In the lower left corner of the Properties Dialog, click on the Font button. This will cause the font dialog to be displayed.



    • Select the Courier New font and Size 16 for font size... then click OK.
    • Courier New is a fixed space font and this is what we need to get straight columns in the List Box Control.
  13. When the Properties Dialog is redisplayed, press the Enter button on your keyboard.
  14. Drag and Drop the TODO: Place dialog controls here. static control to just under the thin blue line.
    • Right click on this static control. This will cause the Text Properties Dialog to appear.



    • Change the caption TODO: Place dialog controls here. to Record ID Last Name First Name Telephone
  15. Drag the OK and Cancel buttons to the bottom of the Address Book dialog.
  16. Right click on the OK button and change the caption to Exit.
  17. Add buttons to get the following:

    Exit Add Update Delete Refresh Cancel


  18. Add a List Box Control under the static control (Record ID Last Name First Name Telephone) and change the Control ID to IDC_ADDRESSLIST.
  19. At this point, the Address Book dialog should look like:



  20. In general, we will attach variables to text edit boxes and list boxes.
    • We will not attach variables to static controls or buttons (I am inconsistent in this area).
    • We will right click on any item (e.g. .cpp or .h file) in the edit window... then click on the ClassWizard
    • Select the Member Variables tab at the top of the ClassWizard Dialog.
    • Select Control ID IDC_ADDRESSLIST... Click on the Add Variable button.



    • This will cause the Add Member variable dialog to be displayed.
    • For the Member Variable Name, enter m_lAddress.
    • In the Variable Type, select CString... then click on OK.



  21. In the OnInitDialog() method and beneath the // TODO: Add extra initialization here comment, we will add the following code:
     
    // If the Addresbook.dat file is empty, initialize the .dat file
    if ( RecordCount() < 1 )
       InitData() ;
    
    DisplayData( RecordCount() )  ;
     

    The OnInitDialog() method is called when the application is first initialized.
  22. We will add the DisplayData method from the first AddressBook application and make some modifications.
     
    afx_msg void CAddressBook_4Dlg::DisplayData(  int n )    {    
        
       AddressStruct addStruct, *addStructPtr = &addStruct ;
    
       char chrSW15[15]   ;  
       strcpy( chrSW15 , "               " ) ;
       char chr[700]  ;  	  
       ostrstream str( chr , 700 ) ;
    
       // Get address of List Box
       pAddresses = ( CListBox * ) GetDlgItem( IDC_ADDRESSLIST );
    
       pAddresses->EnableWindow( TRUE ) ;
    
       for( int ii = 0; ii < n; ii++)    { 
    	  ReadData(ii, addStruct);
    
    	  sprintf( chrSW15, "%d", addStructPtr->recordID );  
              str.seekp( 0 ) ;
    	  str << setiosflags( ios::left  )  
                  << setw(12 )  << addStructPtr->recordID  
                  << setw(18 )  << addStructPtr->lastName 
                  << setw(22 )  << addStructPtr->firstName  
                  << setw(15 )  << addStructPtr->telephone  
                  <<  ends;  
     
          pAddresses->AddString( chr ) ; 
       }
    
       pAddresses->UpdateWindow();
    
    }
     

    Instead of using cout, we will use string stream ostrstream to output and format our data.
  23. We will get addressibility to the List Box Control with the following statement:
     
    // Get address of List Box
    pAddresses = ( CListBox * ) GetDlgItem( IDC_ADDRESSLIST );
     

    And we add the string to the List Box Control with the following statement:
     
    pAddresses->AddString( chr ) ;
     

  24. When a button, menu item, or a keyboard key is pressed, it causes an event. An event causes an alert to register in Windows, which in turn, alerts the program. If the program needs to respond to the alert, it must provide code to handle it. This code is called an event handler.

Event Handlers

An Event Handler is code that responds to events caused by clicking on a button, selecting a menu item, pressing a key, etc. All of the controls have a Control ID. In this application, the Control IDs are defined in the resource.h file. In addition, these Control IDs are part of the control defintion that is found in the AddressBook.rc file. For example,



At this point, we have defined a control and associated it with a control ID, and we have given the control ID a number. The following code snippet defined in the BEGIN_MESSAGE_MAP, tells Windows which method to call when the button is clicked.

 
ON_COMMAND( IDC_ADD , OnAdd)
 

In this example, the control ID associates the button with the callback method OnAdd(). When the Add, Update or Delete button is clicked, the appropriate dialog is displayed. and an example of the code to display the dialog is:
 
afx_msg void CAddressBook_4Dlg::OnAdd( int n )
{
   AddRecord *pAddDlg = new AddRecord;	 
   pAddDlg->DoModal() ;
   pAddDlg->OnOK ;
}
 

Final AddressBook Dialog Class Definition

The final class definition for the Address Book application is:

  

#include 
#include 
#include 

using std::string ;

/////////////////////////////////////////////////////////////////////////////
// CAddressBook_4Dlg dialog

class CAddressBook_4Dlg : public CDialog
{
// Construction
public:
	CAddressBook_4Dlg(CWnd* pParent = NULL);	// standard constructor
	 
	void Add( CString m_sFName , CString m_sLName , 		
              CString m_sAddress , CString  m_sCity  ,
              CString m_sState , CString m_sZip , 		
              CString m_sTele , CString  m_sFax  ,
              CString m_sEmail   ) ; 
	void UpDateRec( int m_iRecID , CString m_sFName , 
              CString m_sLName , 		
              CString m_sAddress , CString  m_sCity  ,
              CString m_sState , CString m_sZip , 		
              CString m_sTele , CString  m_sFax  ,
              CString m_sEmail   ) ;
	void Delete( int  m_iRecordID ) ;

	struct AddressStruct  {
            int recordID ;
            char firstName[30];
            char lastName[30];
            char address[30];
            char city[30];
            char state[20];
            char zip[15];
            char telephone[15];
            char fax[15];
            char email[30];
	};

	
	void ReadData(int, AddressStruct &);          // read record from file

// Dialog Data
   //{{AFX_DATA(CAddressBook_4Dlg)
   enum { IDD = IDD_ADDRESSBOOK_4_DIALOG };
   CString	m_lAddress;
   //}}AFX_DATA

   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CAddressBook_4Dlg)
protected:
   virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
   //}}AFX_VIRTUAL

// Implementation
protected:

    HICON m_hIcon;
    CListBox *pAddresses;

    // Generated message map functions
    //{{AFX_MSG(CAddressBook_4Dlg)
    virtual BOOL OnInitDialog();
	           
    afx_msg static int RecordCount();    // return number of records in file
	                // Display all records in the file
    afx_msg void DisplayData( int n )  ; 
    afx_msg void OnAdd( int n ) ;       // Add a new record to the file
    afx_msg void OnUpdate( int n ) ;    // Get data to update a record 
    afx_msg void OnDelete( int n ) ;    // Delete a record from the file
    afx_msg void OnRefresh() ;
	                // Update data in the file
    afx_msg void UpdateData( long n, AddressStruct ) ;  
    afx_msg void InitData() ;            // init file with records
    
	                             // write record to file
    afx_msg void WriteData( AddressStruct  &addStruct ); 
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
	//}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
  

Creating the Add Record Dialog and Class

To insert a new dialog into the Address Book application, we will go to Workspace pane (under the tool bar)... Select the Resource tab... Right click on the Dialog tab... select Insert Dialog.

  1. On Dialog1 title bar, right click... select properties.
  2. In the Properties dialog
    • Change the ID from IDD_DIALOG to IDD_ADDRECORD.
    • Change the caption from Dialog to Add Record.
  3. Next we go to the menu bar... select Insert... select New Class. We will enter
    • Select Form Class, in the Class Type
    • AddRecord in the Name field
    • CDialog in the Base Class field
    • Click OK




  4. We will add text edit boxes and static text to the Add Record dialog to get:



  5. Next we will right click on the Add Record dialog and select the ClassWizard.
  6. We will add variables to the controls to get:
          Control IDs    
          Type    
          Member    
    IDC_ADD CButton m_bOK
    IDC_ADDRESS CString m_sAddress
    IDC_CITY CString m_sCity
    IDC_EMAIL CString m_sEmail
    IDC_FAX CString m_sFax
    IDC_FNAME CString m_sFName
    IDC_LNAME CString m_sLName
    IDC_STATE CString m_sState
    IDC_TELEPHONE CString m_sTele
    IDC_ZIP CString m_sZip
    IDCANCEL CButton m_bCancel


  7. We will add the following code the BEGIN_MESSAGE_MAP(... ) code.
      
    ON_COMMAND( IDC_ADD , OnAdd )
      

  8. We will add the following OnAdd event handler code :
      
    afx_msg void AddRecord::OnAdd() {
       CAddressBook_4Dlg *addDlg = new CAddressBook_4Dlg ;
    
       UpdateData( TRUE ) ;
    
       if ( m_sFName != "" && m_sLName != "" 
               && m_sTele != "" &&
    	   m_sAddress != "" ) {
    
    	  addDlg-> Add( m_sFName , m_sLName ,  
    		        m_sAddress ,  m_sCity , 
    		        m_sState , m_sZip , m_sTele ,
    			m_sFax ,  m_sEmail   ) ;
          this->OnOK();
       }
    }
      

Creating the Update Record Dialog and Class

Creating the UpdateRecord Dialog and Class will be similar to the steps we used in Creating the AddRecord Dialog and Class. We will perform the following steps:

  1. Go to the Workspace pane (left window under the tool bar)... select Resource Tab... right-click on Dialog... select Insert dialog.
  2. Right-click on the dialog title bar... Select properties... on the Dialog Properties dialog,
    1. Change the ID from IDD_Dialog1 to IDD_UPDATEREC.
    2. Change the caption from Dialog to Update Record.
  3. Go to the MS Visual C++ menu bar... select Insert... select New Class.
  4. The information entered on this form will look like:



  5. Next we will add static text labels and edit boxes until the dialog looks like:



  6. We now right click on the Update Record dialog resource... select classWizard... Select Member Variables.
  7. Add variables to the Control IDs as follows:
        Control IDs      
        Type      
        Member      
    IDCANCEL CButton m_bCancel
    IDU_ADDRESS CString m_sAddress
    IDU_City CString m_sCity
    IDU_EMAIL CString m_sEmail
    IDU_EXIT CButton m_bOk
    IDU_FAX CString m_sFax
    IDU_FNAME CString m_sFName
    IDU_GETREC CButton m_bGetRecID
    IDU_LNAME< CString m_sLName
    IDU_RECID int m_iRecID
    IDU_STATE CString m_sState
    IDU_TELEPHONE CString m_sTelephone
    IDU_ZIP CString m_sZip
  8. The final Update Record class will look like:
      
    /////////////////////////////////////////////////////////////////////////////
    // UpdateRec dialog
    
    class UpdateRec : public CDialog
    {
    // Construction
    public:
    	UpdateRec(CWnd* pParent = NULL);   // standard constructor
    	afx_msg void OnGetRecID() ;        // Get the record
    	afx_msg void OnExit() ;
    
    // Dialog Data
    	//{{AFX_DATA(UpdateRec)
    	enum { IDD = IDD_UPDATEREC };
    	CButton	m_bGetRecID;
    	CButton	m_bOk;
    	CButton	m_bCancel;
    	CString	m_sAddress;
    	CString	m_sCity;
    	CString	m_sEmail;
    	CString	m_sFax;
    	CString	m_sFName;
    	CString	m_sLName;
    	CString	m_sState;
    	CString	m_sTele;
    	CString	m_sZip;
    	int		m_iRecID;
    	//}}AFX_DATA
    
    
    // Overrides
    	// ClassWizard generated virtual function overrides
    	//{{AFX_VIRTUAL(UpdateRec)
    	protected:
    	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    	//}}AFX_VIRTUAL
    
       	struct AddressStruct  {
           int recordID ;
           char firstName[30];
           char lastName[30];
           char address[30];
           char city[30];
           char state[20];
           char zip[15];
           char telephone[15];
           char fax[15];
           char email[30];
        };
    
    // Implementation
    protected:
    
    	// Generated message map functions
    	//{{AFX_MSG(UpdateRec)
    		// NOTE: the ClassWizard will add member functions here
    	//}}AFX_MSG
    	DECLARE_MESSAGE_MAP()
    };
      

  9. In the OnGetRecID() method, we will
    1. Get record ID of the record to be updated
    2. Call ReadData with ID and structure address
    3. Copy variables from structure to dialog variables
    4. Update dialog edit boxes
  10. In the OnExit() method, we will
    1. Get data from the Update Record Dialog
    2. Call UpDateRec with the data the user entered
    3. Close dialog

Creating the Delete Record Dialog and Class

In Creating the Delete Record Dialog and Class, we follow steps similar to those that we performed in Creating the Add Record Dialog and Class and Creating the Update Record Dialog and Class.

  1. Go to the Workspace pane (left window under the tool bar)... select Resource Tab... right-click on Dialog... select Insert dialog.
  2. Right-click on the dialog title bar... Select properties... on the Dialog Properties dialog,
    1. Change the ID from IDD_Dialog1 to IDD_DELETEREC.
    2. Change the caption from Dialog to Delete Record.
  3. Next we go to the menu bar... select Insert... select New Class. We will enter
    • Select Form Class, in the Class Type
    • DeleteRec in the Name field
    • CDialog in the Base Class field
    • Click OK




  4. Next we will add static text labels and edit boxes until the dialog looks like:



  5. We now right click on the Delete Record dialog resource... select classWizard... Select Member Variables.
  6. Add variables to the Control IDs as follows:
        Control IDs      
        Type      
        Member      
    IDC_RECORDID int m_bRecordID
    IDCANCEL CButton m_bCancel
    IDD_DELETE CButton m_bCancel
    IDD_DELETE CButton m_bOk
  7. We will add the following code the BEGIN_MESSAGE_MAP(... ) code.
      
    ON_COMMAND( IDD_DELETE , OnDelete )
      

  8. We will add the following OnDelete event handler code :
      
    afx_msg void DeleteRec::OnDelete()
    {
       CAddressBook_4Dlg *addDlg = new CAddressBook_4Dlg ;
    
       UpdateData( TRUE ) ;
    
       
       if ( m_iRecordID >= 0  ) {
    
          addDlg->Delete( m_iRecordID ) ;
          this->OnOK();
       }
    
    }
      

  9. The final Delete Record class will look like:
      
    /////////////////////////////////////////////////////////////////////////////
    // DeleteRec dialog
    
    class DeleteRec : public CDialog
    {
    // Construction
    public:
    	DeleteRec(CWnd* pParent = NULL);   // standard constructor
    	void DeleteRec::OnDelete() ;
    
    // Dialog Data
    	//{{AFX_DATA(DeleteRec)
    	enum { IDD = IDD_DELETEREC };
    	CButton	m_bOk;
    	CButton	m_bCancel;
    	int		m_iRecordID;
    	//}}AFX_DATA
    
    
    // Overrides
    	// ClassWizard generated virtual function overrides
    	//{{AFX_VIRTUAL(DeleteRec)
    	protected:
    	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    	//}}AFX_VIRTUAL
    
    // Implementation
    protected:
    
    	// Generated message map functions
    	//{{AFX_MSG(DeleteRec)
    		// NOTE: the ClassWizard will add member functions here
    	//}}AFX_MSG
    	DECLARE_MESSAGE_MAP()
    };
      

  10. In the OnDelete() method:
    1. Create a CAddressBook_4Dlg instance
    2. Get the record ID from the Delete Record Dialog
    3. Call the CAddressBook_4Dlg class' Delete method and pass the record ID to be deleted
    4. close the Delete Record dialog

File Streams

You must include header files <iostream> and <fstream> if you want to perform C++ file processing. In the following code snippet, <ifstream> opens a file for reading

  
ifstream infile;                           // make stream
infile.open("Addressbook.dat", ios::binary);
  

and <ofstream> opens a file for writing.
  
ofstream onfile;                           // make stream
onfile.open("Addressbook.dat", ios::app | ios::binary);
  

The following chart shows a few modes that might be used in accessing a file.

Mode Description
ios::ate Open a file for output and move the file pointer to the end of the file. Data can be written anywhere in the file.
ios::app Open a file for output and write all data at the end of the file.
ios::out Open a file for output.
ios::in Open a file for input.
ios::binary Open a file for binary (i.e., non-text) input or output.
ios::trunc Erase the file's contents if it exists.
ios::end Set the file pointer at the end of the file.
ios::beg Set the file pointer at the beginning of the file.


If you want to calculate the number of records in the file, you might create an algorithm like the following:
  
   ifstream infile;
   infile.open("Addressbook.dat", ios::binary);
   infile.seekg(0, ios::end);    // go to 0 bytes from end
                                 // calculate number of address records
   return (int)infile.tellg() / sizeof( AddressBook );
  

Final Class Definition

The final class definition looks like:

  
// CAddressBook_4Dlg dialog

class CAddressBook_4Dlg : public CDialog
{
// Construction
public:
	CAddressBook_4Dlg(CWnd* pParent = NULL);	// standard constructor
	 
	void Add( CString m_sFName , CString m_sLName , 		
              CString m_sAddress , CString  m_sCity  ,
			  CString m_sState , CString m_sZip , 		
              CString m_sTele , CString  m_sFax  ,
              CString m_sEmail   ) ; 
	void UpDateRec( int m_iRecID , CString m_sFName , CString m_sLName , 		
              CString m_sAddress , CString  m_sCity  ,
			  CString m_sState , CString m_sZip , 		
              CString m_sTele , CString  m_sFax  ,
              CString m_sEmail   ) ;
	void Delete( int  m_iRecordID ) ;

	struct AddressStruct  {
       int recordID ;
       char firstName[30];
       char lastName[30];
       char address[30];
       char city[30];
       char state[20];
       char zip[15];
       char telephone[15];
       char fax[15];
       char email[30];
    };

	
	void ReadData(int, AddressStruct &);          // read record from file

// Dialog Data
	//{{AFX_DATA(CAddressBook_4Dlg)
	enum { IDD = IDD_ADDRESSBOOK_4_DIALOG };
	CString	m_lAddress;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAddressBook_4Dlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:

	HICON m_hIcon;
    CListBox *pAddresses;

    // Generated message map functions
    //{{AFX_MSG(CAddressBook_4Dlg)
    virtual BOOL OnInitDialog();
	           
    afx_msg static int RecordCount();    // return number of records in file
	                // Display all records in the file
    afx_msg void DisplayData( int n )  ; 
    afx_msg void OnAdd( int n ) ;       // Add a new record to the file
    afx_msg void OnUpdate( int n ) ;    // Get data to update a record 
    afx_msg void OnDelete( int n ) ;    // Delete a record from the file
    afx_msg void OnRefresh() ;
	                // Update data in the file
    afx_msg void UpdateData( long n, AddressStruct ) ;  
    afx_msg void InitData() ;            // init file with records
    
	                             // write record to file
    afx_msg void WriteData( AddressStruct  &addStruct ); 
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
  

The above class definition will be used to implement the addressbook.cpp code.

Conclusions

I used MS Visual Studio C++ to compile and execute this example. With this tutorial and source, you should have the necessary information to build this Address Book example. Enjoy and have fun with it.

Definitions

Comment - comments help document a program but do cause the computer to perform any action. They are ignored when the program executes. They are of the form:

Function - a named section of a program that performs the specific task included between the { and the }.

<iostream.h> - to declare objects that control reading from and writing to the standard streams. This is often the only header you need include to perform input and output from a C++ program.

persistent data - is data that exists after the exit of an application or the turning off of the computer. It exists on a media storage device (e.g., hard disk).




HOME] Consulting Design Maintenance Project Testing Training Turnkey Java C++ SQL HTML JavaScript ]

© 2002 - 2016 All Rights Reserved Total Application Works