[MFC]PropDemo程序:属性表的应用

1. 属性表的功能和大致创建步骤:

    1) 属性表是Windows提供的非常良好的性能之一,用于调节应用程序的相关参数,比如我的电脑右键后点击属性跳出的含有好几个选项卡的的对话框就是属性表;

    2) 属性表在Win32 API中的实现相当琐碎,但是MFC有了主结构的支持使得其实现属性表相当容易;

    3) 实现属性表的两个MFC类——CPropertySheet和CPropertyPage:

         i. CPropertySheet代表属性表本身,即整个属性表对话框的框架;

         ii. CPropertyPage就是属性表上面的选项卡(学名叫做“页”),有多少中不同的选项卡就要派生多少种CPropertyPage;

         iii. 只要将Page的各种对象按照一定顺序挂接到Sheet上面就能组成一张完整的属性表;

    4) 创建属性表的大致步骤:

         i. 为每个选项卡在.rc中定义相应的对话框模板,对话框的标题就是选项卡左上角的标签名;

         ii. 为每个选项卡派生出一个CPropertyPage类(并在Wizard中绑定相应的.rc模板),并将数据成员和每个选项卡中的控件绑定;

         iii. 创建CPropertySheet的派生类(各个页的对象作为Sheet的数据成员),实例化后在构造函数中使用CPropertySheet::AddPage将各个选项卡的对象按顺序添加进去,添加的顺序就是属性表中从左往右各个选项卡显示的顺序;


2. 模态属性表和非模态属性表:

    1) 属性表也是一种特殊的对话框,其模态和非模态的定义是一样的;

    2) 一般Windows用户更习惯模态的属性表,但通常非模态属性表逼格更加高一点,像Mac OS X的属性表都是非模态的,只要在选项卡中打个勾,修改个数据然后立马就能显现效果,并能在关闭属性表前重新激活上层父窗口;

    3) 创建方法:都是利用CPropertySheet派生类的对象进行创建,模态还是使用DoModal,非模态还是使用Create,就连关闭属性表的方法也是和普通对话框一样一样的;


3. Sheet和Page的构造函数:

    1) Page:CPropertyPage::CPropertyPage(UINT nIDTemplate, UINT nIDCaption = 0);

         i. 第一个参数就是绑定的.rc资源;

         ii. 第二个是选项卡标题的ID,如果该标题字符串是在源代码.cpp中定义的,则传入其ID,如果传0则表示标题直接取自.rc资源中定义的标题;

    2) Sheet:CPropertySheet::CPropertySheet(LPCTSTR lpszCaption, CWnd* pParentWnd = NULL, UNINT iSelectPage = 0);

         i. 和Page不一样,标题需要手工传入,即第一个参数;

         ii. 第二个参数即父窗口指针,如果为NULL则父窗口就是主框架窗口;

         iii. iSelectPage表示初始时被选中并显示出来的选项卡,从左到右从0开始计算下标;

    3) AddPage:

         i. 在Sheet的构造函数中使用,将各个Page按顺序添加到Sheet中;

         ii. 原型:void CPropertySheet::AddPage(CPropertyPage* pPage);


4. 属性表的按钮:

    1) 在Sheet中默认提供了三个按钮,分别是OK、Cancel和Apply,因此在各个Page中不能再包含;

    2) 当各个选项卡没被改动时Apply被灰化,只有当某个属性被改变时Apply才会被激活,因此在实现时需要用到控件的ON_XX_CHANGE消息映射来捕获控件内容的改变,然后在处理程序中调用Page的SetModified(TRUE)函数,表示属性被修饰了,然后自动激活Apply按钮(从SetModified到激活Apply都由MFC底层自动实现);

    3) 然后当Apply被按下时应该在按钮的处理程序中调用UpdateData函数将数据回送至数据成员,这里有一个非常重要的问题,那就是各个Page有各自的数据成员和控件进行交换,那是不是应该在Apply处理程序中调用所有页的UpdateData(TRUE)呢?要考虑这个问题的原因是!!!

有时候修改属性的时候是好几页一起修改的,比如先修改当前页,然后再切到其它页,等全部都修改完之后再按下Apply显示以下效果的,因此当然要考虑是不是应该在Apply中调用所有页的UpdateData(TRUE)函数了,其实答案是否定的!!

