Filling a List Box from a Data Structure in Visual C++

Microsoft Corporation

September 1996

Abstract

A common implementation task is creating a list box within a dialog box. The Microsoft® Foundation Class Library (MFC) dialog data exchange (DDX) automates the moving of data between simple dialog box controls (such as edit boxes) and program data structures. However, moving data between more complex dialog box controls (such as a list box) and program data structures requires a little hand coding on your part.

This article describes a technique for initializing a list box from a data structure in Visual C++®.

Four Simple Steps

The technique of filling a list box requires the following four steps:

  1. Use the New command (File menu) to create a project.

  2. Use the dialog box editor to draw a dialog box containing a list box.

  3. Use ClassWizard to add a class describing the new dialog box. In this example, the class will use a CStringList to hold strings for display in the list box.

  4. Use ClassWizard to add code to the dialog box's OnInitDialog method that fills the list box.

The rest of this article walks you, step by step, through a simple programming project that illustrates the process outlined in the previous steps.

Example Project

Note   This project assumes knowledge of the Visual C++® project facilities such as the menu and dialog editors, ClassView, ResourceView, AppWizard, and ClassWizard. It also assumes knowledge of C++ and the Microsoft® Foundation Class Library. All of the steps and code required to finish the project are provided. Remember that pressing F1 provides help on the tool that has the current focus.

In this example, you'll use AppWizard to create a multi-document project. After creating the project, you'll add one menu and two dialog boxes. The menu will provide entry points to the dialog boxes. One dialog box accepts strings and one displays them. Strings entered by the user are stored in the context of the currently open document. The flow of data is from the first dialog box's object, to the current document's object, and, finally, to the second dialog box's object.

Note that this project adds steps—adding a menu and an extra dialog box—to the four mentioned earlier. These extra steps are required to provide data for the list box. The four initial steps are meant as a springboard into your own project. If you want hands-on experience before jumping into your own project, perform the steps below.

The following list shows the steps in this example project:

  1. Create a new project.

  2. Draw the menu.

  3. Create the Enter Name dialog box.

  4. Create the Retrieve Names dialog box.

  5. Connect the dialog boxes to the menu.

  6. Add a CStringList to CRetrieveNamesDlg.

  7. Add code to OnInitDialog that fills the list box.

1. Create a New Project

Begin by using AppWizard to create a project to work in.

  1. Start Microsoft Developer Studio.

  2. Choose New (on the File menu) and select Project Workspace from the New dialog box.

  3. Click OK.

  4. Name the project "Name List," accept the default Project Type (MFC AppWizard (exe)), and click the Create button when you're finished with the New Project Workspace dialog box.

  5. Click the Finish button when presented with the MFC AppWizard - Step 1 dialog box.

  6. Click the OK button from New Project Information dialog box.

2. Draw the Menu

Next, add a menu called "Names" to the default set of menus.

  1. From ResourceView, double-click the Menu folder.

  2. Double-click IDR_NAMELITYPE to edit the associated menu.

  3. Add a "&Names" menu between the View and Windows menus.

  4. Double-click the "new items" box just below the new "&Names" menu and type "&Enter Name" in the Caption field of the Menu Items Properties page. Accept the generated ID of ID_NAMES_ENTERNAME.

  5. Double-click the "new items" box just below the new "&Names" menu and type "&Retrieve Names" in the Caption field. Accept the generated ID of ID_NAMES_RETRIEVENAMES.

  6. Save your work and close the menu editor.

3. Create the Enter Name Dialog Box

In this section you will create a dialog box that has a list box into which a user can enter data.

  1. From ResourceView, right-click the Dialogs folder and choose the Insert Dialog command. The user will use the dialog box you're about to create to enter names into a list.

  2. In the resource-editor window, double-click on the body of the dialog box to bring up the Dialog Properties. Enter an ID of IDD_ENTERNAME and a caption of "Enter Name" on the Dialog property page.

  3. Add the following controls to the dialog box:
    Type Location Name
    Static text Upper-left corner IDC_STATIC
    Edit box Below static text IDC_ENTERNAME

  4. Click Tab Order (Layout menu). Click on the edit box to ensure that it receives the focus when the dialog box comes up at run time.

    Next, add a class for the Enter Name dialog box.

  5. With the focus on the dialog editor, start ClassWizard (choose ClassWizard from the View menu) and create a new class with the following attributes:
    Attribute Value
    Class Name CEnterNameDlg
    Class Type Cdialog
    Dialog ID IDD_ENTERNAME

  6. Click the Create button. ClassWizard will generate the declaration and implementation of class CEnterNameDlg.

    Next, add variables to the Enter Name dialog box class.

  7. From ClassWizard, select the Member Variables tab and select CEnterNameDlg from the Class name drop-down list. Double-click the IDC_ENTERNAME Control ID to add a variable with the following attributes:
    Attribute Value
    Member Variable Name m_csName
    Category Value
    Variable Type Cstring

  8. When finished, click the dialog box's OK button and then ClassWizard's OK button.

