| Tenouk C & C++ | MFC Home | Menus, Key Acc., Rich Edit & Property Sheets 2 | Menus, Key Acc., Rich Edit & Property Sheets 4 | Download | Site Index |


 

 

 

 

 

Module 7a: Menus, Keyboard Accelerators, the Rich Edit Control,

and Property Sheets – Part 3

 

 

 

 

 

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:

  1. Property Sheets

  2. Building a Property Sheet

  3. Property Sheet Data Exchange

  4. The MYMFCPRO Example Revisited

 

 

Property Sheets

 

You've already seen property sheets in Visual C++ and in many other modern Windows-based programs. A property sheet is a nice UI element that allows you to cram lots of categorized information into a small dialog. The user selects pages by clicking on their tabs. Windows offers a tab control that you can insert in a dialog, but it's more likely that you'll want to put dialogs inside the tab control. The MFC library supports this, and the result is called a property sheet. The individual dialogs are called property pages.

 

Building a Property Sheet

 

Follow these general steps to build a property sheet using the Visual C++ tools:

 

  1. Use the resource editor to create a series of dialog templates that are all approximately the same size. The captions are the strings that you want to display on the tabs.

  2. Use ClassWizard to generate a class for each template. Select CPropertyPage as the base class. Add data members for the controls.

  3. Use ClassWizard to generate a single class derived from CPropertySheet.

  4. To the sheet class, add one data member for each page class.

  5. In the sheet class constructor, call the AddPage() member function for each page, specifying the address of the embedded page object.

  6. In your application, construct an object of the derived CPropertySheet class, and then call DoModal(). You must specify a caption in the constructor call, but you can change the caption later by calling CPropertySheet::SetTitle.

  7. Take care of programming for the Apply button.

 

Property Sheet Data Exchange

 

The framework puts three buttons on a property sheet (See Figure 1 for example). Be aware that the framework calls the Dialog Data Exchange (DDX) code for a property page each time the user switches to and from that page. As you would expect, the framework calls the DDX code for a page when the user clicks OK, thus updating that page's data members. From these statements, you can conclude that all data members for all pages are updated when the user clicks OK to exit the sheet. All this with no C++ programming on your part!

With a normal modal dialog, if the user clicks the Cancel button, the changes are discarded and the dialog class data members remain unchanged. With a property sheet, however, the data members are updated if the user changes one page and then moves to another, even if the user exits by clicking the Cancel button.

What does the Apply button do? Nothing at all if you don't write some code. It won't even be enabled. To enable it for a given page, you must set the page's modified flag by calling SetModified(TRUE) when you detect that the user has made changes on the page.

If you've enabled the Apply button, you can write a handler function for it in your page class by overriding the virtual CPropertyPage::OnApply function. Don't try to understand property page message processing in the context of normal modal dialogs; it's quite different. The framework gets a WM_NOTIFY message for all button clicks. It calls the DDX code for the page if the OK or Apply button was clicked. It then calls the virtual OnApply() functions for all the pages, and it resets the modified flag, which disables the Apply button. Don't forget that the DDX code has already been called to update the data members in all pages, so you need to override OnApply() in only one page class. What you put in your OnApply() function is your business, but one option is to send a user-defined message to the object that created the property sheet. The message handler can get the property page data members and process them. Meanwhile, the property sheet stays on the screen.

 

The MYMFCPRO Project Example Revisited

 

Now we'll add a property sheet to MYMFCPRO that allows the user to change the rich edit control's font characteristics. Of course, we could have used the standard MFC CFontDialog() function, but then you wouldn't have learned how to create property sheets. Figure 1 shows the property sheet that you'll build as you continue with MYMFCPRO.

 

 

Figure 1: The property sheet from MYMFCPRO.

 

If you haven't built MYMFCPRO, follow the instructions that begin under the MYMFCPRO example to build it. If you already have MYMFCPRO working with the Transfer menu commands, just continue on with the following long steps:

 

