[Previous] [Next]

The Common Dialogs

Some dialog boxes appear so frequently in application programs that they have rightfully taken their places as part of the operating system. Before Windows 3.1, programmers had to write their own Open and Save As dialog boxes to get a file name from the user before opening or saving a file. Because both the design and the implementation of these dialog boxes were left up to the programmer, every Open and Save As dialog box was different, and some were far inferior to others. Windows 3.1 fixed this long-standing problem by providing standard implementations of these and other commonly used dialog boxes in a DLL known as the common dialog library. Windows 95 enhanced the library with improved versions of the Windows 3.1 common dialogs and a new Page Setup dialog box for entering page layouts. Windows 98 and Windows 2000 further refine the common dialogs to make them more functional than ever.

MFC provides C++ interfaces to the common dialogs with the classes shown in the following table.

The Common Dialog Classes

Class Dialog Type(s)
CFileDialog Open and Save As dialog boxes
CPrintDialog Print and Print Setup dialog boxes
CPageSetupDialog Page Setup dialog boxes
CFindReplaceDialog Find and Replace dialog boxes
CColorDialog Color dialog boxes
CFontDialog Font dialog boxes

In an SDK-style application, a common dialog is invoked by filling in the fields of a data structure and calling an API function such as ::GetOpenFileName. When the function returns, certain fields of the data structure contain values input by the user. MFC simplifies the interface by providing default input values for most fields and member functions for retrieving data entered into the dialog box. In an MFC application, getting a file name from the user before opening a file is normally no more complicated than this:

TCHAR szFilters[] =
    _T ("Text files (*.txt)¦*.txt¦All files (*.*)¦*.*¦¦");

CFileDialog dlg (TRUE, _T ("txt"), _T ("*.txt"),
    OFN_FILEMUSTEXIST ¦ OFN_HIDEREADONLY, szFilters);

if (dlg.DoModal () == IDOK) {
    filename = dlg.GetPathName ();
    // Open the file and read it.
        
}

The TRUE parameter passed to CFileDialog's constructor tells MFC to display an Open dialog box rather than a Save As dialog box. The "txt" and "*.txt" parameters specify the default file name extension—the extension that is appended to the file name if the user doesn't enter an extension—and the text that initially appears in the dialog's File Name box. The OFN values are bit flags that specify the dialog's properties. OFN_FILEMUSTEXIST tells the dialog to test the file name the user enters and reject it if the file doesn't exist, and OFN_HIDEREADONLY hides the read-only check box that appears in the dialog box by default. szFilters points to a string specifying the file types the user can select from. When DoModal returns, the file name that the user entered, complete with path name, can be retrieved with CFileDialog::GetPathName. Other useful CFileDialog functions include GetFileName, which retrieves a file name without the path, and GetFileTitle, which retrieves a file name with neither path nor extension.

Generally, you'll find that MFC's common dialog classes are exceptionally easy to use, in part because you can often instantiate a common dialog class directly and avoid deriving classes of your own.

Modifying the Common Dialogs

You can modify the behavior of CFileDialog and other common dialog classes in a number of ways. One method involves nothing more than changing the parameters passed to the dialog's constructor. For example, CFileDialog::CFileDialog's fourth parameter accepts about two dozen different bit flags affecting the dialog's appearance and behavior. One use for these flags is to create an Open dialog box that features a multiple-selection list box in which the user can select several files instead of just one. Rather than construct the dialog like this,

CFileDialog dlg (TRUE, _T ("txt"), _T ("*.txt"),
    OFN_FILEMUSTEXIST ¦ OFN_HIDEREADONLY, szFilters);

you would do it like this:

CFileDialog dlg (TRUE, _T ("txt"), _T ("*.txt"),
    OFN_FILEMUSTEXIST ¦ OFN_HIDEREADONLY ¦ OFN_ALLOWMULTISELECT,
    szFilters);

After DoModal returns, a list of file names is stored in the buffer referenced by the dialog object's m_ofn.lpstrFile data member. The file names are easily retrieved from the buffer with CFileDialog's GetStartPosition and GetNextPathName functions.

When you construct a dialog box from CFileDialog, the class constructor fills in the fields of an OPENFILENAME structure with values defining the title for the dialog window, the initial directory, and other parameters. The structure's address is subsequently passed to ::GetOpenFileName or ::GetSaveFileName. Some of the values used to initialize the structure are taken from CFileDialog's constructor parameter list, but other parameters are filled with default values appropriate for the majority of applications. Another way to customize an Open or a Save As dialog box is to modify the fields of the OPENFILENAME structure after constructing the dialog object but before calling DoModal. The OPENFILENAME structure is accessible through the public data member m_ofn.