只需要调用当前页(即当前显示在用户面前的选项卡)的UpdateData(TRUE)函数即可,愿意很简单,因为在从一页切换到另一页时,MFC会自动调用被切的页的UpdateData(TRUE)函数,而当前页是没有被切换的页,所以需要自己手动调用UpdateData函数进行数据回送!!

!!事实上在旧版本Windows中,在OnApply中调用非当前页的UpdateData时MFC直接会报出系统错误,因为非当前页都是没被激活的页,没被激活的页也就意味着没被建立起来,建立都没建立起来何谈调用它的成员函数呢??但是现在Windows版本,如Win7等都已经进行了很好的容错升级,即使调用了非当前页的UpdateData也不会报错而正常执行,但我们平时还是需要正确地编写这些代码;

!!那应该如何得知谁才是真正的当前页呢?只要调用CPropertySheet::GetActivePage函数就能返回当前页的指针了;

    4) 接下来,由于Apply按钮只是“应用”,即查看修改后的结果,但并不关闭属性表(OK也是保存修改结果,但会关闭属性表),因此也需要通过像DlgDemo2使用过的方法,通过DATAWRAP包和SendMessage将更新后的数据回送、传递给视图类来更新视图区;

    5) 当Apply按下后,效果也更新后,那么属性表又变成了未修改的状态了,因此在OnApply的最后应该调用各个页的SetModified(FALSE)取消属性表的修改状态,让Apply按钮重新灰化;


!!相关函数的说明:

        a. SetModified:

             i. 原型:void CPropertyPage::SetModified(BOOL bChange = TRUE);

             ii. 这个函数将决定Apply按钮的灰化与否,如果修饰过(TRUE)则激活Apply按钮,否则灰化Apply按钮;

!!Apply按钮的ID在MFC中预定义过,其ID为ID_APPLY_NOW,所以Apply按钮也常称为Apply Now按钮;

        b. GetActivePage:

             i. 原型:CPropertyPage* CPropertySheet::GetActivePage() const;

             ii. 该函数将返回当前页面的指针;


5. 接下来的PropDemo程序将演示模态属性表的应用:

    1) 其实该程序是基于之前的DlgDemo系列的,只不过视图中画的是椭圆;

    2) 椭圆的属性除了DlgDemo系列有的宽、高、显示模式之外还有颜色一项;

    3) 修改属性必须通过File菜单中的Properties菜单项打开的属性表来完成;

    4) 其中宽、高和显示模式在Size选项卡中,而颜色的修改是在Color选项卡中;


6. MainFrame:

.h:

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

#if !defined(AFX_MAINFRM_H__12F5B351_AFF7_4AA3_8460_B790587BC958__INCLUDED_)
#define AFX_MAINFRM_H__12F5B351_AFF7_4AA3_8460_B790587BC958__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);
		// NOTE - the ClassWizard will add and remove member functions here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG
	afx_msg LRESULT OnApply(WPARAM wParam, LPARAM lParam); // 自定义消息映射都放在注释宏外
	DECLARE_MESSAGE_MAP()
};

/

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

#endif // !defined(AFX_MAINFRM_H__12F5B351_AFF7_4AA3_8460_B790587BC958__INCLUDED_)
!!由于建立的是模态属性表,因此不需要考虑关闭时的close效果,因此只需要传递一下WM_USER_APPLY即可,WM_USER_APPLY定义在StdAfx.h中,和DlgDemo2中的定义一样;

.cpp:

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

#include "stdafx.h"
#include "PropDemo.h"

#include "PropPageSize.h"
#include "PropPageColor.h"
#include "DemoPropSheet.h"

#include "ChildView.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)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code !
	ON_WM_SETFOCUS()
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_USER_APPLY, OnApply)
END_MESSAGE_MAP()

/
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
	// TODO: add member initialization code here
	
}