Use the resource editor to edit the application's main menu. Click on the ResourceView tab in the Workspace window. Edit the IDR_MAINFRAME menu resource to add a Format menu that looks something like this.

 

Adding new menus and its items.

 

Figure 2: Adding new menus and its items.

 

Use the following command IDs for the new Format menu items.

 

Caption

Command ID

&Default

ID_FORMAT_DEFAULT

&Selection

ID_FORMAT_SELECTION

 

Table 1

 

Add appropriate prompt strings for the two menu items as needed.

 

Use ClassWizard to add the view class command and update command UI message handlers. Select the CMymfcproView class, and then add the following member functions.

 

Object ID

Message

Member Function

ID_FORMAT_DEFAULT

COMMAND

OnFormatDefault()

ID_FORMAT_SELECTION

COMMAND

OnFormatSelection()

ID_FORMAT_SELECTION

UPDATE_COMMAND_UI

OnUpdateFormatSelection()

 

Table 1

 

Using ClassWizard to add the view class command and update command UI message handlers.

 

Figure 3: Using ClassWizard to add the view class commands and update command UI message handlers.

 

Use the resource editor to add four property page dialog templates. The templates are shown here with their associated IDs.

 

Using the resource editor to add Font property page dialog templates.

 

Figure 4: Using the resource editor to add Font property page dialog templates.

 

Using the resource editor to add Effects property page dialog templates.

 

Figure 5: Using the resource editor to add Effects property page dialog templates.

 

Using the resource editor to add Color property page dialog templates.

 

Figure 6: Using the resource editor to add Color property page dialog templates.

 

Using the resource editor to add Size property page dialog templates.

 

Figure 7: Using the resource editor to add Size property page dialog templates.

 

Use the IDs in the table below for the controls in the dialogs. Set the Auto Buddy and the Set Buddy Integer properties for the spin button control, and set the Group property for the IDC_FONT and IDC_COLOR radio buttons. Use ClassWizard to create the classes CPage1, CPage2, CPage3, and CPage4. In each case, select:

 

  1. CPropertyPage as the base class.

  2. Click the Change button in ClassWizard's New Class dialog to generate the code for all these classes in the files Property.h and Property.cpp.

 

Then add the data members shown here.

 

Dialog ID

Control

Control ID

Type

Data Member

IDD_PAGE1

First radio button

IDC_FONT

int

m_nFont

IDD_PAGE2

Bold check box

IDC_BOLD

BOOL

m_bBold

IDD_PAGE2

Italic check box

IDC_ITALIC

BOOL

m_bItalic

IDD_PAGE2

Underline check box

IDC_UNDERLINE

BOOL

m_bUnderline

IDD_PAGE3

First radio button

IDC_COLOR

int

m_nColor

IDD_PAGE4

Edit control

IDC_FONTSIZE

int

m_nFontSize

IDD_PAGE4

Spin button control

IDC_SPIN1

 -

 -

 

Table 2

 

New class creation dialog prompt.

 

Figure 8: New class creation dialog prompt.

 

 

Creating the CPage1 class.

 

Figure 9: Creating the CPage1 class.

 

Changing the class header and implementation file names.

 

Figure 10: Changing the class header and implementation file names.

 

Using ClassWizard to create the new CPage2, CPage3, and CPage4 classes.

 

Figure 11: Using ClassWizard to create the new CPage2, CPage3, and CPage4 classes.

 

CPage2 new class information.

 

Figure 12: CPage2 new class information.

 

Continue for CPage3 and CPage4 classes…Then continue for the data member.

 

Adding data member variable.

 

Figure 13: Adding data member variable.

 

Continue for other classes and for CPage4, set the minimum value of IDC_FONTSIZE to 8 and its maximum value to 24.

 

Setting the minimum value of IDC_FONTSIZE to 8 and its maximum value to 24.

 

Figure 14: Setting the minimum value of IDC_FONTSIZE to 8 and its maximum value to 24.

 