Suppose you'd like to change the title of a multiple-selection file dialog to "Select File(s)" instead of "Open." In addition, you'd like the file name filter that was selected when the dialog box was closed to be selected again the next time the dialog box is displayed. Here's how you could make these changes:

CFileDialog dlg (TRUE, _T ("txt"), NULL,
    OFN_FILEMUSTEXIST ¦ OFN_ALLOWMULTISELECT,
    szFilters);

dlg.m_ofn.nFilterIndex = m_nFilterIndex;
static char szTitle[] = _T ("Select File(s)");
dlg.m_ofn.lpstrTitle = szTitle;

if (dlg.DoModal () == IDOK) {
    m_nFilterIndex = dlg.m_ofn.nFilterIndex;
        
}

When the program is started, m_nFilterIndex should be set to 1. The first time the dialog box is created, the first file filter will be selected by default. When the user dismisses the dialog box with the OK button, the index of the currently selected filter is copied out of the OPENFILENAME structure and saved in m_nFilterIndex. The next time the dialog box is invoked, the same filter will be selected automatically. In other words, the dialog box will remember the user's filter selection. For a more thorough encapsulation, you could make m_nFilterIndex a part of the dialog box rather than a member of an external class by deriving your own dialog class from CFileDialog, declaring m_nFilterIndex to be a static member variable of that class, and initializing it to 1 before constructing a CMyFileDialog object for the first time.

You can implement more extensive changes by deriving your own dialog class from CFileDialog and overriding key virtual functions. In addition to OnOK and OnCancel, you can override the virtual functions OnFileNameOK, OnLBSelChangedNotify, and OnShareViolation to customize the way the dialog box validates file names, responds to changes in file name selections, and handles sharing violations. You can override OnInitDialog to perform all sorts of stunts, such as increasing the size of the dialog box and adding or deleting controls. (If you override CFileDialog::OnInitDialog, be sure to call the base class version from your own implementation.) You could, for example, stretch the dialog box horizontally and create a preview area that displays a thumbnail sketch of the contents of the currently selected file. By overriding OnLBSelChangedNotify, you could update the preview window when the selection changes.

The Phones Application

This chapter's final application, Phones, brings together into one project many of the concepts discussed in this chapter and in Chapter 7. As you can see in Figure 8-13, Phones is a simple phone list program that stores names and phone numbers. Names and phone numbers are entered and edited in a modal dialog box that features a standard edit control for names, a numeric edit control for phone numbers, and icon push buttons. Data entered into the application can be saved to disk and read back using the File menu's Open, Save, and Save As commands. Phones uses CFileDialog to solicit file names from the user and CStdioFile to perform its file I/O. It also uses the derived list box class CPhonesListBox as the base class for CChildView, and it uses message reflection in that class to allow the list box to respond to its own double-click notifications. I hand-edited the AppWizard-generated CChildView class to change the base class from CWnd to CPhonesListBox. Pertinent portions of the application's source code are shown in Figure 8-14.

Click to view at full size.

Figure 8-13. The Phones window and dialog box.

Figure 8-14. The Phones application.

MainFrm.h

// MainFrm.h : interface of the CMainFrame class
//
///////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MAINFRM_H__7BE4B248_90ED_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_MAINFRM_H__7BE4B248_90ED_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "ChildView.h"

class CMainFrame : public CFrameWnd
{
    
public:
    CMainFrame();
protected: 
    DECLARE_DYNAMIC(CMainFrame)
// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMainFrame)
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, 
        AFX_CMDHANDLERINFO* pHandlerInfo);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CMainFrame();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
    CChildView    m_wndView;

// Generated message map functions
protected:
    //{{AFX_MSG(CMainFrame)
    afx_msg void OnSetFocus(CWnd *pOldWnd);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

///////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
// !defined(AFX_MAINFRM_H__7BE4B248_90ED_11D2_8E53_006008A82731__INCLUDED_)

MainFrm.cpp

// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "Phones.h"
#include "PhonesListBox.h"
#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_SETFOCUS()
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
}

CMainFrame::~CMainFrame()
{
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if( !CFrameWnd::PreCreateWindow(cs) )
        return FALSE;
    cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
    cs.lpszClass = AfxRegisterWndClass(0);
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
    CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
    CFrameWnd::Dump(dc);
}

#endif //_DEBUG