4. Create the Retrieve Names Dialog Box

In this section you will create a second dialog box. This one will retrieve the data a user enters into the other dialog box. First, draw the new dialog box.

  1. From ResourceView, right click the Dialogs folder and choose the Insert Dialog Command.

  2. In the dialog editor window, double-click on the body of the dialog box to bring up the Dialog Properties. Enter an ID of IDD_RETRIEVENAME and a caption of "Retrieve Name."

  3. Drag a static text field from the Controls toolbar to the upper-left corner of the dialog box. While the focus is still on the static text field, type "Last Name Entered."

  4. Drag an edit box from the Controls toolbar below the Last Name Entered static text. Double-click the edit box to bring up the associated property page and enter an ID of IDC_RETRIEVELASTNAME.

  5. On the property page select the Styles tab and check the Read-Only property to make the edit box read-only. We'll use this text box to illustrate the ease that the MFC dialog data exchange (DDX) mechanism brings to initializing simple controls. ClassWizard writes most of the code for you.

  6. Drag a static text field from the Controls toolbar to just under the read-only text box. While the focus is still on the static text field, type "Retrieved Names".

  7. Drag a list box from the Controls toolbar and drop it just under the "Retrieved Names" static text. Double-click the list box to bring up the associated property page and enter an ID of IDC_RETRIEVENAMES.

  8. Select the Tab Order command from the Layout menu and set the desired tab order.

    Next, add a class for the Retrieve Names dialog box.

  9. With the focus on the dialog editor, start ClassWizard and create a new class with the following attributes:
    Attribute Value
    Class Name CRetrieveNamesDlg
    Class Type Cdialog
    Dialog ID IDD_RETRIEVENAMES

  10. Click the Create button. ClassWizard will generate the declaration and implementation of the CRetrieveNamesDlg class.

To conclude this section, add variables to the Retrieve Names dialog box class.

CRetrieveNamesDlg requires two variables: one for the IDC_RETRIEVELASTNAME control ID and one for the IDC_RETRIEVENAMES control ID.

  1. Start ClassWizard, select the Member Variables tab, and select CRetrieveNamesDlg from the Class Name list box.

  2. Double-click the IDC_RETRIEVELASTNAME control ID and add a variable with the following attributes:
    Attribute Value
    Member Variable Name m_csLastNameEntered
    Category Value
    Variable Type Cstring

  3. Next, double-click the IDC_RETRIEVENAMES control ID and add a variable with the following attributes:
    Attribute Value
    Member Variable
    Name m_csNames
    Category Value
    Variable Type CString

  4. When finished, click the dialog box's OK button and then ClassWizard's OK button.

  5. Click ClassWizard's OK button and save your work.

The next task will be to connect the two dialog boxes to the Names menu.

5. Connect the Dialog Boxes to the Menu

Objects of type CEnterNameDlg are launched when the user clicks Enter Name (Name menu). CEnterNameDlg accepts strings from the user. User-entered strings are stored in a CStringList in the current document.

  1. Start ClassWizard, select the Message Maps tab, and select CNameListDoc from the Class Name list box.

  2. Select the ID_NAMES_ENTERNAME Object ID and double-click the COMMAND Message to add a message handler. Click OK to accept OnNameEntername as the function name, then click the Edit Code button.

  3. Add the code below marked with ">". Don't forget the #include statement.
    #include "stdafx.h"
    #include "Name List.h" 
    
    >#include "EnterNameDlg.h" 
    
    #include "Name ListDoc.h" 
    .
    .
    .
    void CNameListDoc::OnNameEntername()
    {
    
    > CEnterNameDlg dlg; 
    
    > if (dlg.DoModal() == IDOK) 
    
    > // MFC dialog data exchange returns the user's entry
    
    > // into dlg.m_csName. Add dlg.m_csName to the end of
    
    > // the document's CStringList: 
    
    > m_csDocumentNameList.AddTail(dlg.m_csName); 
    
    }
    

This code creates a CEnterNameDlg object when the user selects the Enter Name command from the Name menu. When the user clicks the OK button in the Enter Name dialog, the content of the edit box, dlg.m_csName, is added to the tail of a list.