CMainFrame::~CMainFrame()
{
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	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(NULL, NULL, AFX_WS_DEFAULT_VIEW,
		CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
		return -1;

	return 0;
}

LRESULT CMainFrame::OnApply(WPARAM wParam, LPARAM lParam)
{
	m_wndView.SendMessage(WM_USER_APPLY, wParam, lParam);
	return 0;
}


7. 两个属性页:

CPropPageColor的.h和.cpp:

#if !defined(AFX_PROPPAGECOLOR_H__90FE21C8_9B53_420C_8362_EE4CA75F55AE__INCLUDED_)
#define AFX_PROPPAGECOLOR_H__90FE21C8_9B53_420C_8362_EE4CA75F55AE__INCLUDED_

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

/
// CPropPageColor dialog

class CPropPageColor : public CPropertyPage
{
	DECLARE_DYNCREATE(CPropPageColor)

// Construction
public:
	CPropPageColor();
	~CPropPageColor();

// Dialog Data
public: // 数据成员定义成public的,因为要在CChildView中访问它们
	static const COLORREF m_clrColors[3];
	//{{AFX_DATA(CPropPageColor)
	enum { IDD = IDD_PROPPAGE_COLOR };
	int		m_nColor;
	//}}AFX_DATA


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

// Implementation
protected:
	// Generated message map functions
	//{{AFX_MSG(CPropPageColor)
		// NOTE: the ClassWizard will add member functions here
	//}}AFX_MSG
	afx_msg void OnChange(); // 相应控件数据被修改,自定义放在注释宏外
	DECLARE_MESSAGE_MAP()

};

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

#endif // !defined(AFX_PROPPAGECOLOR_H__90FE21C8_9B53_420C_8362_EE4CA75F55AE__INCLUDED_)
// PropPageColor.cpp : implementation file
//

#include "stdafx.h"
#include "PropDemo.h"
#include "PropPageColor.h"

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

/
// CPropPageColor property page

IMPLEMENT_DYNCREATE(CPropPageColor, CPropertyPage)

const COLORREF CPropPageColor::m_clrColors[3] = { // 先定义使用到的三种颜色
	RGB(255, 0, 0),	// red
	RGB(0, 255, 0), // green
	RGB(0, 0, 255)  // blue
};

CPropPageColor::CPropPageColor() : CPropertyPage(CPropPageColor::IDD)
{
	//{{AFX_DATA_INIT(CPropPageColor)
	m_nColor = -1;
	//}}AFX_DATA_INIT
}

CPropPageColor::~CPropPageColor()
{
}

void CPropPageColor::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPropPageColor)
	DDX_Radio(pDX, IDC_RADIO_RED, m_nColor);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CPropPageColor, CPropertyPage)
	//{{AFX_MSG_MAP(CPropPageColor)
		// NOTE: the ClassWizard will add message map macros here
	//}}AFX_MSG_MAP
	// 点击即改变
	ON_BN_CLICKED(IDC_RADIO_RED, OnChange)
	ON_BN_CLICKED(IDC_RADIO_GREEN, OnChange)
	ON_BN_CLICKED(IDC_RADIO_BLUE, OnChange)
END_MESSAGE_MAP()

/
// CPropPageColor message handlers

void CPropPageColor::OnChange()
{
	// 首先要检查选中的单选框是否和之前的不一样,如果不一样才能将Apply按钮激活

	// 方法有两种
	// 第一种使用CWnd::GetCurrentMessage来获取触发当前消息的空间ID,保存在wParam的低16位
	int nClickedID = LOWORD(GetCurrentMessage()->wParam);
	// 第二种方式使用CWnd::GetCheckedRadioButton来获取当前被选中的单选框的ID
	// int nClickedID = GetCheckedRadioButton(IDC_RADIO_RED, IDC_RADIO_BLUE);

	// 最后只要和未被刷新过的m_nColor比较一下就行了
	// 在选单选按钮的时候肯定不会切换到其它选项卡,因此不会发生数据回送
	if (nClickedID - IDC_RADIO_RED != m_nColor)
		SetModified(TRUE);
}
!!这里顺便介绍以下GetCurrentMessage和GetCheckedRadioButton两个函数:

        1) GetCurrentMessage:

             i. 原型:static const MSG* PASCAL CWnd::GetCurrentMessage();

             ii. 返回的是当前正处理的消息的指针;

             iii. 可以访问到wParam、lParam等,消息ID就保存在wParam的低16位;

        2) GetCheckedRadioButton:

             i. 原型:int CWnd::GetCheckedRadioButton(int nIDFirstButton, int nIDLastButton);

             ii. 返回的是指定ID范围区间[nIDFirstButton, nIDLastButton]内被击中的按钮的ID,必须这些按钮的ID是连续的;