///////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
void CMainFrame::OnSetFocus(CWnd* pOldWnd)
{
    // forward focus to the view window
    m_wndView.SetFocus();
}

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,
    AFX_CMDHANDLERINFO* pHandlerInfo)
{
    // let the view have first crack at the command
    if (m_wndView.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
        return TRUE;

    // otherwise, do default handling
    return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    if (!m_wndView.Create(WS_CHILD ¦ WS_VISIBLE ¦ LBS_USETABSTOPS ¦
        LBS_SORT ¦ LBS_NOTIFY ¦ LBS_NOINTEGRALHEIGHT, CRect(0, 0, 0, 0),
        this, AFX_IDW_PANE_FIRST))
        return -1;

    return 0;
}

ChildView.h

// ChildView.h : interface of the CChildView class
//
///////////////////////////////////////////////////////////////////////////

#if !defined(AFX_CHILDVIEW_H__7BE4B24A_90ED_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_CHILDVIEW_H__7BE4B24A_90ED_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

///////////////////////////////////////////////////////////////////////////
// CChildView window

class CChildView : public CPhonesListBox
{
// Construction
public:
    CChildView();

// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CChildView)
    protected:
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CChildView();

    // Generated message map functions
protected:
    BOOL SaveFile (LPCTSTR pszFile);
    BOOL LoadFile (LPCTSTR pszFile);
    static const TCHAR m_szFilters[];
    CString m_strPathName;
    //{{AFX_MSG(CChildView)
    afx_msg void OnNewEntry();
    afx_msg void OnFileOpen();
    afx_msg void OnFileSave();
    afx_msg void OnFileSaveAs();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

///////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
// !defined(
//    AFX_CHILDVIEW_H__7BE4B24A_90ED_11D2_8E53_006008A82731__INCLUDED_)

ChildView.cpp

// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "Phones.h"
#include "PhonesListBox.h"
#include "ChildView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// CChildView

const TCHAR CChildView::m_szFilters[] =
    _T ("Phone Files (*.phn)¦*.phn¦All Files (*.*)¦*.*¦¦");

CChildView::CChildView()
{
}

CChildView::~CChildView()
{
}

BEGIN_MESSAGE_MAP(CChildView, CPhonesListBox)
    //{{AFX_MSG_MAP(CChildView)
    ON_COMMAND(ID_FILE_NEW, OnNewEntry)
    ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
    ON_COMMAND(ID_FILE_SAVE, OnFileSave)
    ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CChildView message handlers

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
    if (!CPhonesListBox::PreCreateWindow(cs))
        return FALSE;

    cs.dwExStyle ¦= WS_EX_CLIENTEDGE;
    cs.style &= ~WS_BORDER;
    return TRUE;
}

void CChildView::OnNewEntry() 
{
    NewEntry ();    
}

void CChildView::OnFileOpen() 
{
    CFileDialog dlg (TRUE, _T ("phn"), _T ("*.phn"),
        OFN_FILEMUSTEXIST ¦ OFN_HIDEREADONLY, m_szFilters);

    if (dlg.DoModal () == IDOK) {
        if (LoadFile (dlg.GetPathName ())) {
            m_strPathName = dlg.GetPathName ();
            SetCurSel (0);
        }
    }
}

void CChildView::OnFileSave() 
{
    if (!m_strPathName.IsEmpty ())
        SaveFile (m_strPathName);
    else // Need a file name first.
        OnFileSaveAs ();
}

void CChildView::OnFileSaveAs() 
{
    CFileDialog dlg (FALSE, _T ("phn"), m_strPathName,
        OFN_OVERWRITEPROMPT ¦ OFN_PATHMUSTEXIST ¦ OFN_HIDEREADONLY,
        m_szFilters);

    if (dlg.DoModal () == IDOK)
        if (SaveFile (dlg.GetPathName ()))
            m_strPathName = dlg.GetPathName ();
}

BOOL CChildView::LoadFile(LPCTSTR pszFile)
{
    BOOL bResult = FALSE;

    try {
        CStdioFile file (pszFile, CFile::modeRead);
        ResetContent ();
        DWORD dwCount;
        file.Read (&dwCount, sizeof (dwCount));
        if (dwCount) {
            for (int i=0; i<(int) dwCount; i++) {
                CString string;
                file.ReadString (string);
                AddString (string);
            }
        }
        bResult = TRUE;
    }
    catch (CFileException* e) {
        e->ReportError ();
        e->Delete ();
    }
    return bResult;
}

BOOL CChildView::SaveFile(LPCTSTR pszFile)
{
    BOOL bResult = FALSE;

    try {
        CStdioFile file (pszFile, CFile::modeWrite ¦ CFile::modeCreate);
        DWORD dwCount = GetCount ();
        file.Write (&dwCount, sizeof (dwCount));
        if (dwCount) {
            for (int i=0; i<(int) dwCount; i++) {
                CString string;
                GetText (i, string);
                string += _T ("\n");
                file.WriteString (string);
            }
        }
        bResult = TRUE;
    }
    catch (CFileException* e) {
        e->ReportError ();
        e->Delete ();
    }
    return bResult;
}

PhonesListBox.h

#if
!defined(AFX_PHONESLISTBOX_H__7BE4B250_90ED_11D2_8E53_006008A82731__INCLUDED_)
#define
AFX_PHONESLISTBOX_H__7BE4B250_90ED_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// PhonesListBox.h : header file
//

///////////////////////////////////////////////////////////////////////////
// CPhonesListBox window

class CPhonesListBox : public CListBox
{
// Construction
public:
    CPhonesListBox();

// Attributes
public:

// Operations
public:
// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CPhonesListBox)
    //}}AFX_VIRTUAL

// Implementation
public:
    void NewEntry();
    virtual ~CPhonesListBox();

    // Generated message map functions
protected:
    CFont m_font;
    //{{AFX_MSG(CPhonesListBox)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnEditItem();
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

///////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
// !defined(
//    AFX_PHONESLISTBOX_H__7BE4B250_90ED_11D2_8E53_006008A82731__INCLUDED_)

PhonesListBox.cpp

// PhonesListBox.cpp : implementation file
//

#include "stdafx.h"
#include "Phones.h"
#include "PhonesListBox.h"
#include "PhoneEdit.h"
#include "EditDialog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// CPhonesListBox

CPhonesListBox::CPhonesListBox()
{
}

CPhonesListBox::~CPhonesListBox()
{
}

BEGIN_MESSAGE_MAP(CPhonesListBox, CListBox)
    //{{AFX_MSG_MAP(CPhonesListBox)
    ON_WM_CREATE()
    ON_CONTROL_REFLECT(LBN_DBLCLK, OnEditItem)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CPhonesListBox message handlers

int CPhonesListBox::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CListBox::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    m_font.CreatePointFont (80, _T ("MS Sans Serif"));
    SetFont (&m_font, FALSE);
    SetTabStops (128);
    return 0;
}

void CPhonesListBox::OnEditItem() 
{
    CEditDialog dlg;

    CString strItem;
    int nIndex = GetCurSel ();
    GetText (nIndex, strItem);
    int nPos = strItem.Find (_T (`\t'));

    dlg.m_strName = strItem.Left (nPos);
    dlg.m_strPhone = strItem.Right (strItem.GetLength () - nPos - 1);
    if (dlg.DoModal () == IDOK) {
        strItem = dlg.m_strName + _T ("\t") + dlg.m_strPhone;
        DeleteString (nIndex);
        AddString (strItem);
    }
    SetFocus ();
}

void CPhonesListBox::NewEntry()
{
    CEditDialog dlg;
    if (dlg.DoModal () == IDOK) {
        CString strItem = dlg.m_strName + _T ("\t") + dlg.m_strPhone;
        AddString (strItem);
    }
    SetFocus ();
}

EditDialog.h

#if 
!defined(AFX_EDITDIALOG_H__7BE4B252_90ED_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_EDITDIALOG_H__7BE4B252_90ED_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// EditDialog.h : header file
//

///////////////////////////////////////////////////////////////////////////
// CEditDialog dialog

class CEditDialog : public CDialog
{
// Construction
public:
    CEditDialog(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
    //{{AFX_DATA(CEditDialog)
    enum { IDD = IDD_EDITDLG };
    CButton    m_wndOK;
    CButton    m_wndCancel;
    CPhoneEdit    m_wndPhoneEdit;
    CString    m_strName;
    CString    m_strPhone;
    //}}AFX_DATA

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

// Implementation
protected:

    // Generated message map functions
    //{{AFX_MSG(CEditDialog)
    virtual BOOL OnInitDialog();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
// !defined(
//    AFX_EDITDIALOG_H__7BE4B252_90ED_11D2_8E53_006008A82731__INCLUDED_)

EditDialog.cpp

// EditDialog.cpp : implementation file
//

#include "stdafx.h"
#include "Phones.h"
#include "PhoneEdit.h"
#include "EditDialog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////////////////////////////////////////
// CEditDialog dialog

CEditDialog::CEditDialog(CWnd* pParent /*=NULL*/)
    : CDialog(CEditDialog::IDD, pParent)
{
    //{{AFX_DATA_INIT(CEditDialog)
    m_strName = _T("");
    m_strPhone = _T("");
    //}}AFX_DATA_INIT
}

void CEditDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CEditDialog)
    DDX_Control(pDX, IDOK, m_wndOK);
    DDX_Control(pDX, IDCANCEL, m_wndCancel);
    DDX_Control(pDX, IDC_PHONE, m_wndPhoneEdit);
    DDX_Text(pDX, IDC_NAME, m_strName);
    DDV_MaxChars(pDX, m_strName, 32);
    DDX_Text(pDX, IDC_PHONE, m_strPhone);
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CEditDialog, CDialog)
    //{{AFX_MSG_MAP(CEditDialog)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CEditDialog message handlers

BOOL CEditDialog::OnInitDialog() 
{
    CDialog::OnInitDialog();
    m_wndOK.SetIcon (AfxGetApp ()->LoadIcon (IDI_OK));
    m_wndCancel.SetIcon (AfxGetApp ()->LoadIcon (IDI_CANCEL));
    return TRUE;
}

PhoneEdit.h

#if !defined(AFX_PHONEEDIT_H__7BE4B251_90ED_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_PHONEEDIT_H__7BE4B251_90ED_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// PhoneEdit.h : header file
//

///////////////////////////////////////////////////////////////////////////
// CPhoneEdit window

class CPhoneEdit : public CEdit
{
// Construction
public:
    CPhoneEdit();

// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CPhoneEdit)
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CPhoneEdit();

    // Generated message map functions
protected:
    //{{AFX_MSG(CPhoneEdit)
    afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

///////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
//!defined(AFX_PHONEEDIT_H__7BE4B251_90ED_11D2_8E53_006008A82731__INCLUDED_)

PhoneEdit.cpp

// PhoneEdit.cpp : implementation file
//

#include "stdafx.h"
#include "Phones.h"
#include "PhoneEdit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// CPhoneEdit

CPhoneEdit::CPhoneEdit()
{
}

CPhoneEdit::~CPhoneEdit()
{
}

BEGIN_MESSAGE_MAP(CPhoneEdit, CEdit)
    //{{AFX_MSG_MAP(CPhoneEdit)
    ON_WM_CHAR()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CPhoneEdit message handlers

void CPhoneEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (((nChar >= _T (`0')) && (nChar <= _T (`9'))) ¦¦
        (nChar == VK_BACK) ¦¦ (nChar == _T (`(`)) ¦¦ (nChar == _T (`)')) ¦¦
        (nChar == _T (`-')) ¦¦ (nChar == _T (` `)))
    
        CEdit::OnChar(nChar, nRepCnt, nFlags);
}

One of the most subtle yet important elements of Phones' source code is the innocent-looking statement

DDX_Control (pDX, IDC_PHONE, m_wndPhoneEdit);

in EditDialog.cpp. m_wndPhoneEdit is an instance of the CEdit-derived class CPhoneEdit, which represents an edit control that filters out nonnumeric characters. But m_wndPhoneEdit is linked to IDC_PHONE, which is an ordinary edit control created from the dialog template. The only reason IDC_PHONE acts like a CPhoneEdit instead of a CEdit is that DDX_Control subclasses the control and routes messages destined for the control through m-wndPhoneEdit's message map. The moral is both simple and profound. Whenever you want a control in a dialog box to behave as if it were an instance of a derived control class, map the control to a class instance with DDX_Control. Otherwise, any special behavior built into the derived class will go unused.

Phones does a reasonable job of demonstrating how MFC's CFileDialog class is used and how documents can be written to disk and read back. What it doesn't do very well is safeguard the user's data. If a list of names and phone numbers contains unsaved changes and another list is loaded or the application is shut down, Phones doesn't prompt you to save your changes. That's not how a real application should behave. Phones has other shortcomings, too, such as the fact that it doesn't register a file name extension with the operating system so that a saved file can be opened with a double-click. But don't despair: document handling is infinitely cleaner when performed in the context of the document/view architecture, and in the next chapter, we'll finally begin writing document/view applications. Once we do, MFC will handle many of the mundane chores expected of Windows applications, such as registering file name extensions and giving users the opportunity to save changes. If you've never written a document/view application before, you'll be pleasantly surprised at the level of support the framework provides.

The CHM file was converted to HTML by chm2web software.