This code also assumes a CStringList named m_csDocumentNameList in class CNameListDoc. The application's flow of data is from CEnterNameDlg to CNameListDoc to CRetrieveNamesDlg.

To add the CStringList to the document view, open CNameListDoc from ClassView and add the code below marked with ">".

class CNameListDoc : public Cdocument 
{
protected: // create from serialization only
CNameListDoc();
DECLARE_DYNCREATE(CNameListDoc) 
// Attributes
public: 

>// Attributes

>protected: 

> // Names entered and retrieved from Name menu

> CStringList m_csDocumentNameList; 

This code makes use of the MFC CStringList container class to hold multiple user entries. Later, you'll add the appropriate variables to class CRetrieveNamesDlg in order to complete the data flow.

Next, connect the Retrieve Names dialog box to the Names menu.

  1. Start ClassWizard, select the Message Map tab, and select CNameListDoc from the Class Name list box.

  2. Double-click the ID_NAMES_RETRIEVENAMES Object ID and double-click the COMMAND Message to add a message handler. Click OK to accept OnNameRetrievenames as the function name, then click the Edit Code button.

  3. Add the code below marked with ">". Don't forget the #include statement.
    #include "stdafx.h" 
    #include "Name List.h" 
    #include "EnterNameDlg.h" 
    
    >#include "RetrieveNamesDlg.h" 
    
    #include "Name ListDoc.h". 
    . 
    . 
    . 
    void CNameListDoc::OnNameRetrievenames()
    {
    
    > CRetrieveNamesDlg dlg; 
    
    >// At this point, the DoDataExchange call to DDX_Text that is
    
    >// associated with CNameListDoc::OnNamesRetrievenames
    
    >// automatically performs the required UpdateData call to fill
    
    >// the IDC_RETRIEVELASTNAME text box with user-entered data. 
    
    > dlg.m_csLastNameEntered = m_csDocumentNameList.GetTail(); 
    
    > 
    
    >// Assign a pointer to the document's CStringList to
    
    >// the dialog box object's CstringList. 
    
    > dlg.m_pcsNameList = &m_csDocumentNameList; 
    
    > if (dlg.DoModal() == IDOK) 
    
    > {
    
    > }
    
    } 
    

This code creates a CRetrieveNamesDlg object when the user clicks Retrieve Name (on the Name menu). The object's data members are initialized to contain the document's data. After the call to DoModal, DDX automatically fills the IDC_RETRIEVELASTNAME text box with user-entered data.

The final two steps in this exercise are to add a CStringList to the CRetrieveNamesDlg class and to add some code to CRetrieveNamesDlg::OnInitDialog, which fills the dialog box's list box from the CstringList.

6. Add a CStringList to CRetrieveNamesDlg

One important data structure is missing from the CRetrieveNamesDlg class: a CStringList to contain user-entered data. Open CRetrieveNamesDlg from ClassView and add the code below marked with ">".

class CRetrieveNamesDlg : public Cdialog 
{
// Construction
public: 
CRetrieveNamesDlg(CWnd* pParent = NULL); // standard constructor 
// Dialog Data
//{{AFX_DATA(CRetrieveNamesDlg) 
enum { IDD = IDD_RETRIEVENAMES };
CString m_csNames; 
CString m_csLastNameEntered; 
//}}AFX_DATA

>CStringList* m_pcsNameList; 

This added CStringList allows you to assign the user-entered data stored in a CNameListDoc object to a CRetrieveNamesDlg object. In the final step you will add code to CRetrieveNamesDlg::OnInitDialog, which fills the dialog box's list box from the CStringList.

7. Add Code to OnInitDialog, Which Fills the ListBox

  1. Start ClassWizard, select the Message Map tab, and select CRetrieveNamesDlg from the Class Name list box.

  2. With the CRetrieveNamesDlg Object ID selected, double-click WM_INITDIALOG in the Messages list to add a message handler. Click the Edit Code button and add the code below marked with ">".
    BOOL CRetrieveNamesDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();
    
    > CListBox* pLB = (CListBox*) GetDlgItem(IDC_RETRIEVENAMES); 
    
    > POSITION p = m_pcsNameList->GetHeadPosition();
    
    > while(p != NULL ) 
    
    > {
    
    > pLB->InsertString(-1, m_pcsNameList->GetNext(p)); 
    
    > }
    
    return TRUE; // Return TRUE unless you set the focus to a control.
    // EXCEPTION: OCX Property Pages should return FALSE.
    }
    

This code creates a pointer to a ListBox, associates it with the dialog box's list box, and uses the member functions of a CStringList to fill the list box. The data transfer is complete. Build and run the application.