CPropPageSize的.h和.cpp文件:

#if !defined(AFX_PROPPAGESIZE_H__C1A6F9F0_2EF1_4ACE_883C_13F2CACECFBC__INCLUDED_)
#define AFX_PROPPAGESIZE_H__C1A6F9F0_2EF1_4ACE_883C_13F2CACECFBC__INCLUDED_

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

/
// CPropPageSize dialog

class CPropPageSize : public CPropertyPage
{
	DECLARE_DYNCREATE(CPropPageSize)

// Construction
public:
	CPropPageSize();
	~CPropPageSize();

// Dialog Data
	//{{AFX_DATA(CPropPageSize)
	enum { IDD = IDD_PROPPAGE_SIZE };
	int		m_nHeight;
	int		m_nWidth;
	int		m_nUnits;
	//}}AFX_DATA


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

// Implementation
protected:
	// Generated message map functions
	//{{AFX_MSG(CPropPageSize)
		// NOTE: the ClassWizard will add member functions here
	//}}AFX_MSG
	afx_msg void OnChange();
	DECLARE_MESSAGE_MAP()

};

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

#endif // !defined(AFX_PROPPAGESIZE_H__C1A6F9F0_2EF1_4ACE_883C_13F2CACECFBC__INCLUDED_)
// PropPageSize.cpp : implementation file
//

#include "stdafx.h"
#include "PropDemo.h"
#include "PropPageSize.h"

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

/
// CPropPageSize property page

IMPLEMENT_DYNCREATE(CPropPageSize, CPropertyPage)

CPropPageSize::CPropPageSize() : CPropertyPage(CPropPageSize::IDD)
{
	//{{AFX_DATA_INIT(CPropPageSize)
	m_nHeight = 0;
	m_nWidth = 0;
	m_nUnits = -1;
	//}}AFX_DATA_INIT
}

CPropPageSize::~CPropPageSize()
{
}

void CPropPageSize::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CPropPageSize)
	DDX_Text(pDX, IDC_EDIT_HEIGHT, m_nHeight);
	DDV_MinMaxInt(pDX, m_nHeight, 1, 128);
	DDX_Text(pDX, IDC_EDIT_WIDTH, m_nWidth);
	DDV_MinMaxInt(pDX, m_nWidth, 1, 128);
	DDX_Radio(pDX, IDC_RADIO_INCHES, m_nUnits);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CPropPageSize, CPropertyPage)
	//{{AFX_MSG_MAP(CPropPageSize)
		// NOTE: the ClassWizard will add message map macros here
	//}}AFX_MSG_MAP
	ON_EN_CHANGE(IDC_EDIT_WIDTH, OnChange)
	ON_EN_CHANGE(IDC_EDIT_HEIGHT, OnChange)
	ON_BN_CLICKED(IDC_RADIO_INCHES, OnChange)
	ON_BN_CLICKED(IDC_RADIO_CENTIMETERS, OnChange)
	ON_BN_CLICKED(IDC_RADIO_PIXELS, OnChange)
END_MESSAGE_MAP()

/
// CPropPageSize message handlers

void CPropPageSize::OnChange()
{
	// 因为除了单选框之外还有编辑框等
	// 所以得先判断触发消息的是哪种控件

	int nID = LOWORD(GetCurrentMessage()->wParam);
	// 只有当触发消息的是单选框再判断是否和原来选的一样
	if (IDC_RADIO_INCHES <= nID && nID <= IDC_RADIO_PIXELS) {
		if (nID - IDC_RADIO_INCHES != m_nUnits)
			SetModified(TRUE);
	}
	else { // 编辑框触发的消息
		SetModified(TRUE);
	}		
}

8. 属性表本身——CDemoPropSheet:

.h:

#if !defined(AFX_DEMOPROPSHEET_H__87DC51A3_1A46_439D_AD0A_D650FF444FB9__INCLUDED_)
#define AFX_DEMOPROPSHEET_H__87DC51A3_1A46_439D_AD0A_D650FF444FB9__INCLUDED_

#include "PropPageSize.h"	// Added by ClassView
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// DemoPropSheet.h : header file
//

/
// CDemoPropSheet

