资源预览内容
第1页 / 共161页
第2页 / 共161页
第3页 / 共161页
第4页 / 共161页
第5页 / 共161页
第6页 / 共161页
第7页 / 共161页
第8页 / 共161页
第9页 / 共161页
第10页 / 共161页
亲,该文档总共161页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第五章 MFC 与对话框编程5.1 对话框的基本原理 对话框是 Windows 应用程序 GUI 界面的一个重要组成部分,它的主要功能:接收用户的输入(数据和操作、控制命令);输出用户关心的状态信息(操作的结果数据和状态)。 在对话框中,可以放置各种必要的控件,所有与用户的交互 操作都是通过这些控件实现的。 在 MFC 中,对话框的基本属性和行为由 CDialog 类描述,该类的基类为 CWnd,因此,它继承了窗口类的所有属性和功能,并且增加了交互操作所需要的数据交换特性。放置在对话框中的各类控件,MFC 都有相应的类来描述,所有控件类的基类都是 CWnd。 CDialog 类和所有控件类的派生层次结构如下: 由此我们可以看出:对话框可以视为是提供了一个能放置多个子窗口,并提供了窗口之间通讯操作的窗口框架。这种异类同构的结构有利于简化构造和便于通讯。CObjectCCmdTargetCWndCDialogCStaticCEditCComboBoxCListBoxCProgressCtrlCSliderCtrlCSpinButtonCtrl控件控件 对话框和放置在其中的控件的创建,MFC 应用程序是从两个方面实现的:对话框模板资源:描述对话框本身和各个控件的类型、外观 属性和控件在对话框中的分布信息。使用资源编辑器绘制模 板,并将相关的信息保存在资源文件(扩展名为“.rc”)中。对话框类:封装对话框的属性和操作功能。保存在相应的定 义和实现文件中。注意,与所有 CWnd 及其派生类对象一样,对话框类对象必须与对话框模板所描述的 Windows 窗口相关联后,所有施加于对话框的操作才是合法的。 对话框的设计步骤一般是先设计对话框模板,然后定义与对话框模板资源相关联的对话框类。对话框的使用分为模态对话框和非模态对话框两种:模态对话框:对话框打开时,用户只能在对话框中进行交互 操作,只有对话框关闭后,才能进行其他用户界面的操作。非模态对话框:打开对话框进行操作的同时,仍然可以进行 其他用户界面的交互操作。5.2 创建模态对话框 创建一个名为“RegisterRegister” SDI 应用程序,用于人员信息的注册登记。该应用程序项目除视图的基类选择 CEditView 外,其他均接受缺省选择。5.2.1 创建对话框模板资源 为项目添加一个对话框模板(缺省命名为 IDD_DIALOG1)。从控件选择面板上选择所需要的控件,将它放置到对话框模板中你所希望的设计位置上。 对话框和对话框中的每个控件都可以通过“属性”对话框设置它们的属性和风格。选中对话框或某个控件,点击鼠标右键便会弹出一个浮动上下文菜单:选择菜单项 Properties,便弹出选中对话框或控件的属性操作对话框:操作选择标签的个数和类型随着选中的对象(对话框或不同控件)的不同而不同,但其中最右边的两个按钮和第一项标签是相同的。1 按钮 :按下该按钮,使按钮变成 ,使得该属性对话框 保持可见(Keep Visible),直到释放该按钮或强制关闭对话框。 属性对话框的内容会随着当前选中对象的改变而变化。2 按钮 :点击该按钮,将显示当前选中属性页面相关内容的 帮助信息。3 通用标签 :提供对话框或所有控件的通用属性操作。 对话框的主要通用属性操作: ID指定对话框的标识值,程序可使用它装载对话框资源和选取对话框窗口。 Caption指定对话框的显示标题。 Font选择对话框中所有文本(包括控件标题)字体。 Menu如果需要,可为对话框选择显示菜单资源。 控件的通用属性的含义如下: ID指定控件的标识值,程序使用它装载控件资源和选取控件窗口。 Caption指定诸如静态文本、组框、按钮、复选框、单选按钮等控件的显示标题。对于能发出通知消息的控件,其标题与菜单项标题一样,紧跟字符& 之后的字符在运行时具有下划线,可通过按Alt +下划线字符替代点击该控件。 Visible指定控件被创建后是否可见。 Disable指定控件被创建后是处于允许还是被禁止使用。 Tab stop对话框运行时,允许用户使用Tab 键顺序选中具有该属性的控件。控件选中顺序可以任意指定。 Group用来指定一组控件(例如单选按钮),用户可以用箭头键在组内的控件之间移动选择;在同一组内的单选按钮具有互斥性,即只能选中一个单选按钮。如果一个控件具有Group 属性,则这个控件以及按Tab 顺序紧随其后的的所有控件都属于同一组,直到另一个有Group 属性的控件为止。本例中对话框的具体设计步骤如下:1 修改对话框模板资源的标识为 IDD_REGISTER。2 在对话框中添加控件控件类型控件标识显示标题其他属性组框组框静态文本文本框复选框静态文本文本框组框单选按钮单选按钮默认默认默认IDC_NAME IDC_MARRIED 默认IDC_AGE 默认IDC_SEX默认个人情况单位情况姓名婚否性别男女默认默认默认默认默认默认默认默认Group、Tab stop默认控件类型控件标识显示标题其他属性组框单选按钮单选按钮静态文本文本框静态文本组合列表框静态文本列表框按钮按钮 默认 IDC_WORK IDC_WORK1 默认 IDC_UINT默认 IDC_KIND默认 IDC_INCOME IDOK IDCANCEL 就业情况在职下岗工作单位单位性质工资收入确定(&Y)取消(&C) 默认 Group、Tab stop默认默认默认默认下拉列表、不排序、初始化列表项。默认不排序默认默认 3 设置控件的 Tab 顺序4 测试对话框效果5.2.2 对话框类的创建 使用 ClassWizard 创建与对话框模板资源 IDD_REGISTER 相关联的对话框类 CRegisterDialog,其基类为 CDialog。5.2.3 为对话框类加入成员变量 对话框的主要功能就是要完成数据信息的输入输出,用户通过控件输入的数据信息需要存放在相应的数据对象中,才能被程序读取使用;程序通过控件输出的数据信息也需要先存储到相应数据对象中,才能被送到对应的控件显示。这些数据对象相当于控件与用户或程序之间交换数据信息的缓冲区,所以在对话框类中增加与对话框模板中控件对应的数据对象是必要的。控件所对应的数据对象有两类:一类是控件所对应的 MFC 类对象(控件类对象)可以用于对 控件的功能的调用;另一类是数值类对象,用于数据信息的交换。常用控件所对 应的数值类对象如下:控件类型数值类对象类型文本框 CEditCString, int, UINT, long, DWORD, float, double, short, BOOL, COleDateTime, COleCurrency(输入输出的不同类型内容) 普通复选框 CButton BOOL(非零:选择,零:不选择) 三态复选框 CButton int(0: 未选中、1: 选中、2: 不确定)滚动条 CScrollBar,滑尺 CSliderCtrlint(游标位置) 组中的确单选按钮 CButtonint (-1: 无选中, 0: 选中组中第一按钮, 1: 组中第二个) 列表框 CListCtrlCString (空:无列表项选中、非空:选中的列表项) 下拉式组合列表框 CComboBoxCString (空:无列表项选中、非空:选中的列表项) 或int (-1: 未选中, 0: 选中列表第一项, 1: 选中列表第二项) 为控件定义哪一类数据对象,或是两类数据对象都定义将根据控件在对话框类中的使用情况而定。具体的添加方法:在 Visual C+ 6.0 中,使用 ClassWizard 的Member Variables属性页完成。添加方法如下:在对话框中选中要添加数据对象的控件 ID,双击被选项或点击 按钮,弹出“Add Member Variable”对话框:在该对话框中可以确定你所要添加的数据对象的种类是数值类对象还是控件类对象,并为所添加数据对象命名(推荐命名规则:m_+ 数据对象类型缩写 + 数据对象含义,例如 m_nWork)。本例在 CRegisterDialog 中增加的数据对象如下:控件ID数据对象类型数据对象名IDC_AGEIDC_INCOMEIDC_INCOMEIDC_KINDIDC_MARRIEDIDC_NAMEIDC_SEXIDC_UINTIDC_WORK UINTCStringCListBoxCStringBOOLCStringintCStringint m_nAgem_strIncomem_ctrlIncomem_strKindm_bMarriedm_strNamem_nSexm_strUnitm_nWork 在 Visual C+ .NET 中,是通过在对话框模板中,为每个需要添加对象成员的方法实现的。具体的添加方法如下图所示:5.2.4 对话框的初始化 对话框初始化是在对话框类构造函数和对话框窗口初始化成员函数 OnInitDialog 中完成的。构造函数只完成数据成员的初始化,因为此时对话框类对象 正在创建中,而对话框窗口还未创建,窗口句柄无效。如果 需要初始化的数据对象是通过 ClassWizard 的 Member Variables 页添加的,则构造函数中数据对象的初始化代码将会由 ClassWizard 自动添加(当然可以根据需要修改这些代码)。 例如: CRegisterDialog:CRegisterDialog(CWnd* pParent /*=NULL*/) : CDialog(CRegisterDialog:IDD, pParent) /AFX_DATA_INIT(CRegisterDialog) m_nAge = 0; m_strIncome = _T(); m_strKind = _T(); m_bMarried = FALSE;m_strName = _T(); m_nSex = -1; m_strUnit = _T(); m_nWork = -1; /AFX_DATA_INIT OnInitDialog 虚函数是窗口消息 WM_INITDIALOG 的响应函数。 当对话框对象收到此消息时,对话框窗口已创建完成,对话 框窗口句柄已经有效,但对话框还没有被显示,对话框窗口 中的控件也已被创建。这意味着在重新定义的 OnInitDialog 中 可以添加一些影响对话框(包括控件)外观的初始化和对话 框构造函数无法完成的初始化工作。OnInitDialog 对对话框的 作用与 OnCreate 对 CMainFrame 对象的作用类似。本例中,首 先使用 ClassWizard 为 CRegisterDialog 添加对 WM_INITDIALOG 消息映射和处理函数 OnInitDialog(注意,在 Visual C+ .NET 中 是通过重载基类的虚函数 OnInitDialog 的方法完成的),然后 手工在 OnInitDialog 中加入对用于工资收入的列表框控件的初 始化代码如下: BOOL CRegisterDialog:OnInitDialog() CDialog:OnInitDialog(); / TODO: Add extra initialization here m_ctrlIncome.AddString(500元以下); m_ctrlIncome.AddString(500-1000元); m_ctrlIncome.AddString(1000-2000元); m_ctrlIncome.AddString(2000元以上); return TRUE; / return TRUE unless you set the focus to a control/ EXCEPTION: OCX Property Pages should return FALSE 5.2.5 信息交换、验证 在对话框操作时,用户只要通过交互方式修改对话框窗口某个控件中的数据信息,就可以修改与该控件关联数值对象的值;反过来只要程序修改了对话框类对象中某个与控件关联数值对象的值,也就可以修改对话框窗口中与该数值对象关联控件中显示的信息。对话框的这种数据交换(DDX)和数据有效验证(DDV)能力是由 MFC 类 CDataExchange 来实现的。 实现这种信息交换操作是由虚函数 CDialog:DoDataExchange完成的。派生类中该函数的重定义版本将在调用该函数的基类版本基础上完成派生类对象所需要的特定的数据交换操作。所幸的是该函数的重定义工作可以借助 ClassWizard 在定义与控件相关的对话框类数据对象的同时自动完成的。例如在本例中:void CRegisterDialog:DoDataExchange(CDataExchange* pDX) CDialog:DoDataExchange(pDX); /AFX_DATA_MAP(CRegisterDialog) DDX_Control(pDX, IDC_INCOME, m_ctrlIncome); DDX_Text(pDX, IDC_AGE, m_nAge); DDV_MinMaxUInt(pDX, m_nAge, 0, 150); DDX_LBString(pDX, IDC_INCOME, m_strIncome); DDX_CBString(pDX, IDC_KIND, m_strKind); DDV_MaxChars(pDX, m_strKind, 15); DDX_Check(pDX, IDC_MARRIED, m_bMarried); DDX_Text(pDX, IDC_NAME, m_strName); DDV_MaxChars(pDX, m_strName, 15); DDX_Radio(pDX, IDC_SEX, m_nSex); DDX_Text(pDX, IDC_UNIT, m_strUnit); DDV_MaxChars(pDX, m_strUnit, 30); DDX_Radio(pDX, IDC_WORK, m_nWork); /AFX_DATA_MAP 由于对话框的数据交换是双向的,所以参数 pDX 所指向的CDataExchange 类对象含有数据的传递方向。实现数据交换不是直接调用 DoDataExchange(因为无法确定数据传递方向),而是调用 CWnd:UpdateData。UpdateData 的原型如下:BOOL UpdateData( BOOL bSaveAndValidate = TRUE );参数:bSaveAndValidate = TRUE 指定数据值从控件传递到数据对象;bSaveAndValidate = FALSE 指定数据值从数据对象传递到控件。该函数调用了 DoDataExchange,显然,参数 bSaveAndValidate 确定了 DoDataExchange 的数据传递方向。5.2.6 对话框的运行在程序中运行模态对话框的步骤: 在栈上构建一个局部对话框类对象。 调用CDialog:DoModal() DoModal 负责模态对话框的创建启动一个消息循环撤消 对话框(接收到 按钮或 按钮控件发出的消 息)。显然,在 DoModal 函数返回之前,不可能进行其他界 面上的操作的。该函数是一个虚函数,意味着可以根据需要 在 CDialog 的派生类中定义特定功能的 DoModal ,但大多数 情况下无须重新定义。 对话框创建时,程序框架会自动调用 OnInitDialog 初始化 对话框,并调用 UpdateData(FALSE) 初始化控件的显示。 对话框撤消时,分为两种情况: 按 按钮,消息处理成员函数 OnOk 被调用。在该函数的执行中,首先调用 UpdateData( TRUE ) 将控件中的数据传递到相应数据对象中保存起来,然后调用 EndDialog 关闭对话框,并返回标志值 IDOK。 按 按钮,消息处理成员函数 OnCancel 被调用,在该函数的执行中,只调用 EndDialog 关闭对话框,并返回标志值 IDCANCEL。 根据 DoModal 返回的标志值,程序就可以知道对话框是如何 结束的(按 或 按钮),从而确定后续操作。 在本例中,为了模态使用对话框,添加了一条菜单命令来触发对话框类对象的创建,对话框的模态创建、显示、操作和根据对话框结束状态的后续操作。1 在菜单资源中的 Edit 弹出式菜单中添加菜单项“登录数据”, 其标识为 ID_EDIT_REGISTER。2 使用 ClassWizard 为菜单项 “登录数据” 在视图类 CRegisterView 中定义命令消息映射项和处理函数 OnEditRegister。3 定义函数 OnEditRegister 实现代码:void CRegisterView:OnEditRegister() / TODO: Add your command handler code here CRegisterDialog dlg; if(dlg.DoModal() = IDOK) CString str; GetWindowText(str);/ 获取视图窗口原有文本内容 str += rn; str += 姓名:; str += dlg.m_strName; str += rn; str += 性别:; str += dlg.m_nSex?女:男; str += rn; str += 年龄:; CString str1; str1.Format(%d, dlg.m_nAge); str += str1; str += rn; str +=婚否:; str +=dlg.m_bMarried?已婚:未婚; str += rn; str += 就业情况:; str+=dlg.m_nWork?下岗:在职; str+=rn; str += 工作单位:; str += dlg.m_strUnit; str += rn; str += 单位性质:; str += dlg.m_strKind; str += rn; str += 工资收入:; str += dlg.m_strIncome; str += rn; SetWindowText(str);/ 在视图窗口中显示新文本 4 在 CRegisterView 的实现文件中加入: #include “RegisterDialog.h”5 编译运行5.2.7 程序的改进 在程序的运行中发现存在这样的缺点:当在“就业情况”组框中选择了单选按钮“下岗”,而“单位情况”组框中的所有控件仍然有效,显然这是不合理的,会引起登录数据的错误。为了克服这一缺点,应该当在“就业情况”组框中选择了单选按钮时,程序能够响应对应的控件消息,在消息处理函数中根据所选按钮的不同,使“单位情况”组框中的相关控件允许或禁止。虽然组框中的单选按钮是两个,而它们的消息处理函数只需要一个,因此应该使用 ON_CONTROL_RANGE 消息映射宏进行消息映射。不幸的是 ClassWizard 不支持 ON_CONTROL_RANGE,所以我们必须按下列步骤手工创建单选按钮的消息映射和消息处理函数。1 在 CRegisterDialog 的定义文件中定义消息处理函数原型: class CRegisterDialog : public CDialog / Implementation protected:/ Generated message map functions /AFX_MSG(CRegisterDialog) virtual BOOL OnInitDialog(); /AFX_MSG afx_msg void OnWorkClicked(UINT nCmdID);DECLARE_MESSAGE_MAP() ;2 在 CRegisterDialog 实现文件的消息映射表中加入映射条目: BEGIN_MESSAGE_MAP(CRegisterDialog, CDialog)/AFX_MSG_MAP(CRegisterDialog)/AFX_MSG_MAP ON_CONTROL_RANGE(BN_CLICKED, IDC_WORK, IDC_WORK1, OnWorkClicked) END_MESSAGE_MAP()3 在 CRegisterDialog 实现文件中添加消息响应函数实现代码: void CRegisterDialog:OnWorkClicked(UINT nCmdID) if(IsDlgButtonChecked(IDC_WORK) / 选中“在职”单选按钮否? / 使“单位情况”组框中的相关控件有效 GetDlgItem(IDC_UNIT)-EnableWindow(TRUE); GetDlgItem(IDC_KIND)-EnableWindow(TRUE); m_ctrlIncome.EnableWindow(TRUE); else / 清除文本框内容,并使之禁止 GetDlgItem(IDC_UNIT)-SetWindowText(); GetDlgItem(IDC_UNIT)-EnableWindow(FALSE);/ 使组合框处于未选择状态,并使之禁止 CComboBox *pComboBox = (CComboBox*)GetDlgItem(IDC_KIND); pComboBox-SetCurSel(-1);pComboBox-EnableWindow(FALSE); / 使列表框为未选和禁止状态 m_ctrlIncome.SetCurSel(-1); m_ctrlIncome.EnableWindow(FALSE); 在对话框中获取控件对象并对其控制操作有两种方法: 直接使用与控件关联的控件对象,本例中对“工资收入”列表 框控件的操作是通过与 CListBox 对象 m_ctrlIncome 进行的。 利用 CWnd 类的一组操作对话框控件的成员函数,本例中对 于“工作单位”文本框和“单位性质”组合框控件的操作就是调 用 CWnd:GetDlgItem(int nID) 返回指向标识值为 nID 的控件对 象的指针,调用代码如下: CEdit *pUnit = (CEdit*)GetDlgItem(IDC_UNIT); CComboBox *pKind = (CComboBox*)GetDlgItem(IDC_KIND); 从而借助该指针对相应的控件进行操作。4 编译运行(应用程序项目“Register1”)5.3 非模态对话框5.3.1 非模态对话框的特点 非模态对话框的设计与模态对话框基本类似,也是由对话框模板资源设计和对话框类设计两部分组成。但在对话框的创建和删除过程中,两类对话框存在下列差异:1 对话框模板的 Visible 属性不同非模态对话框模板应该具有 Visible 属性,否则对话框创建时 将不能自动可见。当然,如果对话框模板没有设置 Visible 属 性,可以在对话框创建后调用 CWnd:ShowWindow( SW_SHOW ) 显示对话框。模态对话框则不必设置 Visible 属性,因为 DoModal 函数隐含 解决了对话框的可见性。2 创建空间不同非模态对话框类对象是使用 new 操作符在堆中动态创建的, 并在该对话框对象的所属类中增加该对话框类指针,用于指 向动态创建的对话框类对象。模态对话框一般是以局部对象的形式构建在堆栈上。3 窗口创建函数不同非模态对话框的创建是调用 CDialog:Create 函数创建的,该函 数不启动新的消息循环,因此,所创建的对话框与应用程序 使用同一个消息循环。模态对话框调用 CDialog:DoModal 完成对话框窗口的创建、启 动消息循环和撤消。4 窗口删除函数不同非模态对话框类必须编写自己的 OnOK 和 OnCancel 函数,以 便在函数中调用 CWnd:DestroyWindow 关闭对话框。模态对话框通过默认的 CDialog:OnOK 和 CDialog:OnCancel 调用 CDialog:EndDialog 关闭对话框。注意,DoModal、OnOk 和 OnCancel 都是虚函数,这意味着如果需要,可以在派生类 中重新定义这些函数的特定版本,完成特定的附加工作。5 清理对话框对象的方式不同非模态对话框对象应在不再使用时,使用 delete 操作符删除 在堆中动态创建的对话框对象。由于一个窗口被删除后,框 架会自动调用虚函数 CWnd:PostNcDestroy,因此可以在该函 数的重定义版本中使用 delete 删除窗口的关联对话框对象。模态对话框类对象是局部的,因此不需要显式删除。6 非模态对话框不能“重入”非模态对话框必须通过对指向对话框对象的指针的检查,来 判别对话框对象是否已经创建,如果未创建,则创建对话框 对象;否则只需激活已经创建的对话框,而不要重新创建。模态对话框是局部对象,每次调用都重新在栈上构建。根据上述两种对话框的不同,修改“Register1”应用程序,将数据登录对话框改为无模式对话框,项目名为“Register2”。修改步骤如下: 修改对话框模板的 Visible 属性有效,并将 和 按钮的显示标题改为 和 。 在 CRegisterView 类定义中加入数据登录对话框类指针: public:CRegisterDialog *m_pRegisterDlg; 注意将 CRegisterDialog 类的头文件包含在 CRegisterView 类的 定义文件中,或在定义对话框指针之前加入超前声明: class CRegisterDialog; 在 CRegisterDialog 类中增加 CWnd 类型的指针数据成员,用 于保存其父窗口对象的指针(以便与父窗口进行联系), 并在构造函数中对该指针进行设置。 protected:CWnd* m_pParent; CRegisterDialog:CRegisterDialog(CWnd* pParent /*=NULL*/) : CDialog(CRegisterDialog:IDD, pParent) /AFX_DATA_INIT m_pParent = pParent; 修改 CRegisterView 类构造函数和消息处理函数: CRegisterView:CRegisterView() / TODO: add construction code here m_pRegisterDlg = NULL; void CRegisterView:OnEditRegister() / TODO: Add your command handler code hereif(m_pRegisterDlg)/ 判断对话框已创建否?m_pRegisterDlg-SetActiveWindow(); / 激活已创建的对话框 else m_pRegisterDlg = new CRegisterDialog(this); / 创建对话框对象m_pRegisterDlg-Create(IDD_REGISTER, this); / 创建窗口 使用 ClassWizard 为 CRegisterDialog 类添加 OnCancel、OnOK 消息响应函数和重载虚函数 PostNcDestroy,并为这两个函数 编写定义代码: void CRegisterDialog:OnCancel() / TODO: Add extra cleanup here m_pParent-SendMessage(WM_DIALOG_DESTROY, 0, 0);/向父窗口发出关闭对话框消息 DestroyWindow();/ 关闭对话框窗口 void CRegisterDialog:OnOK() / TODO: Add extra validation here CString str; UpdateData(); m_pParent-GetWindowText(str); / 获取父窗口中的显示文本 / 在原来文本的基础上添加新文本 str += rn; str += 姓名:; str += m_strName; str += rn; str += 性别:; str += m_nSex?女:男; str += rn; str += 年龄:; CString str1; str1.Format(%d, m_nAge); str += str1; str += rn; str += 婚否:; str += m_bMarried ? 已婚:未婚; str += rn; str += 就业情况:; str += m_nWork ? 下岗:在职; str += rn; str += 工作单位:; str += m_strUnit; str += rn; str += 单位性质:; str += m_strKind; str += rn; str += 工资收入:; str += m_strIncome; str += rn; m_pParent-SetWindowText(str); / 在父窗口中显示添加后的文本 void CRegisterDialog:PostNcDestroy() / TODO: Add your specialized code here and/or call the base class delete this;/ 删除对话框类对象 CDialog:PostNcDestroy(); 对话框关闭时要求其父窗口类对象能及时将指向对话框的指 针设置为 NULL。实现方法和步骤如下: 在 CRegisterDialog 的定义文件中增加用户自定义消息: const WM_DIALOG_DESTROY = WM_USER + 100; 在 CRegisterView 定义中增加消息处理函数声明: afx_msg LRESULT OnDialogDestroy(WPARAM, LPARAM); 在 CRegisterView 实现文件中添加消息响应函数定义代码: LRESULT CRegisterView:OnDialogDestroy( WPARAM wParam, LPARAM lParam ) m_pRegisterDlg = NULL; return 1; 编译运行5.3.2 关于窗口对象的自动清除1 MFC 窗口类对象的组成 一个 MFC 窗口由两部分组成: 描述窗口属性和功能的 C+类 窗口类(CWnd 或CWnd 的派生类)。 Windows 窗口,该窗口的句柄作为一个数据成员 m_hWnd被封装在窗口类中。2 MFC 窗口类对象的创建 首先创建窗口的窗口类对象; 然后调用窗口类的创建函数(例如 CWnd:Create)创建由参数确定的 Windows 窗口,并使之与窗口类对象相关联;或调用Windows 窗口关联函数(例如 CWnd:Attach)使窗口类对象与一个已经存在的 Windows 窗口相关联。3 MFC 窗口类的删除 先调用 CWnd:DestroyWindow 撤消 Windows 窗口,并使窗口类 对象的 Windows 窗口句柄属性 m_hWnd = NULL(表示窗口已 经无效)。一般情况下,该函数不需要直接调用,而是由应 用程序框架自动调用,例如:关闭窗口的 WM_CLOSE 消息、 对话框的 或 按钮发出的消息都会导致该函数 被调用。如果 DestroyWindow 所撤消的窗口拥有子窗口,则 先删除所有的子窗口,然后删除父窗口。 然后删除窗口的窗口类对象。删除窗口类对象分两种情况: 构建在堆栈上的窗口窗口类对象:该窗口类对象是作为某 个类的数据成员或函数中的局部变量。它们的生存期总是 有限的,随着拥有窗口类对象的撤消或函数的返回,被自 动被删除,所以不必关心这类窗口类对象的删除。 创建在堆上的窗口类对象:该窗口类对象是使用运算符 new 创建的,其生存期是任意的,因此,在关闭 Windows窗口后必须使用运算符 delete 从堆中删除窗口类对象,否则不会被自动清除。4 自动清除功能 自动清除功能是指在 Windows 窗口被删除时,自动删除窗口 类对象。这是因为当 CWnd:DestroyWindow 被调用撤消一个 Windows 窗口时,与该窗口关联的窗口类对象的成员函数 PostNcDestroy 被调用。该函数是一个虚函数,MFC 所提供的 有些 CWnd 的派生类重定义了该函数,在新版本函数中通过 代码 delete this 来删除与被撤消窗口关联的窗口类对象本身, 因此不需要显式使用 delete 删除窗口类对象,即自动清除功 能。具有自动清除功能的 MFC 窗口类有: 主框架窗口类(基类为 CFrameWnd ) 视图类(基类为 CView) 这些窗口类的对象在应用程序中一般都是动态在堆中创建 的。而对于其他 MFC 窗口类的使用,由于大多数情况下是在 堆栈中创建的。所以 MFC 没有为这些类提供自动清除功能。 这些窗口类包括: 所有标准的 Windows 控件类 从 CWnd 直接派生的自定义窗口类(如用户定制的控件) 切分窗口类 CSplitterWnd 默认的控制栏类(如工具栏和状态栏) 对话框类(从 CDialog 派生) 由此可见,要不要为自己的派生窗口类添加自动清除功能, 取决于所派生的窗口类的创建方法,例如,我们前面的非模 式对话框的实例中,由于对话框是在堆中创建的,要求其生 存期任意,所以需要为它添加了自动清除功能。否则我们必 须在对话框窗口被删除后,在对话框类对象的拥有者 (父窗 口类对象) 中的适当位置(如析构函数中),调用 delete 显式删 除对话框类对象。5.4 创建属性表对话框 属性表是一种包含了多个对话框的对话框框架,这些包含在属性表中的对话框通常被称为属性页(Page)。每次只有一个属性页是可见的,在属性表的顶端有一行标签(tab)用于指示属性表中不同的属性页,用户通过单击这些标签可以切换到不同的属性页。 使用属性表可以按操作功能将控件分类放在不同的属性页中,便于组织和构造需要大量控件的复杂对话框,也使得对话框的使用界面更加友好。5.4.1 属性表对话框的创建 属性表是由多个属性页和一个能放置这些属性页,并且将这些属性页关联起来的对话框框架组成的,就像是一个夹有多页文件的文件夹。为了支持属性表,MFC 提供了两个类:CPropertySheet :用于创建属性表框架和提供相应的属性和 操作。它的直接基类是 CWnd,使该类在窗口的属性和功能的 基础上增加了对属性页的创建、激活、切换、数据交换、撤 消等功能。CPropertyPage :用于创建属性表的属性页和提供相应的属性 和操作。它的直接基类是 CDialog,使该类在对话框的属性和 功能的基础上增加了对属性表框架各项管理的响应操作。 与普通对话框相比,属性表的设计和实现既有许多相似之处,又有一些不同。创建一个属性表一般包括以下几个步骤:1 创建属性页(对话框)资源 创建所有属性页的资源,每个属性页的资源都是一个对话框 模板。与创建普通对话框模板相比应注意以下几点: 一般需要删除缺省的 和 按钮; 每页的模板尺寸最好相同,如果不同,则在属性表创建时,系统会按最大的模板尺寸统一创建所有页的尺寸; 指定标题(Caption)的内容将成为属性表中相应属性页的指示标签内容; 属性页模板属性设定为 TitleBar、Child、ThinBoder 和 Disable 。2 生成属性页类 使用 ClassWizard 为每个属性页模板定义相应的属性页类,基 类必须选择 CPropertyPage。3 在属性页类中为属性页中的控件添加关联的数据对象 与普通对话框一样,使用 ClassWizard 的Member Variables 属性页为每个属性页模板中的控件在相应的属性页类中添加 所需要的数据对象。4 创建属性表创建属性表的方法两种: 直接使用 CPropertySheet 类创建 在需要使用属性表的码段中加入相应的创建代码。例如: void CMyView:DoModalPropertySheet() CPropertySheet propsheet; / 创建属性表框架CMyFirstPage pageFirst; / 创建第一属性页CMySecondPage pageSecond; / 创建第二属性页pageFirst.m_nMember1 = m_nMember1; / 初始化第一属性页pageFirst.m_nMember2 = m_nMember2; / 初始化第一属性页pageSecond.m_nMember3 = m_nMember3; / 初始化第二属性页pageSecond.m_nmember4 = m_nMember4; / 初始化第二属性页propsheet.AddPage(&pageFirst); / 在属性表中添加第一属性页propsheet.AddPage(&pageSecond); / 在属性表中添加第二属性页if(propsheet.DoModal() = IDOK) / 模态运行属性页对话框m_nMember1 = pageFirst.m_nMember1;m_nMember2 = pageFirst.m_nMember2;m_nMember3 = pageSecond.m_nMember3;m_nMember4 = pageSecond.m_nMember4; 使用 CPropertySheet 类的派生类创建 一般步骤如下: 使用 ClassWizard 定义一个 CPropertySheet 类的派生类; 将所有属性页类对象以数据成员的形式加入到此派生类的 定义中; 在此派生类构造函数中调用 CPropertySheet:AddPage 函数将各个属性页添加到属性表中,使得在属性表对象的创建操作自动包含了属性页的添加操作。 在需要使用属性表的地方,使用此派生的属性表类创建特 定的属性表。5.4.2 属性表的运行机制1 属性表初始化包括属性表框架的初始化和属性页的初始化。 如果需要进行自定义初始化操作,属性表框架初始化应在重 定义的虚函数 CPropertySheet:OnCreate 中完成;而属性页初始 化应在重定义的虚函数 CPropertyPage:OnInitDialog 中完成。2 属性表的使用者一般调用 CPropertySheet:DoModal ,按模态运 行方式创建属性表,并可通过判断 DoModal 的返回值(IDOK 或 IDCANCEL),确定关闭属性表的操作方式是 “确认” 还是 “放弃”,从而确定后续操作。3 模态运行的属性表会自动在属性表的底部提供三个按钮,依 次为 或 、 或 和 或 按钮(非模态运行的属性表不提供这些按钮)。其中: 或 按钮的作用是确认当前属性页的操作,并关闭整个属性表。 或 按钮的作用是放弃当前属性页的操作,关闭整个属性表。 通过标签的属性页切换操作将确认被切换属性页的操作。 或 按钮的作用是确认当前属性页操作,并使属性表的所有操作生效,但不关闭该属性表。 对这三个按钮作出处理的响应函数可由 CPropertyPage 类提供 的消息响应函数 OnOK、OnCancel 和 OnApply (OnApply 会自 动调用 OnOK)完成。因此,我们可以在属性页派生类中重定 义这三个消息响应函数完成所需要的特定操作。 应当指出:对属性页的这三个消息响应函数的调用并非直接 响应属性表的三个按钮的通知消息 BN_CLICKED 的结果,而 是通过 CPropertySheet 类或派生类对这三个按钮的消息响应函 数,间接调用上述 CPropertyPage 的三个消息响应函数完成的。 例如,定义响应 按钮的通知消息,完成特定的处理 可以有两种方法: 使用 ClassWizard 为属性表中任意一个属性页类重定义虚 函数 OnApply,在该函数中定义要实现的特定功能代码。 一般情况下所定义的功能将对整个属性表起作用,所以 只须在一个属性页中重定义 OnApply 即可,不必为每个属 性页都重定义 OnApply。 在 CPropertySheet 的派生类中添加 按钮的通知消 息 BN_CLICKED 的消息映射和响应函数,在该函数中定义 想要实现的操作。注意,系统为 按钮定义的标识 为 ID_APLLY_NOW,但在VC 6.0 中 ClassWizard 无法识别该 标识 ,因此上述添加操作只能手工完成。4 CPropertyPage 类中几个与运行机制相关的重要成员函数: void SetModified( BOOL bChanged = TRUE ); 该函数用于设置属性页的修改标志。参数bChange 为 TRUE 表明属性页的属性已修改,否则表示未修改。调用该函数的 主要功能是“允许”或“禁止” 按钮: 只要在一个属性页中调用了 SetModified(),就会使 按钮的状态变为“允许”; 必须在所有的属性页中调用 SetModified(FALSE),才能使 按钮的状态变为“禁止”。 virtual BOOL OnSetActive( ); 当属性页被激活或被创建时,该函数将被程序框架调用。该 函数的缺省操作是: BEGIN if(属性页创建否?)then 激活已创建的属性页 else 创建属性页 endif 调用 UpdateData(FALSE) 刷新属性页内的控件。 END 可以重定义该虚函数完成一些所需要的工作,但执行这些特 定操作之前必须首先调用 CPropertyPage:OnSetActive 。 virtual BOOL OnKillActive( ); 当切换属性页时,被切换的属性页会调用该函数。该函数的 缺省操作是调用 UpdateData(TRUE) 保存数据。可以重定义该 虚函数完成一些特定数据的有效检查和操作。该函数的成功 返回将导致程序框架调用 OnOK 函数。 注意: 在属性表的一次调用中,表中的属性页并不一定都被创建 过。那些从未打开过的属性页就不会被创建。 只有确定属性页已经存在,才能调用与属性页和控件相关 的函数。如果收到了控件通知消息或 OnSetActive 函数被调 用,则表明属性页已经创建。 属性页之间的数据交换只能在 OnSetActive 和 OnKillActive 函 数中进行。5.4.3 属性表的具体实例 创建一个属性表应用程序项目“Register3Register3” ,在该项目中将“RegisterRegister” 应用程序对话框中的两组控件 “个人情况” 和 “单位情况” 分解为属性表的两个属性页。创建属性表步骤如下:1 创建第一个属性页 创建属性页资源 生成新对话框模板 IDD_PERSONAL; 去掉缺省的 和 按钮; 确定模板标题为 “个人信息” 作为该属性页的 “标签”; 设置模板属性:TitleBar、Child、ThinBoder 和 Disable; 添加所需要的控件:本例可以复制前例中对话框模板中的相应的控件。控件类型控件标识显示标题其他属性静态文本缺省姓名缺省文本框IDC_NAME缺省复选框IDC_MARRIED婚否缺省静态文本缺省年龄缺省文本框IDC_AGE缺省组框缺省性别缺省单选按钮IDC_SEX男Group、Tab stop单选按钮IDC_SEX1女缺省组框缺省就业情况 缺省单选按钮IDC_WORK在职Group、Tab stop单选按钮IDC_WORK1下岗缺省 定义属性页类 CPersonalPage 使用 ClassWizard 为 IDD_PERSONAL 定义 CPropertyPage 派 生类 CPersonalPage。 使用 ClassWizard ,为各控件添加对应的数据对象:控件 ID数据对象类型数据对象名IDC_AGEIDC_MARRIEDIDC_NAMEIDC_SEXIDC_WORK UINTBOOLCStringintint m_nAgem_bMarriedm_strNamem_nSexm_nWork 手工添加消息映射和处理函数: 在类定义文件中加入消息响应函数声明:afx_msg void OnChangeProperties();/处理相应控件的 BN_CLICKED 消息afx_msg void OnChangeProperties1();/处理相应控件的 EN_CHANGE 消息 在类实现文件的消息映射表中加入:ON_CONTROL_RANGE( BN_CLICKED,IDC_MARRIED,IDC_WORK1, OnChangeProperties )ON_CONTROL_RANGE( EN_CHANGE, IDC_NAME, IDC_AGE, OnChangeProperties1 ) 在类实现文件中加入消息响应函数的定义代码:void CPersonalPage:OnChangeProperties() SetModified(TRUE);/ 使能 Apply 按钮 UpdateData(TRUE);/ 确认数据设置void CPersonalPage:OnChangeProperties1() SetModified(TRUE); / 使能 Apply 按钮 UpdateData(TRUE); / 确认数据设置2 创建第二个属性页 创建属性页资源 生成新对话框模板 IDD_UNIT; 去掉缺省的 和 按钮; 确定模板标题为 “单位信息” 作为该属性页的 “标签”; 设置模板属性:TitleBar、Child、ThinBoder 和 Disable; 添加所需要的控件:本例可以复制前例中对话框模板中的相应控件。控件类型控件标识显示标题其他属性静态文本缺省工作单位缺省文本框IDC_UINT缺省静态文本缺省单位类型缺省组合列表框IDC_KIND下拉列表、不排序、初始化列表项。静态文本缺省工资收入缺省列表框IDC_INCOME不排序 定义属性页类 CUnitPage 使用 ClassWizard 为 IDD_UNIT 创建 CPropertyPage 的派生类 CUnitPage。 使用 ClassWizard 在类定义中,为各控件添加对应的数据对 象:控件 ID数据对象类型数据对象名IDC_INCOMEIDC_INCOMEIDC_KINDIDC_UINTCStringCListBoxCStringCStringm_strIncomem_ctrlIncomem_strKindm_strUnit 使用 ClassWizard 添加消息映射和处理函数: 在类定义文件中加入消息响应函数声明:afx_msg void OnChangeUnit();afx_msg void OnSelchangeIncome();afx_msg void OnEditchangeKind();afx_msg void OnSelchangeKind(); 在类实现文件的消息映射表中加入:ON_EN_CHANGE(IDC_UNIT, OnChangeUnit)ON_LBN_SELCHANGE(IDC_INCOME, OnSelchangeIncome)ON_CBN_EDITCHANGE(IDC_KIND, OnEditchangeKind)ON_CBN_SELCHANGE(IDC_KIND, OnSelchangeKind) 在类实现文件中加入处理函数的定义代码:void CUnitPage:OnChangeUnit() SetModified(TRUE); UpdateData(TRUE);void CUnitPage:OnSelchangeIncome() SetModified(TRUE); UpdateData(TRUE);void CUnitPage:OnEditchangeKind() SetModified(TRUE); UpdateData(TRUE);void CUnitPage:OnSelchangeKind() SetModified(TRUE); UpdateData(TRUE); 使用 ClassWizard 添加窗口消息 WM_INITDIALOG 的响应函数 OnInitDialog,并加入如下代码,为列表框进行初始化: BOOL CUnitPage:OnInitDialog() CPropertyPage:OnInitDialog();/ TODO: Add extra initialization herem_ctrlIncome.AddString(500元以下);m_ctrlIncome.AddString(500-1000元);m_ctrlIncome.AddString(1000-2000元);m_ctrlIncome.AddString(2000元以上);return TRUE; / return TRUE unless you set the focus to a control / EXCEPTION: OCX Property Pages should return FALSE 重定义虚函数 OnSetActive 用于从 “个人情况” 属性页切换到 “单位情况” 属性页时的就业情况判断: BOOL CUnitPage:OnSetActive() / TODO: Add your specialized code here and/or call the base classBOOL result = CPropertyPage:OnSetActive();if(result = 0) return result;if(CRegisterSheet*)GetParent()-m_PersonalPage.m_nWork=0) / 在职 GetDlgItem(IDC_UNIT)-EnableWindow(TRUE); GetDlgItem(IDC_KIND)-EnableWindow(TRUE); m_ctrlIncome.EnableWindow(TRUE);else / 下岗 GetDlgItem(IDC_UNIT)-SetWindowText(); GetDlgItem(IDC_UNIT)-EnableWindow(FALSE); m_strUnit = ; CComboBox *pComboBox = (CComboBox*)GetDlgItem(IDC_KIND); pComboBox-SetCurSel(-1); pComboBox-EnableWindow(FALSE); m_strKind = ; m_ctrlIncome.SetCurSel(-1); m_ctrlIncome.EnableWindow(FALSE); m_strIncome = ;return result; 注意:需要在此实现文件添加包含头文件代码: #include “RegisterSheet.h” 定义 CRegisterSheet 创建 CPropertySheet 的派生类 CRegisterSheet。 在类定义文件中添加属性页对象: public:CPersonalPage m_PersonalPage;CUnitPage m_UnitPage; 注意:需要在此定义文件中添加头文件包含代码: #include “PersonalPage.h” #include “UnitPage.h” 修改类构造函数: CRegisterSheet:CRegisterSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage): CPropertySheet(nIDCaption, pParentWnd, iSelectPage) AddPage(&m_PersonalPage);AddPage(&m_UnitPage); CRegisterSheet:CRegisterSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage): CPropertySheet(pszCaption, pParentWnd, iSelectPage) AddPage(&m_PersonalPage);AddPage(&m_UnitPage); 3 自定义消息 WM_USER_OUTPUT 处理 当属性表对话框的 被按下时,程序应向视图发送该 自定义消息,视图类对应的响应函数能根据属性表当前的数 据设置,在视图中显示相应的文本。实现这些操作需要进行 以下编码: 在 CRegisterApp 定义文件中添加自定义消息的定义: const WM_USER_OUTPUT = WM_USER+200; 在 CRegisterSheet 定义文件添加 按钮单击消息的响 应函数: afx_msg void OnApplyNow(); 在 CRegisterSheet 实现文件添加 按钮单击消息的响 应函数定义代码: void CRegisterSheet:OnApplyNow() CFrameWnd* pFrameWnd = (CFrameWnd*)AfxGetMainWnd(); CView* pView=pFrameWnd-GetActiveFrame()-GetActiveView(); pView-SendMessage(WM_USER_OUTPUT, (WPARAM)this); m_PersonalPage.SetModified(FALSE); m_UnitPage.SetModified(FALSE); 并在消息映射表中加入 按钮的单击消息映射: ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow) 在 CRegisterView 定义文件中添加自定义消息响应函数声明: afx_msg LRESULT OnOutput(WPARAM, LPARAM); 在 CRegisterView实现文件的消息映射表中添加映射条目: ON_MESSAGE(WM_USER_OUTPUT, OnOutput) 在 CRegisterView 实现文件中添加 OnOutput 函数定义代码: LRESULT CRegisterView:OnOutput(WPARAM wParam, LPARAM lParam) CRegisterSheet *pSheet = (CRegisterSheet*)wParam; CString str, str1 = “”, str2; GetWindowText(str);str1 += rn;str1+=姓名:; str1+=pSheet-m_PersonalPage.m_strName; str1+=rn; str1+= 性别:; str1 += pSheet-m_PersonalPage.m_nSex?女:男; str1 += rn;str1 += 年龄:;str2.Format(%d, pSheet-m_PersonalPage.m_nAge); str1 += str2; str1 += rn; str1 += 婚否:; str1 += pSheet-m_PersonalPage.m_bMarried?已婚:未婚; str1 += rn;str1 += 就业情况:; str1 += pSheet-m_PersonalPage.m_nWork?下岗:在职; str1 += rn;str1 += 工作单位:; str1 += pSheet-m_UnitPage.m_strUnit; str1 += rn; str1 += 单位性质:; str1+= pSheet-m_UnitPage.m_strKind; str1 += rn; str1 += 工资收入:; str1 += pSheet-m_UnitPage.m_strIncome; str1 += rn;if(str.Find(str1) = -1)/ 判断当前人员信息是否已经登录str += str1; SetWindowText(str); return 0; 4 定义菜单命令和响应函数 在 Edit 菜单中添加标识为 ID_EDIT_PROPDLG 的菜单项“属性 表对话框” 。并为该菜单项添加响应函数 OnPropdlg 以便使用 属性表对话框。 在 CRegisterView 实现文件添加头文件包含代码: #include “RegisterSheet.h” 在CRegisterView:OnEditPropdlg 添加定义代码: void CRegisterView:OnEditPropdlg() / TODO: Add your command handler code hereCRegisterSheet RegisterSheet(登录);if(RegisterSheet.DoModal() = IDOK) OnOutput(WPARAM)&RegisterSheet, 0); 5 编译运行(应用程序“Register3”)5.5 创建向导 向导是一种特殊的属性表。它的各个属性页必须按照顺序切换显示和操作,不允许像普通属性表那样,可以任意选择任何属性页的显示和操作,但允许随时结束和退出向导操作。这种强制性的切换顺序对于指导用户如何按步骤完成一个复杂任务是十分必要的。为此向导提供了 或 、 或 、 或 和 或 按钮替代了普通属性表的 或 、 或 和 或 按钮。下面我们通过创建一个应用程序项目“Register4” 描述如何创建一个向导。5.5.1 创建向导的属性页1 创建向导的第一属性页 创建属性页对话框模板 IDD_PAGE1,用于登录个人信息。显 示标题为“登录:三页之一”、模板属性和所加入的控件与前 例中属性页模板 IDD_PERSONAL 相同。 定义 CPage1 类 用 ClassWizard 创建与模板 IDD_PAGE1 关联的 CPropertyPage 派生类 CPage1。 用 ClassWizard 为 CPage1 类添加与控件关联的数据对象, 这些数据对象的个数和命名与前例 CPersonalPage 相同。2 创建向导的第二属性页 创建属性页对话框模板 IDD_PAGE2,用于登录单位信息。显 示标题为“登录:三页之二”、模板属性和所添加的控件与前 例中属性页模板 IDD_UNIT 相同。 定义 CPage2 类 用 ClassWizard 创建与模板 IDD_PAGE2 关联的 CPropertyPage 派生类 CPage2。 用 ClassWizard 为 CPage2 类添加与控件对应的数据对象, 这些数据对象的个数和命名与前例中的 CUnitPage 相同。 用 ClassWizard 为 CPage2 添加窗口消息 WM_INITDIALOG 的 响应函数 OnInitDialog: BOOL CPage2:OnInitDialog() CPropertyPage:OnInitDialog();/ TODO: Add extra initialization herem_ctrlIncome.AddString(500元以下);m_ctrlIncome.AddString(500-1000元);m_ctrlIncome.AddString(1000-2000元);m_ctrlIncome.AddString(2000元以上);return TRUE; / return TRUE unless you set the focus to a control / EXCEPTION: OCX Property Pages should return FALSE 3 创建向导的第三属性页 创建属性页对话框模板 IDD_PAGE3,用于显示和确认已经登 录的信息。显示标题为“登录:三页之三”、其属性与其他属 性页相同,并添加一个静态文本框控件。由于该控件需要在 程序中动态确定在控件中的显示文本,因此需要将控件标识 修改为 IDC_AFFIRMANCE。 定义 CPage3 类 用 ClassWizard 创建与模板 IDD_PAGE3 关联的 CPropertyPage 派生类 CPage3。5.5.2 创建向导属性表1 用 ClassWizard 创建 CPropertySheet 派生类 CWizardSheet。 在 CWizardSheet 定义文件中添加属性页对象作为数据成员: CPage1 m_page1; CPage2 m_page2; CPage3 m_page3; 注意,在 CPropertySheet 类的定义文件中包含相应属性页类的 头文件。2 修改类构造函数: CWizardSheet:CWizardSheet(UINT nIDCaption,CWnd* pParentWnd, UINT iSelectPage) : CPropertySheet(nIDCaption, pParentWnd, iSelectPage) AddPage(&m_page1); AddPage(&m_page2); AddPage(&m_page3); CWizardSheet:CWizardSheet(LPCTSTR pszCaption,CWnd* pParentWnd, UINT iSelectPage) : CPropertySheet(pszCaption, pParentWnd, iSelectPage) AddPage(&m_page1); AddPage(&m_page2); AddPage(&m_page3); 5.5.3 显示向导1 在 Edit 菜单中添加标识为 ID_EDIT_WIZARD 的菜单项,其菜单 名为“向导(&W)” 。2 用 ClassWizard 为 CRegisterView 添加菜单消息映射和消息响应 函数: 在 CRegisterView 定义文件中添加了: afx_msg void OnEditWizard(); /AFX_MSG 在 CRegisterView 实现文件的映射表中添加了: ON_COMMAND(ID_EDIT_WIZARD, OnEditWizard) /AFX_MSG_MAP在 CRegisterView 实现文件中添加 OnEditWizard 定义代码: void CRegisterView:OnEditWizard() / TODO: Add your command handler code hereCWizardSheet wizsheet(登录向导);wizsheet.SetWizardMode();if(wizsheet.DoModal() = ID_WIZFINISH) CString str; GetWindowText(str); str += rn; str+=姓名:; str+=wizsheet.m_page1.m_strName; str+=rn; str += 性别:; str += wizsheet.m_page1.m_nSex?女:男; str += rn; str += 年龄:; CString str1; str1.Format(%d, wizsheet.m_page1.m_nAge); str += str1; str += rn; str+=婚否:; str+=wizsheet.m_page1.m_bMarried?已婚:未婚; str += rn; str+=就业情况; str+=wizsheet.m_page1.m_nWork?下岗:在职; str += rn; str+=工作单位:; str+=wizsheet.m_page2.m_strUnit; str+=rn; str+=单位性质:; str+=wizsheet.m_page2.m_strKind; str+=rn; str+=工资收入:; str+=wizsheet.m_page2.m_strIncome; str+=rn; SetWindowText(str); 注意在实现文件添加头文件包含代码:#include “WizardSheet.h”5.5.4 设置向导的按钮 使用 ClassWizard 为各属性页重定义虚函数 OnSetActive ,用于设置向导属性页的相应导向按钮:第一属性页: BOOL CPage1:OnSetActive() BOOL result = CPropertyPage:OnSetActive(); if(result) CPropertySheet *parent = ( CPropertySheet* )GetParent(); parent-SetWizardButtons( PSWIZB_NEXT ); return result; 其中 SetWizardButtons 函数的实参为设置按钮允许的标志。这 些标志包括:PSWIZB_BACK、PSWIZB_NEXT、PSWIZB_FINISH 和 PSWIZB_DISABLEFINISH。可以根据属性页的不同采用这些 标志的不同组合。第二属性页: BOOL CPage2:OnSetActive() BOOL result = CPropertyPage:OnSetActive(); if( result = 0 ) return result; CWizardSheet *parent = ( CWizardSheet* )GetParent(); parent-SetWizardButtons( PSWIZB_NEXT | PSWIZB_BACK ); if(parent-m_page1.m_nWork = 0)/ 判断登录人员在职否? GetDlgItem( IDC_UNIT )-EnableWindow( TRUE );GetDlgItem( IDC_KIND )-EnableWindow( TRUE );m_ctrlIncome.EnableWindow( TRUE ); else GetDlgItem( IDC_UNIT )-SetWindowText( );GetDlgItem( IDC_UNIT )-EnableWindow( FALSE );m_strUnit = ;CComboBox *pComboBox = ( CComboBox* )GetDlgItem( IDC_KIND );pComboBox-SetCurSel( -1 );pComboBox-EnableWindow( FALSE );m_strKind = ;m_ctrlIncome.SetCurSel( -1 );m_ctrlIncome.EnableWindow( FALSE );m_strIncome = ; return result; 在CPage2 实现文件中添加包含命令:#include WizardSheet.h第三属性页: BOOL CPage3:OnSetActive() BOOL result = CPropertyPage:OnSetActive(); if(result) CPropertySheet *parent = ( CPropertySheet* )GetParent();parent-SetWizardButtons( PSWIZB_FINISH | PSWIZB_BACK );/ 显示正在登录人员的信息CString str = 登录人员信息:rn;str += 姓名:; str +=(CWizardSheet*)parent)-m_page1.m_strName; str+=rn;str += 性别:; str += (CWizardSheet*)parent)-m_page1.m_nSex ? 女rn:男rn; str += 年龄:;CString str1;str1.Format(%d, (CWizardSheet*)parent)-m_page1.m_nAge);str += str1; str += rn;str += 婚否:; str += (CWizardSheet*)parent)-m_page1.m_bMarried ? 已婚rn:未婚rn; str += 就业情况:; str += (CWizardSheet*)parent)-m_page1.m_nWork ? 下岗rn:在职rn; str += 工作单位:; str += (CWizardSheet*)parent)-m_page2.m_strUnit; str += rn;str += 单位性质:; str += (CWizardSheet*)parent)-m_page2.m_strKind; str += rn;str += 工资收入:; str += (CWizardSheet*)parent)-m_page2.m_strIncome; str += rn;SetDlgItemText( IDC_AFFIRMANCE, str ); return result; 在Page3.cpp 中添加头文件包含代码:#include WizardSheet.h5.5.5 响应向导按钮 使用 ClassWizard 可为各属性页重定义虚函数 OnWizardBack 、OnWizardNext 和 OnWizardFinish,响应向导表的相应按钮消息。第一属性页: LRESULT CPage1:OnWizardNext() UpdateData( TRUE ); if( m_strName = | m_nSex = -1 | m_nWork = -1 ) AfxMessageBox( 姓名不能为空,性别和就业情况必须选择! );return -1; return CPropertyPage:OnWizardNext(); 第二属性页: LRESULT CPage2:OnWizardNext() UpdateData( TRUE ); CWizardSheet *parent = ( CWizardSheet* )GetParent(); if( parent-m_page1.m_nWork = 0 &( m_strUnit = | m_strKind = | m_strIncome = ) ) AfxMessageBox( 在职人员的各项信息均不能为空! );return -1; return CPropertyPage:OnWizardNext(); 编译运行(应用程序“Register4”)5.6 几种重要控件的使用5.6.1 RichEdit 控件(MFC类:CRichEditCtrl)1 控件的创建 直接创建法 在任何 CWnd 或 CWnd 派生类对象中调用 CRichEditCtrl 的成员 函数 Create 直接创建 RichEdit 控件,该函数原型如下: BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 调用实例: #define IDC_RICHEDIT 1000 / 定义要创建的RichEdit 控件的标识值ID CRichEditCtrl m_richEdit;/ 定义RichEdit 控件的CRichEditCtrl 对象 m_richEdit.Create( WS_CHILD | WS_VISIBLE | WS_BORDER |ES_AUTOVSCROLL | ES_MULTILINE, CRect(180, 260, 393, 360), this, IDC_RICHEDIT); / 创建控件 间接创建法创建控件资源:在对话框模板中安放 RichEdit 控件,并为该 控件设定标识(例如 IDC_RICHEDIT)。该控件对象将在所属 对话框对象的创建的同时被创建。 注意,一般在 RichEdit 控件属性设置如下:引用控件对象:使用 ClassWizard 的 属性页 为 RichEdit 控件资源声明关联的对象成员(例如 m_richEdit) 或在需要使用该控件对象时调用 CWnd 的成员函数 GetDlgItem 获得对话框中控件对象的指针,即可直接或间接引用 RichEdit 控件对象。GetDlgItem的原型如下: CWnd* GetDlgItem( int nID ) const; void CWnd:GetDlgItem( int nID, HWND* phWnd ) const; 调用实例: CRichEditCtrl *pRichedit = (CRicheditCtrl*)GetDlgItem(ID_RICHEDIT);2 控件的初始化 在应用程序类的 InitInstance 成员函数的开始位置添加 RichEdit 控件的初始化函数: AfxInitRichEdit();3 控件的使用 通过 CRichEditCtrl 类对象或指向类对象的指针,调用该类的成 员函数,在 CRichEditCtrl 类对象所关联的 RichEdit 控件中实现 所需要的功能操作。5.6.2 列表控件(MFC类:CListCtrl)1 控件的创建 直接创建法 在任何 CWnd 或 CWnd 派生类对象中调用 CListCtrl 的成员函 数 Create 直接创建列表控件,该函数原型如下: BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 调用实例: #define IDC_LISTVIEW 1000 / 定义要创建的列表控件的标识值ID CListCtrl m_listView; / 定义列表控件的CListCtrl 对象 m_listView.Create(WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT | LVS_NOSORTHEADER | LVS_EDITLABELS, CRect(160, 120, 394, 220), this, IDC_LISTVIEW); / 创建列表控件 间接创建法创建控件资源:在对话框模板中安放列表控件资源,并为该 资源设定标识(例如,IDC_LISTVIEW)。该控件对象将在所 属对话框对象的创建的同时被创建。 注意,一般在列表控件的属性一般设置如下:引用控件对象:使用 ClassWizard 的 属性页 为列表控件资源声明关联的对象成员(例如 m_listView)或在 需要使用该控件对象时调用 CWnd 的成员函数 GetDlgItem 获 得对话框中控件对象的指针,即可直接或间接引用列表控件 对象。 调用实例: CListCtrl *pListView = (CListCtrl*)GetDlgItem(ID_LISTVIEW);2 控件的初始化创建图标列表使该图标列表与列表控件关联。图标列表的 MFC类是 CImageList,调用 CImageList:Create 创建图标列表; 再调用 CImageList:Add 将图标(资源)加入到图标列表中; 最后调用 CListCtrl:SetImageList 将所创建的图标列表与列表控 件对象关联。上述操作所涉及的成员函数原型如下: BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow ); BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask ); BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask ); BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2, int nImage2, int dx, int dy ); BOOL Create( CImageList* pImageList ); int Add( CBitmap* pbmImage, CBitmap* pbmMask ); int Add( CBitmap* pbmImage, COLORREF crMask ); int Add( HICON hIcon ); CImageList* SetImageList( CImageList* pImageList, int nImageList ); 调用实例:假设在使用列表控件的类中已经定义了 CListCtrl 对象成员 m_listView 和 CImageList 对象成员 m_smallImageList 以及 m_largeImageList,则典型的初始化操作如下: m_smallImageList.Create( 16, 16, ILC_COLOR4, 1, 0 ); m_largeImageList.Create( 32, 32, ILC_COLOR4, 1, 0 ); HICON hIcon = :LoadIcon ( AfxGetResourceHandle(),MAKEINTRESOURCE( IDI_ICON1 ) );/ IDI_ICON1图标资源ID m_smallImageList.Add( hIcon ); hIcon = :LoadIcon ( AfxGetResourceHandle(),MAKEINTRESOURCE( IDI_ICON2 ) );/ IDI_ICON2图标资源ID m_largeImageList.Add( hIcon ); m_listView.SetImageList( &m_smallImageList, LVSIL_SMALL ); m_listView.SetImageList( &m_largeImageList, LVSIL_NORMAL ); 注意,如果列表控件中不使用图标,上述初始化可以忽略。 为列表控件创建列(Column)。调用 CListctrl:InsertColumn 为 列表控件创建列。该成员函数的原型如下: int InsertColumn( int nCol, const LVCOLUMN* pColumn ); int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1 ); 调用实例: 第一种形式: LV_COLUMN lvColumn;/ 定义列结构变量 lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM ; lvColumn.fmt = LVCFMT_CENTER; lvColumn.cx = 75; lvColumn.iSubItem = 0; lvColumn.pszText = Column 0; m_listView.InsertColumn( 0, &lvColumn ); lvColumn.iSubItem = 1; lvColumn.pszText = Column 1; m_listView.InsertColumn(1, &lvColumn); lvColumn.iSubItem = 2; lvColumn.pszText = Column 2; m_listView.InsertColumn( 2, &lvColumn ); 第二种形式: m_listView.InsertColumn( 0, Column 0, LVCFMT_CENTER, 75, 0 ); m_listView.InsertColumn( 0, Column 1, LVCFMT_CENTER, 75, 1 ); m_listView.InsertColumn( 0, Column 2, LVCFMT_CENTER, 75, 2 ); 为列表控件加入表项(Item)。调用 CListctrl:InsertItem 为列表 控件加入表项。该成员函数的原型如下: int InsertItem( const LVITEM* pItem ); int InsertItem( int nItem, LPCTSTR lpszItem ); int InsertItem( int nItem, LPCTSTR lpszItem, int nImage ); int InsertItem( UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam ); 调用实例:(常用形式) LV_ITEM lvItem;/ 定义表项结构变量 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE; lvItem.state = 0; lvItem.stateMask = 0; lvItem.iImage = 0; lvItem.iItem = 0; lvItem.iSubItem = 0; lvItem.pszText = Item 0; m_listView.InsertItem( &lvItem ); m_listView.SetItemText( 0, 1, Sub Item 0.1 ); m_listView.SetItemText( 0, 2, Sub Item 0.2 ); lvItem.iItem = 1; lvItem.pszText = Item 1; m_listView.InsertItem( &lvItem ); m_listView.SetItemText( 1, 1, Sub Item 1.1 ); m_listView.SetItemText( 1, 2, Sub Item 1.2 ); lvItem.iItem = 2; lvItem.pszText = Item 2; m_listView.InsertItem( &lvItem ); m_listView.SetItemText( 2, 1, Sub Item 2.1 ); m_listView.SetItemText( 2, 2, Sub Item 2.2 );3 控件的使用 通过 CListCtrl 类对象或指向类对象的指针,调用该类的成员 函数,在 CListCtrl 类对象所关联的列表控件中实现所需要的 功能操作。5.6.3 树型表控件(MFC类:CTreeCtrl)1 控件的创建 直接创建法 在任何 CWnd 或 CWnd 派生类对象中调用 CTreeCtrl:Create 直 接创建树型表控件,该函数原型如下: BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); 调用实例: #define IDC_TREEVIEW 1000 / 定义要创建的树型表控件的标识值ID CTreeCtrl m_treeView; / 定义树型表控件的 CTreeCtrl 对象 m_treeView.Create( WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_EDITLABELS, CRect(20, 260, 160, 360), this, IDC_TREEVIEW); 间接创建法创建控件资源:在对话框模板中安放树型表控件资源,并为 该资源设定标识(例如 IDC_TREEVIEW)。该控件对象将在 所属对话框对象的创建的同时被创建。 注意,一般树型表控件选择缺省属性。引用控件对象:使用 ClassWizard 的 属性页 为树型表控件资源声明关联的对象成员(例如 m_treeView) 或在需要使用该控件对象时调用 CWnd:GetDlgItem 获得对话框 中控件对象的指针,即可直接或间接引用树型表控件对象。 调用实例: CTreeCtrl *pTreeView = ( CTreeCtrl* )GetDlgItem( ID_TREEVIEW );2 控件的初始化创建图标列表与树型表控件关联。初始化工作与列表控件类 同,其中用于关联操作的 CTreeCtrl:SetImageList 的原型: CImageList* SetImageList( CImageList * pImageList, int nImageListType ); 调用实例:假设在使用树型表控件的类中已定义了 CTreeCtrl 对象成员 m_treeView 和 CImageList 对象成员 m_treeImageList, 则典型的初始化操作如下: m_treeImageList.Create( 13, 13, ILC_COLOR4, 3, 0 ); HICON hIcon = :LoadIcon( AfxGetResourceHandle(),MAKEINTRESOURCE( IDI_ICON3 ) ); / IDI_ICON3是图标资源的ID m_treeImageList.Add( hIcon ); hIcon = :LoadIcon( AfxGetResourceHandle(),MAKEINTRESOURCE( IDI_ICON4 ) ); / IDI_ICON4是图标资源的ID m_treeImageList.Add( hIcon ); hIcon = :LoadIcon( AfxGetResourceHandle(),MAKEINTRESOURCE( IDI_ICON5 ) ); / IDI_ICON5是图标资源的ID m_treeImageList.Add( hIcon ); m_treeView.SetImageList( &m_treeImageList, TVSIL_NORMAL );为树型表控件创建各个层次表项。调用 CTreeCtrl:InsertItem 逐次完成各个层次表项的创建操作。该成员函数原型如下: HTREEITEM InsertItem( LPTVINSERTSTRUCT lpInsertStruct ); HTREEITEM InsertItem( UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, HTREEITEM hParent, HTREEITEM hInsertAfter ); HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST ); HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST); 调用实例:常用 InsertItem 的第一种版本形式 / 创建根表项 TV_ITEM tvItem; tvItem.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tvItem.pszText = Root; tvItem.cchTextMax = 4; tvItem.iImage = 0; tvItem.iSelectedImage = 0; TV_INSERTSTRUCT tvInsert; tvInsert.hParent = TVI_ROOT; tvInsert.hInsertAfter = TVI_FIRST; tvInsert.item = tvItem; HTREEITEM hRoot = m_treeView.InsertItem(&tvInsert); / 创建第一层次的一个表项 tvItem.pszText = Child Item 1; tvItem.cchTextMax = 12; tvItem.iImage = 1; tvItem.iSelectedImage = 1; tvInsert.hParent = hRoot; tvInsert.hInsertAfter = TVI_FIRST; tvInsert.item = tvItem; HTREEITEM hChildItem = m_treeView.InsertItem(&tvInsert); / 创建第二层次的一个表项 tvItem.pszText = Child Item 2; tvItem.cchTextMax = 12; tvItem.iImage = 2; tvItem.iSelectedImage = 2; tvInsert.hParent = hChildItem; tvInsert.hInsertAfter = TVI_FIRST; tvInsert.item = tvItem; m_treeView.InsertItem( &tvInsert ); / 创建第一层次的另一个表项 tvItem.pszText = Child Item 3; tvItem.cchTextMax = 12; tvItem.iImage = 1; tvItem.iSelectedImage = 1; tvInsert.hParent = hRoot; tvInsert.hInsertAfter = TVI_LAST; tvInsert.item = tvItem; m_treeView.InsertItem( &tvInsert );3 控件的使用 通过 CTreeCtrl 类对象或指向类对象的指针,调用该类的成员 函数,在 CTreeCtrl 类对象所关联的树型表控件中实现所需要 的功能操作。5.6.4 自画按钮控件1 具有自绘属性的控件的自绘操作 用 ClassWizzard 定义 CButton 的派生类(如 COwnerButton)。 添加三幅位图(如 IDB_NORMAL、IDB_SELECT 和 IDB_FOCUS) 资源,分别对应按钮的三种不同状态:弹起(NORMAL)状 态、按下(SELECT)状态和选中(FOCUS)状态。 用 ClassWizzard 为 CButton 的派生类重定义虚函数 DrawItem, 该函数的原型如下: virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); 重定义中主要包括: 从 DrawItem 函数的参数中获得绘图设备场境: CDC* pDC = CDC:FromHandle( lpDrawItemStruct-hDC ); 绘制弹起状态位图(如 IDB_NORMAL) : if(lpDrawItemStruct-itemAction = ODA_DRAWENTIRE) 在按钮表面绘制位图 IDB_NORMAL 绘制按下状态位图(如 IDB_SELECT): if(lpDrawItemStruct-itemAction = ODA_SELECT) 在按钮表面绘制位图 IDB_SELECT 绘制聚焦状态位图(如 IDB_FOCUS): if(lpDrawItemStruct-itemState = ODS_FOCUS) 在按钮表面绘制位图 IDB_FOCUS 将自定义自画按钮类(例如 COwnerButton) 的定义文件包含 到使用自画按钮的代码源文件中; 在使用自画按钮的窗口中直接或间接创建自画按钮,例如: 直接创建 m_pButton = new COwnerButton(); m_pButton-Create( NULL, WS_CH I LD | WS_VISIBLE | BS_OWNERDRAW, CRect(0, 0, 30, 30), this, 100 ); 间接创建 在资源模板(例如对话框模板)中放置一个与你所定义 的自绘控件类的基类控件资源(本例中的 PUSHBUTTON 控件,并设定 BS_OWNERDRAW 属性); 在控件资源的所属类中,通过 ClassWizard 添加与控件资 源关联的自绘控件类对象。本例中: COwnerButtonm_bnOwner;/ 自绘普通按钮控件 m_bnOwner 的绘制刷新是通过 DoDataExchange 中的相应代 码实现的。例如: DDX_Control( pDX, IDC_OWERBUTTON, m_bnOwner ); 不管哪一种创建方法,控件风格必须具有 BS_OWNERDRAW 风格,否则框架将不会调用自定义控件类实现自绘操作的成 员函数 DrawItem。允许设置自绘风格的控件有按钮(Button)、 复合下拉列表框(ComboBox)、列表框(ListBox)、菜单 (Menu)、列表控件(ListCtrl)、静态文本框(Static) 和标签 控件(TabCtrl)。2 不具有自绘属性的控件自绘操作 有些控件不能设置 BS_OWNERDRAW风格,但如果需要,也可以通过以下描述的操作实现这些控件的自绘操作。对于不同的控件,控制实现的代码会有所不同,但一般可以包括以下几部分内容: 在应用程序中自定义以这类控件的 MFC 基类的派生类。例 如本例中定义的自绘单选按钮类: class COwnerRadioButton : public CButton 自定义控件的绘制操作:由于程序框架不调用这些控件类的 DrawItem 成员函数,因此实现这些控件类对象所关联控件的 自绘制操作必须通过响应 WM_PAINT 消息的成员函数 OnPaint 完成。本例(应用程序“ConntrolsDemo”)中: void COwnerRadioButton:OnPaint() CPaintDC dc(this); / device context for painting / TODO: Add your message handler code here HMETAFILE hNormalMtFile = GetMetaFile(m_strNormal); HMETAFILE hSelectMtFile = GetMetaFile(m_strSelect); if(hNormalMtFile & hSelectMtFile) if(GetCheck() = 0)/ 未选中状态 dc.PlayMetaFile(hNormalMtFile);else dc.PlayMetaFile(hSelectMtFile); / Do not call CButton:OnPaint() for painting messages 注意,以上代码中实现按钮绘制是借助元文件 MetaFile 完成 的。使用 MetaFile 的优点是: 控件的绘制内容由 MetaFile 中的绘制操作确定,便于实现 同类控件的不同对象的不同绘制。 通过动态创建 MetaFile 易于实现在程序中动态创建同类控 件的不同对象。 MetaFile 文件占用磁盘空间小。 另外需要注意的是要求元文件 MetaFile 中所绘制的图形应该 与控件的尺寸一致(可以通过先确定控件的尺寸,然后调整 MetaFile 中绘制图形的大小来满足此要求)。 在自定义控件类中添加必要的属性和成员函数,满足在控件 的各类操作时的自绘功能,对于不同的控件要添加的函数会 有所不同。本例(应用程序“OwnerDrawDlgBar”)中: 添加用于存放 MetaFile 文件名和存放单选控件组 ID 范围的 属性: private: CString m_strSelect; / 用于绘制选中状态按钮的MetaFile文件名 CString m_strNormal; / 用于绘制未选中状态按钮的MetaFile文件名 public: static WPARAM m_lIdRange; / 存放单选按钮组ID范围(低字:第一个ID;高字:最后一个ID) 添加用于设置 MetaFile 文件名的成员函数,满足不同控件 对象使用不同 MetaFile 文件: void COwnerRadioButton:SetMetaFileName(CString normal, CString select) m_strNormal = normalm_strSelect = select; 添加 BN_CLICKED 的反射消息映射和响应函数: ON_CONTROL_REFLECT(BN_CLICKED, OnClicked) afx_msg void OnClicked(); / 响应NM_CLICK反射消息 void COwnerRadioButton:OnClicked() / TODO: Add your control notification handler code here CWnd* parent = GetParent(); if(!parent)return; for(int i = LOWORD(COwnerRadioButton:m_lIdRange); i GetDlgItem(i); if(pradio) pradio-Invalidate(); NMHDR nmhdr; nmhdr.code = NM_CLICK; nmhdr.idFrom = GetDlgCtrlID(); nmhdr.hwndFrom = GetSafeHwnd(); parent-SendMessage(WM_NOTIFY, (WPARAM)GetDlgCtrlID(), (LPARAM)&nmhdr); 覆盖定义用于使能控件的成员函数 EnableWindow: BOOL COwnerRadioButton:EnableWindow(BOOL bEnable) BOOL res = CWnd:EnableWindow(bEnable);Invalidate();return res; 5.7 特定用途对话框MFC 还提供了一些对话框类用于特定用途的交互操作,例如: “打开”、“保存”等文件操作所需要的文件浏览选择操作;文本编辑、绘图操作所需要的字体选择操作和颜色选择操作;打印和打印预览输出操作所需要的打印设置操作等等。所有这些操作在程序中常常是不可缺少的,使用这些特定用途对话框类将能大大简化程序的编写工作,同时使得各类应用程序中这些常用的交互操作具有统一性、安全性和友好性。这些特定用途的对话框类主要有:ColorDialog、CFileDialog、CFontDialog、 CFindReplaceDialog、CPageSetupDialog 和 CPrintDialog 。5.7.1 特定用途对话框类的派生结构CObjectCCmdTargetCWndCDialogCCommonDialogCColorDialogCFileDialogCFindReplaceDialogCFontDialogCPageSetupDialogCPrintDialog5.7.2 特定用途对话框类的运行状态、用途和用法类名运行状态用途用法CColorDialog模态“颜色对话框”用于文本、图形的颜色选择。1 在堆栈中定义对话框对象;2 调用 DoModal 创建对话框,模态 运行:选择颜色;3 根据对话框运行结束状态和后续 执行的需要,访问对话框对象的 相应属性获取所选择的颜色。CFileDialog模态“文件对话框”用于被操作文件的浏览、查找、选择。1 在堆栈中定义对话框对象,并为 对话框对象设置文件过滤描述;2 调用 DoModal 创建对话框,模态 运行:查找、选择文件名;3 根据对话框运行结束状态和后续 执行的需要,访问对话框对象的 相应属性获取所选择的文件全名。类名运行状态用途用法CFindReplaceDialog非模态“查找替换对话框”用于在文本中查找、替换指定的字串。1 用 new 在堆中创建对话框对象;2 为对话框对象设置所属窗口句柄;3 为对话框对象的通知消息描述字串 注册消息标识,并在对话框对象所 属窗口对象中,为该注册通知消息 添加消息处理函数和消息映射;2 调用 Create 创建对话框,模态运行: 设定被查找/替换的字串,选择操作 (查找下一个、替换当前匹配、替 换全部匹配、退出等)并向对话框 所属窗口发出相应的通知消息。类名运行状态用途用法CFontDialog模态“字体对话框”用于文本的字体选择。1 在堆栈中定义对话框对象;2 调用DoModal创建对话框,模态 运行:选择字体;3 根据对话框运行结束状态和后续 执行的需要,访问对话框对象的 相应属性获取所选择的字体。CPageSetupDialog模态“页面设置对话框”用于打印页面的格式设置。1 在堆栈中定义对话框对象;2 调用DoModal创建对话框,模态 运行:设置页面格式;3 根据对话框运行结束状态和后续 执行的需要,访问对话框对象的 相应属性获取所设置的页面格式。类名运行状态用途用法CPrintDialog模态“打印对话框”用于打印设备的参数设置。1 在堆栈中定义对话框对象;2 调用DoModal创建对话框,模 态运行:设置打印设备;3 根据对话框运行结束状态和后 续执行的需要访问对话框对象 的相应属性获取所设置的打印 设备参数。
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号