Finally, use ClassWizard to add an OnInitDialog() message handler function for CPage4.

 

Adding an OnInitDialog() message handler function for CPage4.

 

Figure 15: Adding an OnInitDialog() message handler function for CPage4.

 

Use ClassWizard to create a class derived from CPropertySheet. Choose the name CFontSheet. Generate the code in the files Property.h and Property.cpp, the same files you used for the property page classes. Listing 1 shows these files with the added code in orange color.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Using ClassWizard to create a CFontSheet class that derived from CPropertySheet.

 

Figure 16: Using ClassWizard to create a CFontSheet class that derived from CPropertySheet.

 

 

PROPERTY.H

#if !defined(AFX_PROPERTY_H__CD702F99_7495_11D0_8FDC_00C04FC2A0C2__INCLUDED_)

#define AFX_PROPERTY_H_ _CD702F99_7495_11D0_8FDC_00C04FC2A0C2__INCLUDED_

 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// Property.h : header file

//

 

#define WM_USERAPPLY WM_USER + 5

extern CView* g_pView;

 

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

// CPage1 dialog

 

class CPage1 : public CPropertyPage

{

    DECLARE_DYNCREATE(CPage1)

 

// Construction

public:

    CPage1();

    ~CPage1();

 

// Dialog Data

    //{{AFX_DATA(CPage1)

    enum { IDD = IDD_PAGE1 };

    int     m_nFont;

    //}}AFX_DATA

 

// Overrides

    // ClassWizard generate virtual function overrides

    //{{AFX_VIRTUAL(CPage1)

    protected:

    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV

                                                      // support

    //}}AFX_VIRTUAL

    virtual BOOL OnApply();

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

// Implementation

protected:

    // Generated message map functions

    //{{AFX_MSG(CPage1)

        // NOTE: the ClassWizard will add member functions here

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

 

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

// CPage2 dialog

 

class CPage2 : public CPropertyPage

{

    DECLARE_DYNCREATE(CPage2)

 

// Construction

public:

    CPage2();

    ~CPage2();

 

// Dialog Data

    //{{AFX_DATA(CPage2)

    enum { IDD = IDD_PAGE2 };

    BOOL    m_bBold;

    BOOL    m_bItalic;

    BOOL    m_bUnderline;

    //}}AFX_DATA

 

// Overrides

    // ClassWizard generate virtual function overrides

    //{{AFX_VIRTUAL(CPage2)

    protected:

    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV

                                                      // support

    //}}AFX_VIRTUAL

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

// Implementation

protected:

    // Generated message map functions

    //{{AFX_MSG(CPage2)

        // NOTE: the ClassWizard will add member functions here

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

 

};

 

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

// CPage3 dialog

 

class CPage3 : public CPropertyPage

{

    DECLARE_DYNCREATE(CPage3)

 

// Construction

public:

    CPage3();

    ~CPage3();

 

// Dialog Data

    //{{AFX_DATA(CPage3)

    enum { IDD = IDD_PAGE3 };

    int     m_nColor;

    //}}AFX_DATA

 

// Overrides

    // ClassWizard generate virtual function overrides

    //{{AFX_VIRTUAL(CPage3)

    protected:

    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV

                                                      // support

    //}}AFX_VIRTUAL

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

// Implementation

protected:

    // Generated message map functions

    //{{AFX_MSG(CPage3)

        // NOTE: the ClassWizard will add member functions here

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

 

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

// CPage4 dialog

 

class CPage4 : public CPropertyPage

{

    DECLARE_DYNCREATE(CPage4)

 

// Construction

public:

    CPage4();

    ~CPage4();

 

// Dialog Data

    //{{AFX_DATA(CPage4)

    enum { IDD = IDD_PAGE4 };

    int     m_nFontSize;

    //}}AFX_DATA

 

// Overrides

    // ClassWizard generate virtual function overrides

    //{{AFX_VIRTUAL(CPage4)

    protected:

    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV

                                                     // support

    //}}AFX_VIRTUAL

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

// Implementation

protected:

    // Generated message map functions

    //{{AFX_MSG(CPage4)

    virtual BOOL OnInitDialog();

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

 

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

// CFontSheet

 

class CFontSheet : public CPropertySheet

{

    DECLARE_DYNAMIC(CFontSheet)

 

public:

    CPage1 m_page1;

    CPage2 m_page2;

    CPage3 m_page3;

    CPage4 m_page4;

 

// Construction

public:

    CFontSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);

    CFontSheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);

 

// Attributes

public:

 

// Operations

public:

// Overrides

    // ClassWizard generated virtual function overrides

    //{{AFX_VIRTUAL(CFontSheet)

    //}}AFX_VIRTUAL

 

// Implementation

public:

    virtual ~CFontSheet();

 

    // Generated message map functions

protected:

    //{{AFX_MSG(CFontSheet)

        // NOTE - the ClassWizard will add and remove member functions here.

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

};

 

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

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations

//  immediately before the previous line.

 

#endif // !defined(AFX_PROPERTY_H_ _CD702F99_7495_11D0_8FDC_00C04FC2A0C2__INCLUDED_)

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

PROPERTY.CPP

// Property.cpp : implementation file

//

 

#include "stdafx.h"

#include "mymfcpro.h"

#include "Property.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[ ] = __FILE__;

#endif

 

CView* g_pView;

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

// CPage1 property page

 

IMPLEMENT_DYNCREATE(CPage1, CPropertyPage)

 

CPage1::CPage1() : CPropertyPage(CPage1::IDD)

{

    //{{AFX_DATA_INIT(CPage1)

    m_nFont = -1;

    //}}AFX_DATA_INIT

}

 

CPage1::~CPage1()

{

}

 

BOOL CPage1::OnApply()

{

    TRACE("CPage1::OnApply\n");

    g_pView->SendMessage(WM_USERAPPLY);

    return TRUE;

}

 

BOOL CPage1::OnCommand(WPARAM wParam, LPARAM lParam)

{

    SetModified(TRUE);

    return CPropertyPage::OnCommand(wParam, lParam);

}

 

void CPage1::DoDataExchange(CDataExchange* pDX)

{

    TRACE("Entering CPage1::DoDataExchange -- %d\n", pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CPage1)

    DDX_Radio(pDX, IDC_FONT, m_nFont);

    //}}AFX_DATA_MAP

}

 

BEGIN_MESSAGE_MAP(CPage1, CPropertyPage)

    //{{AFX_MSG_MAP(CPage1)

        // NOTE: the ClassWizard will add message map macros here

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CPage1 message handlers

 

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

// CPage2 property page

 

IMPLEMENT_DYNCREATE(CPage2, CPropertyPage)

 

CPage2::CPage2() : CPropertyPage(CPage2::IDD)

{

    //{{AFX_DATA_INIT(CPage2)

    m_bBold = FALSE;

    m_bItalic = FALSE;

    m_bUnderline = FALSE;

    //}}AFX_DATA_INIT

}

 

CPage2::~CPage2()

{      }

 

BOOL CPage2::OnCommand(WPARAM wParam, LPARAM lParam)

{

    SetModified(TRUE);

    return CPropertyPage::OnCommand(wParam, lParam);

}

 

void CPage2::DoDataExchange(CDataExchange* pDX)

{

    TRACE("Entering CPage2::DoDataExchange -- %d\n", pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CPage2)

    DDX_Check(pDX, IDC_BOLD, m_bBold);

    DDX_Check(pDX, IDC_ITALIC, m_bItalic);

    DDX_Check(pDX, IDC_UNDERLINE, m_bUnderline);

    //}}AFX_DATA_MAP

}

 

BEGIN_MESSAGE_MAP(CPage2, CPropertyPage)

    //{{AFX_MSG_MAP(CPage2)