class CDemoPropSheet : public CPropertySheet
{
	DECLARE_DYNAMIC(CDemoPropSheet)

// Construction
public:
	CDemoPropSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
	CDemoPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);

// Attributes
public:

// Operations
public:

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

// Implementation
public:
	// 将两张选项卡的对象作为数据成员
	// 但是必须定义成共有的,因为在视图和属性表交换数据时
	   // 需要在视图类中访问它们
	CPropPageSize m_pageSize;
	CPropPageColor m_pageColor;
	virtual ~CDemoPropSheet();

	// Generated message map functions
protected:
	//{{AFX_MSG(CDemoPropSheet)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
	afx_msg void OnApply(); // OnApply不是Wizard能自动生成的消息响应函数因此需要
							// 声明在注释宏外
	DECLARE_MESSAGE_MAP()
};

/

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

#endif // !defined(AFX_DEMOPROPSHEET_H__87DC51A3_1A46_439D_AD0A_D650FF444FB9__INCLUDED_)


.cpp:

// DemoPropSheet.cpp : implementation file
//

#include "stdafx.h"
#include "PropDemo.h"

#include "PropPageSize.h"
#include "PropPageColor.h"
#include "DemoPropSheet.h"

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

/
// CDemoPropSheet

IMPLEMENT_DYNAMIC(CDemoPropSheet, CPropertySheet)

// 在两个版本的构造函数中按顺序添加Size页和Color页

CDemoPropSheet::CDemoPropSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
	AddPage(&m_pageSize);
	AddPage(&m_pageColor);
}

CDemoPropSheet::CDemoPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
	AddPage(&m_pageSize);
	AddPage(&m_pageColor);
}

CDemoPropSheet::~CDemoPropSheet()
{
}


BEGIN_MESSAGE_MAP(CDemoPropSheet, CPropertySheet)
	//{{AFX_MSG_MAP(CDemoPropSheet)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(ID_APPLY_NOW, OnApply) // ID_APPLY_NOW的消息映射
									// 由于OnApply的名字是用户自定义的
									// 因此不属于MFC可以自动生成的函数
									// 所以要定义在注释宏外
END_MESSAGE_MAP()

/
// CDemoPropSheet message handlers

void CDemoPropSheet::OnApply()
{
	GetActivePage()->UpdateData(); // 先回送当前页的数据

	PROPDATAWRAP pdw;
	pdw.nWidth = m_pageSize.m_nWidth;
	pdw.nHeight = m_pageSize.m_nHeight;
	pdw.nUnits = m_pageSize.m_nUnits;
	pdw.nColor = m_pageColor.m_nColor;
	GetParent()->SendMessage(WM_USER_APPLY, 0, (LPARAM)&pdw); // 数据打包发送到主框架
	// !!这里使用GetParent也行,因为父窗口同时也是主框架窗口

	// 应用后立马使Apply按钮灰化
	m_pageSize.SetModified(FALSE);
	m_pageColor.SetModified(FALSE);
}


9. 视图类:

.h:

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

#if !defined(AFX_CHILDVIEW_H__0C558D90_9B13_4A8E_A292_7504594C3B8B__INCLUDED_)
#define AFX_CHILDVIEW_H__0C558D90_9B13_4A8E_A292_7504594C3B8B__INCLUDED_

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

/
// CChildView window

class CChildView : public CWnd
{
// 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:
	// 用于和属性框中的数据进行交换
	int m_nColor;
	int m_nUnits;
	int m_nHeight;
	int m_nWidth;
	//{{AFX_MSG(CChildView)
	afx_msg void OnPaint();
	//}}AFX_MSG
	afx_msg void OnFileProperties(); // 菜单项中打开属性表
	afx_msg LRESULT OnApply(WPARAM wParam, LPARAM lParam); // 处理属性表的Apply Now消息
	DECLARE_MESSAGE_MAP()
};

/

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

#endif // !defined(AFX_CHILDVIEW_H__0C558D90_9B13_4A8E_A292_7504594C3B8B__INCLUDED_)

.cpp:

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

#include "stdafx.h"
#include "PropDemo.h"

#include "PropPageSize.h"
#include "PropPageColor.h"
#include "DemoPropSheet.h"

#include "ChildView.h"

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

/
// CChildView

CChildView::CChildView()
{
	m_nWidth = 4;
	m_nHeight = 2;
	m_nUnits = 0;
	m_nColor = 0;
}

CChildView::~CChildView()
{
}


BEGIN_MESSAGE_MAP(CChildView,CWnd )
	//{{AFX_MSG_MAP(CChildView)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
	ON_COMMAND(ID_FILE_PROPERTIES, OnFileProperties)
	ON_MESSAGE(WM_USER_APPLY, OnApply)
END_MESSAGE_MAP()


/
// CChildView message handlers

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

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL);

	return TRUE;
}

void CChildView::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	// TODO: Add your message handler code here
	CBrush brush(CPropPageColor::m_clrColors[m_nColor]);
	CBrush* pOldBrush = dc.SelectObject(&brush);

	switch (m_nUnits)
	{
	case 0:
		dc.SetMapMode(MM_LOENGLISH);
		dc.Ellipse(0, 0, m_nWidth * 100, -m_nHeight * 100);
		break;
	case 1:
		dc.SetMapMode(MM_LOMETRIC);
		dc.Ellipse(0, 0, m_nWidth * 100, -m_nHeight * 100);
		break;
	case 2:
		dc.SetMapMode(MM_TEXT);
		dc.Ellipse(0, 0, m_nWidth, m_nHeight);
		break;
	}
	dc.SelectObject(pOldBrush);
	// Do not call CWnd::OnPaint() for painting messages
}

void CChildView::OnFileProperties()
{
	// 创建属性表资源并对属性表中的数据进行初始化
	CDemoPropSheet ps(_T("Properties"));
	ps.m_pageSize.m_nWidth = m_nWidth;
	ps.m_pageSize.m_nHeight = m_nHeight;
	ps.m_pageSize.m_nUnits = m_nUnits;
	ps.m_pageColor.m_nColor = m_nColor;

	if (ps.DoModal() == IDOK) { // 属性表选中OK后就要回送数据
		m_nWidth = ps.m_pageSize.m_nWidth;
		m_nHeight = ps.m_pageSize.m_nHeight;
		m_nUnits = ps.m_pageSize.m_nUnits;
		m_nColor = ps.m_pageColor.m_nColor;
		Invalidate(); // 重画
	}
}

LRESULT CChildView::OnApply(WPARAM wParam, LPARAM lParam)
{ // 解包一路传递过来的数据,然后更新到视图的数据成员中并重画
	PROPDATAWRAP* pdw = (PROPDATAWRAP*)lParam;
	m_nWidth = pdw->nWidth;
	m_nHeight = pdw->nHeight;
	m_nUnits = pdw->nUnits;
	m_nColor = pdw->nColor;
	Invalidate();

	return 0;
}

10. StdAfx.h:

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__F0C74D39_A626_4C90_BAAB_194E2A5E0BBB__INCLUDED_)
#define AFX_STDAFX_H__F0C74D39_A626_4C90_BAAB_194E2A5E0BBB__INCLUDED_

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

#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h>         // MFC extensions
#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>			// MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

typedef struct tagPROPDATAWRAP {
	int nWidth;
	int nHeight;
	int nUnits;
	int nColor;
} PROPDATAWRAP;

#define	WM_USER_APPLY		(WM_USER + 0x0100)

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

#endif // !defined(AFX_STDAFX_H__F0C74D39_A626_4C90_BAAB_194E2A5E0BBB__INCLUDED_)


10. .rc:

//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/
#undef APSTUDIO_READONLY_SYMBOLS

/
// Chinese (P.R.C.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
#ifdef _WIN32
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
    "#define _AFX_NO_OLE_RESOURCES\r\n"
    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
    "\r\n"
    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
    "#ifdef _WIN32\r\n"
    "LANGUAGE 9, 1\r\n"
    "#pragma code_page(1252)\r\n"
    "#endif //_WIN32\r\n"
    "#include ""res\\PropDemo.rc2""  // non-Microsoft Visual C++ edited resources\r\n"
    "#include ""afxres.rc""         // Standard components\r\n"
    "#endif\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/
//
// Dialog
//