        // NOTE: the ClassWizard will add message map macros here

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CPage2 message handlers

 

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

// CPage3 property page

 

IMPLEMENT_DYNCREATE(CPage3, CPropertyPage)

 

CPage3::CPage3() : CPropertyPage(CPage3::IDD)

{

    //{{AFX_DATA_INIT(CPage3)

    m_nColor = -1;

    //}}AFX_DATA_INIT

}

 

CPage3::~CPage3()

{     }

 

BOOL CPage3::OnCommand(WPARAM wParam, LPARAM lParam)

{

    SetModified(TRUE);

    return CPropertyPage::OnCommand(wParam, lParam);

}

 

void CPage3::DoDataExchange(CDataExchange* pDX)

{

   TRACE("Entering CPage3::DoDataExchange -- %d\n", pDX->m_bSaveAndValidate);

   CPropertyPage::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CPage3)

    DDX_Radio(pDX, IDC_COLOR, m_nColor);

    //}}AFX_DATA_MAP

}

 

BEGIN_MESSAGE_MAP(CPage3, CPropertyPage)

    //{{AFX_MSG_MAP(CPage3)

        // NOTE: the ClassWizard will add message map macros here

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CPage3 message handlers

 

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

// CPage4 property page

 

IMPLEMENT_DYNCREATE(CPage4, CPropertyPage)

 

CPage4::CPage4() : CPropertyPage(CPage4::IDD)

{

    //{{AFX_DATA_INIT(CPage4)

    m_nFontSize = 0;

    //}}AFX_DATA_INIT

}

 

CPage4::~CPage4()

{      }

 

BOOL CPage4::OnCommand(WPARAM wParam, LPARAM lParam)

{

    SetModified(TRUE);

    return CPropertyPage::OnCommand(wParam, lParam);

}

 

void CPage4::DoDataExchange(CDataExchange* pDX)

{

    TRACE("Entering CPage4::DoDataExchange -- %d\n", pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CPage4)

    DDX_Text(pDX, IDC_FONTSIZE, m_nFontSize);

    DDV_MinMaxInt(pDX, m_nFontSize, 8, 24);

    //}}AFX_DATA_MAP

}

 

BEGIN_MESSAGE_MAP(CPage4, CPropertyPage)

    //{{AFX_MSG_MAP(CPage4)

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CPage4 message handlers

 

BOOL CPage4::OnInitDialog()

{

    CPropertyPage::OnInitDialog();

    ((CSpinButtonCtrl*) GetDlgItem(IDC_SPIN1))->SetRange(8, 24);

    return TRUE;  // return TRUE unless you set the focus to a control

                  //  EXCEPTION: OCX Property Pages should return FALSE

}

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

// CFontSheet

 

IMPLEMENT_DYNAMIC(CFontSheet, CPropertySheet)

 

CFontSheet::CFontSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)

{   }

 

CFontSheet::CFontSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) : CPropertySheet(pszCaption, pParentWnd, iSelectPage)

{

    AddPage(&m_page1);

    AddPage(&m_page2);

    AddPage(&m_page3);

    AddPage(&m_page4);

}

 

CFontSheet::~CFontSheet()

{     }

 

BEGIN_MESSAGE_MAP(CFontSheet, CPropertySheet)

    //{{AFX_MSG_MAP(CFontSheet)

        // NOTE - the ClassWizard will add and remove mapping macros here.

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

 

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

// CFontSheet message handlers

 

 

Listing 1: The MYMFCPRO header and implementation file listings for the property page and property sheet classes.

 

 

Continue on next module...part 2.

 

 

 

 

Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. Porting & Migrating your older programs.

  4. MSDN Library

  5. DCOM at MSDN.

  6. COM+ at MSDN.

  7. COM at MSDN.

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 

 

 


 

| Tenouk C & C++ | MFC Home | Menus, Key Acc., Rich Edit & Property Sheets 2 | Menus, Key Acc., Rich Edit & Property Sheets 4 | Download | Site Index |