IDD_PROPPAGE_SIZE DIALOG DISCARDABLE  0, 0, 187, 143
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Size"
FONT 10, "System"
BEGIN
    LTEXT           "&Width",IDC_STATIC,15,15,25,8,NOT WS_GROUP
    EDITTEXT        IDC_EDIT_WIDTH,45,15,40,10,ES_AUTOHSCROLL
    LTEXT           "&Height",IDC_STATIC,15,30,25,8,NOT WS_GROUP
    EDITTEXT        IDC_EDIT_HEIGHT,45,30,40,10,ES_AUTOHSCROLL
    GROUPBOX        "&Units",IDC_STATIC,15,50,70,65
    CONTROL         "&Inches",IDC_RADIO_INCHES,"Button",BS_AUTORADIOBUTTON | 
                    WS_GROUP | WS_TABSTOP,25,65,40,10
    CONTROL         "&Centimeters",IDC_RADIO_CENTIMETERS,"Button",
                    BS_AUTORADIOBUTTON,25,80,50,10
    CONTROL         "&Pixels",IDC_RADIO_PIXELS,"Button",BS_AUTORADIOBUTTON,
                    25,95,35,10
END

IDD_PROPPAGE_COLOR DIALOG DISCARDABLE  0, 0, 187, 123
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Color"
FONT 10, "System"
BEGIN
    GROUPBOX        "Color",IDC_STATIC,20,10,70,75
    CONTROL         "&Red",IDC_RADIO_RED,"Button",BS_AUTORADIOBUTTON | 
                    WS_GROUP | WS_TABSTOP,35,25,35,10
    CONTROL         "&Green",IDC_RADIO_GREEN,"Button",BS_AUTORADIOBUTTON,35,
                    40,35,10
    CONTROL         "&Blue",IDC_RADIO_BLUE,"Button",BS_AUTORADIOBUTTON,35,55,
                    35,10
END


/
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE 
BEGIN
    IDD_PROPPAGE_SIZE, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 180
        TOPMARGIN, 7
        BOTTOMMARGIN, 136
    END

    IDD_PROPPAGE_COLOR, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 180
        TOPMARGIN, 7
        BOTTOMMARGIN, 116
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // Chinese (P.R.C.) resources
/


/
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

/
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDR_MAINFRAME           ICON    DISCARDABLE     "res\\PropDemo.ico"

/
//
// Menu
//

IDR_MAINFRAME MENU PRELOAD DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Properties...",              ID_FILE_PROPERTIES
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_APP_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo\tCtrl+Z",               ID_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t\tCtrl+X",                ID_EDIT_CUT
        MENUITEM "&Copy\tCtrl+C",               ID_EDIT_COPY
        MENUITEM "&Paste\tCtrl+V",              ID_EDIT_PASTE
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About PropDemo...",          ID_APP_ABOUT
    END
END


/
//
// Accelerator
//

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE 
BEGIN
    "C",            ID_EDIT_COPY,           VIRTKEY, CONTROL, NOINVERT
    "V",            ID_EDIT_PASTE,          VIRTKEY, CONTROL, NOINVERT
    VK_BACK,        ID_EDIT_UNDO,           VIRTKEY, ALT, NOINVERT
    VK_DELETE,      ID_EDIT_CUT,            VIRTKEY, SHIFT, NOINVERT
    VK_F6,          ID_NEXT_PANE,           VIRTKEY, NOINVERT
    VK_F6,          ID_PREV_PANE,           VIRTKEY, SHIFT, NOINVERT
    VK_INSERT,      ID_EDIT_COPY,           VIRTKEY, CONTROL, NOINVERT
    VK_INSERT,      ID_EDIT_PASTE,          VIRTKEY, SHIFT, NOINVERT
    "X",            ID_EDIT_CUT,            VIRTKEY, CONTROL, NOINVERT
    "Z",            ID_EDIT_UNDO,           VIRTKEY, CONTROL, NOINVERT
END


/
//
// Dialog
//

IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 235, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About PropDemo"
FONT 8, "MS Sans Serif"
BEGIN
    ICON            IDR_MAINFRAME,IDC_STATIC,11,17,20,20
    LTEXT           "PropDemo Version 1.0",IDC_STATIC,40,10,119,8,
                    SS_NOPREFIX
    LTEXT           "Copyright (C) 2015",IDC_STATIC,40,25,119,8
    DEFPUSHBUTTON   "OK",IDOK,178,7,50,14,WS_GROUP
END


#ifndef _MAC
/
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,0,0,1
 PRODUCTVERSION 1,0,0,1
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x1L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904B0"
        BEGIN
            VALUE "CompanyName", "\0"
            VALUE "FileDescription", "PropDemo MFC Application\0"
            VALUE "FileVersion", "1, 0, 0, 1\0"
            VALUE "InternalName", "PropDemo\0"
            VALUE "LegalCopyright", "Copyright (C) 2015\0"
            VALUE "LegalTrademarks", "\0"
            VALUE "OriginalFilename", "PropDemo.EXE\0"
            VALUE "ProductName", "PropDemo Application\0"
            VALUE "ProductVersion", "1, 0, 0, 1\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

#endif    // !_MAC


/
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE 
BEGIN
    IDD_ABOUTBOX, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 228
        TOPMARGIN, 7
        BOTTOMMARGIN, 48
    END
END
#endif    // APSTUDIO_INVOKED


/
//
// String Table
//

STRINGTABLE PRELOAD DISCARDABLE 
BEGIN
    IDR_MAINFRAME           "PropDemo"
END

STRINGTABLE PRELOAD DISCARDABLE 
BEGIN
    AFX_IDS_APP_TITLE       "PropDemo"
    AFX_IDS_IDLEMESSAGE     "Ready"
END

STRINGTABLE DISCARDABLE 
BEGIN
    ID_INDICATOR_EXT        "EXT"
    ID_INDICATOR_CAPS       "CAP"
    ID_INDICATOR_NUM        "NUM"
    ID_INDICATOR_SCRL       "SCRL"
    ID_INDICATOR_OVR        "OVR"
    ID_INDICATOR_REC        "REC"
END

STRINGTABLE DISCARDABLE 
BEGIN
    ID_APP_ABOUT            "Display program information, version number and copyright\nAbout"
    ID_APP_EXIT             "Quit the application; prompts to save documents\nExit"
END

STRINGTABLE DISCARDABLE 
BEGIN
    ID_NEXT_PANE            "Switch to the next window pane\nNext Pane"
    ID_PREV_PANE            "Switch back to the previous window pane\nPrevious Pane"
END

STRINGTABLE DISCARDABLE 
BEGIN
    ID_WINDOW_SPLIT         "Split the active window into panes\nSplit"
END

STRINGTABLE DISCARDABLE 
BEGIN
    ID_EDIT_CLEAR           "Erase the selection\nErase"
    ID_EDIT_CLEAR_ALL       "Erase everything\nErase All"
    ID_EDIT_COPY            "Copy the selection and put it on the Clipboard\nCopy"
    ID_EDIT_CUT             "Cut the selection and put it on the Clipboard\nCut"
    ID_EDIT_FIND            "Find the specified text\nFind"
    ID_EDIT_PASTE           "Insert Clipboard contents\nPaste"
    ID_EDIT_REPEAT          "Repeat the last action\nRepeat"
    ID_EDIT_REPLACE         "Replace specific text with different text\nReplace"
    ID_EDIT_SELECT_ALL      "Select the entire document\nSelect All"
    ID_EDIT_UNDO            "Undo the last action\nUndo"
    ID_EDIT_REDO            "Redo the previously undone action\nRedo"
END

STRINGTABLE DISCARDABLE 
BEGIN
    AFX_IDS_SCSIZE          "Change the window size"
    AFX_IDS_SCMOVE          "Change the window position"
    AFX_IDS_SCMINIMIZE      "Reduce the window to an icon"
    AFX_IDS_SCMAXIMIZE      "Enlarge the window to full size"
    AFX_IDS_SCNEXTWINDOW    "Switch to the next document window"
    AFX_IDS_SCPREVWINDOW    "Switch to the previous document window"
    AFX_IDS_SCCLOSE         "Close the active window and prompts to save the documents"
END

STRINGTABLE DISCARDABLE 
BEGIN
    AFX_IDS_SCRESTORE       "Restore the window to normal size"
    AFX_IDS_SCTASKLIST      "Activate Task List"
END

#endif    // English (U.S.) resources
/



#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE 9, 1
#pragma code_page(1252)
#endif //_WIN32
#include "res\PropDemo.rc2"  // non-Microsoft Visual C++ edited resources
#include "afxres.rc"         // Standard components
#endif

/
#endif    // not APSTUDIO_INVOKED


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值