与对话框编程_第1页
与对话框编程_第2页
与对话框编程_第3页
与对话框编程_第4页
与对话框编程_第5页
已阅读5页,还剩156页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第五章MFC

与对话框编程5.1

对话框的基本原理

对话框是Windows

应用程序GUI

界面的一个重要组成部分,它的主要功能:·接收用户的输入(数据和操作、控制命令);·输出用户关心的状态信息(操作的结果数据和状态)。在对话框中,可以放置各种必要的控件,所有与用户的交互操作都是通过这些控件实现的。在MFC中,对话框的基本属性和行为由CDialog

类描述,该类的基类为CWnd,因此,它继承了窗口类的所有属性和功能,并且增加了交互操作所需要的数据交换特性。放置在对话框中的各类控件,MFC都有相应的类来描述,所有控件类的基类都是CWnd。CDialog

类和所有控件类的派生层次结构如下:由此我们可以看出:对话框可以视为是提供了一个能放置多个子窗口,并提供了窗口之间通讯操作的窗口框架。这种异类同构的结构有利于简化构造和便于通讯。CObjectCCmdTargetCWndCDialogCStaticCEditCComboBoxCListBoxCProgressCtrlCSliderCtrlCSpinButtonCtrl控件对话框和放置在其中的控件的创建,MFC

应用程序是从两个方面实现的:·对话框模板资源:描述对话框本身和各个控件的类型、外观

属性和控件在对话框中的分布信息。使用资源编辑器绘制模板,并将相关的信息保存在资源文件(扩展名为“.rc”)中。·对话框类:封装对话框的属性和操作功能。保存在相应的定义和实现文件中。注意,与所有CWnd

及其派生类对象一样,对话框类对象必须与对话框模板所描述的Windows

窗口相关联后,所有施加于对话框的操作才是合法的。对话框的设计步骤一般是先设计对话框模板,然后定义与对话框模板资源相关联的对话框类。 对话框的使用分为模态对话框和非模态对话框两种:·模态对话框:对话框打开时,用户只能在对话框中进行交互操作,只有对话框关闭后,才能进行其他用户界面的操作。·非模态对话框:打开对话框进行操作的同时,仍然可以进行其他用户界面的交互操作。5.2创建模态对话框创建一个名为“Register”SDI应用程序,用于人员信息的注册登记。该应用程序项目除视图的基类选择CEditView

外,其他均接受缺省选择。5.2.1创建对话框模板资源为项目添加一个对话框模板(缺省命名为IDD_DIALOG1)。从控件选择面板上选择所需要的控件,将它放置到对话框模板中你所希望的设计位置上。对话框和对话框中的每个控件都可以通过“属性”对话框设置它们的属性和风格。选中对话框或某个控件,点击鼠标右键便会弹出一个浮动上下文菜单:选择菜单项Properties,便弹出选中对话框或控件的属性操作对话框:操作选择标签的个数和类型随着选中的对象(对话框或不同控件)的不同而不同,但其中最右边的两个按钮和第一项标签是相同的。1

按钮

:按下该按钮,使按钮变成,使得该属性对话框保持可见(KeepVisible),直到释放该按钮或强制关闭对话框。属性对话框的内容会随着当前选中对象的改变而变化。2按钮:点击该按钮,将显示当前选中属性页面相关内容的

帮助信息。3

通用标签:提供对话框或所有控件的通用属性操作。对话框的主要通用属性操作:

·ID

指定对话框的标识值,程序可使用它装载对话框 资源和选取对话框窗口。

·Caption

指定对话框的显示标题。

·Font…

选择对话框中所有文本(包括控件标题)字体。

·Menu

如果需要,可为对话框选择显示菜单资源。

控件的通用属性的含义如下:

·ID

指定控件的标识值,程序使用它装载控件资源和 选取控件窗口。

·Caption

指定诸如静态文本、组框、按钮、复选框、单选 按钮等控件的显示标题。对于能发出通知消息的 控件,其标题与菜单项标题一样,紧跟字符&

之 后的字符在运行时具有下划线,可通过按《Alt+

下划线字符》替代点击该控件。

·Visible

指定控件被创建后是否可见。

·Disable

指定控件被创建后是处于允许还是被禁止使用。

·Tabstop

对话框运行时,允许用户使用Tab

键顺序选中具 有该属性的控件。控件选中顺序可以任意指定。

·Group

用来指定一组控件(例如单选按钮),用户可以 用箭头键在组内的控件之间移动选择;在同一组 内的单选按钮具有互斥性,即只能选中一个单选 按钮。如果一个控件具有Group

属性,则这个控 件以及按Tab

顺序紧随其后的的所有控件都属于 同一组,直到另一个有Group

属性的控件为止。本例中对话框的具体设计步骤如下:1

修改对话框模板资源的标识为IDD_REGISTER。2

在对话框中添加控件控件类型控件标识显示标题其他属性组框组框静态文本文本框复选框静态文本文本框组框单选按钮单选按钮默认默认默认IDC_NAME

IDC_MARRIED

默认IDC_AGE

默认IDC_SEX默认个人情况单位情况姓名婚否性别男女默认默认默认默认默认默认默认默认Group、Tabstop默认控件类型控件标识显示标题其他属性组框单选按钮单选按钮静态文本文本框静态文本组合列表框静态文本列表框按钮按钮

默认

IDC_WORK

IDC_WORK1默认

IDC_UINT默认

IDC_KIND默认

IDC_INCOME

IDOK

IDCANCEL

就业情况在职下岗工作单位单位性质工资收入确定(&Y)取消(&C)

默认

Group、Tabstop默认默认默认默认下拉列表、不排序、初始化列表项。默认不排序默认默认

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(输入输出的不同类型内容)

普通复选框CButtonBOOL(非零:选择,零:不选择)

三态复选框

CButtonint(0:未选中、1:选中、2:不确定)滚动条

CScrollBar,滑尺CSliderCtrlint(游标位置)

组中的确单选按钮

CButtonint

(-1:无选中,0:选中组中第一按钮,1:组中第二个…)

列表框

CListCtrlCString

(空:无列表项选中、非空:选中的列表项)

下拉式组合列表框

CComboBoxCString

(空:无列表项选中、非空:选中的列表项)或int

(-1:未选中,0:选中列表第一项,1:选中列表第二项…)为控件定义哪一类数据对象,或是两类数据对象都定义将根据控件在对话框类中的使用情况而定。具体的添加方法:在VisualC++6.0中,使用ClassWizard

的《MemberVariables》属性页完成。添加方法如下:在对话框中选中要添加数据对象的控件ID,双击被选项或点击<AddVariable…>按钮,弹出“AddMemberVariable”对话框:在该对话框中可以确定你所要添加的数据对象的种类是数值类对象还是控件类对象,并为所添加数据对象命名(推荐命名规则: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

在VisualC++.NET中,是通过在对话框模板中,为每个需要添加对象成员的方法实现的。具体的添加方法如下图所示:5.2.4

对话框的初始化对话框初始化是在对话框类构造函数和对话框窗口初始化成员函数OnInitDialog

中完成的。·构造函数只完成数据成员的初始化,因为此时对话框类对象正在创建中,而对话框窗口还未创建,窗口句柄无效。如果需要初始化的数据对象是通过ClassWizard

的MemberVariables

页添加的,则构造函数中数据对象的初始化代码将会由

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(注意,在VisualC++.NET中是通过重载基类的虚函数OnInitDialog

的方法完成的),然后手工在OnInitDialog

中加入对用于工资收入的列表框控件的初始化代码如下:

BOOLCRegisterDialog::OnInitDialog(){

CDialog::OnInitDialog();

//TODO:Addextrainitializationhere

m_ctrlIncome.AddString("500元以下");

m_ctrlIncome.AddString("500-1000元");

m_ctrlIncome.AddString("1000-2000元");

m_ctrlIncome.AddString("2000元以上");

returnTRUE;//returnTRUEunlessyousetthefocustoacontrol //EXCEPTION:OCXPropertyPagesshouldreturnFALSE}5.2.5

信息交换、验证在对话框操作时,用户只要通过交互方式修改对话框窗口某个控件中的数据信息,就可以修改与该控件关联数值对象的值;反过来只要程序修改了对话框类对象中某个与控件关联数值对象的值,也就可以修改对话框窗口中与该数值对象关联控件中显示的信息。对话框的这种数据交换(DDX)和数据有效验证(DDV)能力是由MFC类CDataExchange

来实现的。实现这种信息交换操作是由虚函数CDialog::DoDataExchange完成的。派生类中该函数的重定义版本将在调用该函数的基类版本基础上完成派生类对象所需要的特定的数据交换操作。所幸的是该函数的重定义工作可以借助ClassWizard

在定义与控件相关的对话框类数据对象的同时自动完成的。例如在本例中:voidCRegisterDialog::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

的原型如下:BOOLUpdateData(BOOLbSaveAndValidate=TRUE);参数:bSaveAndValidate=TRUE

指定数据值从控件传递到数据对象;bSaveAndValidate=FALSE

指定数据值从数据对象传递到控件。该函数调用了DoDataExchange,显然,参数bSaveAndValidate

确定了DoDataExchange

的数据传递方向。5.2.6

对话框的运行在程序中运行模态对话框的步骤:⑴在栈上构建一个局部对话框类对象。⑵调用CDialog::DoModal()

DoModal

负责模态对话框的创建→启动一个消息循环→撤消对话框(接收到<OK>按钮或<Cancel>按钮控件发出的消息)。显然,在DoModal

函数返回之前,不可能进行其他界面上的操作的。该函数是一个虚函数,意味着可以根据需要在CDialog

的派生类中定义特定功能的DoModal

,但大多数情况下无须重新定义。①对话框创建时,程序框架会自动调用OnInitDialog

初始化对话框,并调用UpdateData(FALSE)

初始化控件的显示。②对话框撤消时,分为两种情况:

·按<OK>

按钮,消息处理成员函数OnOk

被调用。在该函数 的执行中,首先调用UpdateData(TRUE

)将控件中的数据传 递到相应数据对象中保存起来,然后调用EndDialog

关闭对 话框,并返回标志值IDOK。

·按<Cancel>按钮,消息处理成员函数OnCancel

被调用,在 该函数的执行中,只调用EndDialog

关闭对话框,并返回标 志值IDCANCEL。

根据DoModal

返回的标志值,程序就可以知道对话框是如何

结束的(按<OK>或<Cancel>按钮),从而确定后续操作。

在本例中,为了模态使用对话框,添加了一条菜单命令来触发对话框类对象的创建,对话框的模态创建、显示、操作和根据对话框结束状态的后续操作。1

在菜单资源中的Edit

弹出式菜单中添加菜单项“登录数据”,其标识为ID_EDIT_REGISTER。2

使用ClassWizard

为菜单项“登录数据”在视图类CRegisterView

中定义命令消息映射项和处理函数OnEditRegister。3

定义函数OnEditRegister

实现代码:voidCRegisterView::OnEditRegister(){//TODO:Addyourcommandhandlercodehere

CRegisterDialog

dlg;

if(dlg.DoModal()==IDOK){

CString

str;

GetWindowText(str); //获取视图窗口原有文本内容

str+="\r\n";

str+="姓名:";str+=dlg.m_strName;str+="\r\n";

str+="性别:";str+=dlg.m_nSex?"女":"男";str+="\r\n";

str+="年龄:";

CStringstr1;str1.Format("%d",dlg.m_nAge);

str+=str1;str+="\r\n";

str+="婚否:";str+=dlg.m_bMarried?"已婚":"未婚";str+="\r\n";

str+="就业情况:";str+=dlg.m_nWork?"下岗":"在职";str+="\r\n";

str+="工作单位:";str+=dlg.m_strUnit;str+="\r\n";

str+="单位性质:";str+=dlg.m_strKind;str+="\r\n";

str+="工资收入:";str+=dlg.m_strIncome;str+="\r\n";

SetWindowText(str); //在视图窗口中显示新文本

}}4在CRegisterView

的实现文件中加入:

#include“RegisterDialog.h”5

编译运行5.2.7程序的改进在程序的运行中发现存在这样的缺点:当在“就业情况”组框中选择了单选按钮“下岗”,而“单位情况”组框中的所有控件仍然有效,显然这是不合理的,会引起登录数据的错误。为了克服这一缺点,应该当在“就业情况”组框中选择了单选按钮时,程序能够响应对应的控件消息,在消息处理函数中根据所选按钮的不同,使“单位情况”组框中的相关控件允许或禁止。虽然组框中的单选按钮是两个,而它们的消息处理函数只需要一个,因此应该使用ON_CONTROL_RANGE

消息映射宏进行消息映射。不幸的是ClassWizard

不支持ON_CONTROL_RANGE,所以我们必须按下列步骤手工创建单选按钮的消息映射和消息处理函数。1在CRegisterDialog

的定义文件中定义消息处理函数原型:

classCRegisterDialog:publicCDialog{ … //Implementationprotected:

//Generatedmessagemapfunctions //{{AFX_MSG(CRegisterDialog) virtualBOOLOnInitDialog();

//}}AFX_MSG

afx_msgvoidOnWorkClicked(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

实现文件中添加消息响应函数实现代码:

voidCRegisterDialog::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

属性有效,并将<OK>

和<Cancel>

按钮的显示标题改为<提交>和<退出>。⑵在CRegisterView

类定义中加入数据登录对话框类指针:

…public:

CRegisterDialog*m_pRegisterDlg;

注意将CRegisterDialog

类的头文件包含在CRegisterView

类的定义文件中,或在定义对话框指针之前加入超前声明:

classCRegisterDialog;⑶在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:addconstructioncodehere

m_pRegisterDlg=NULL;}voidCRegisterView::OnEditRegister(){//TODO:Addyourcommandhandlercodehere

if(m_pRegisterDlg) //判断对话框已创建否?

m_pRegisterDlg->SetActiveWindow();//激活已创建的对话框

else{

m_pRegisterDlg

=newCRegisterDialog(this);//创建对话框对象

m_pRegisterDlg->Create(IDD_REGISTER,this);//创建窗口

}}⑸使用ClassWizard

为CRegisterDialog

类添加OnCancel、OnOK

消息响应函数和重载虚函数PostNcDestroy,并为这两个函数编写定义代码:

voidCRegisterDialog::OnCancel(){

//TODO:Addextracleanuphere

m_pParent->SendMessage(WM_DIALOG_DESTROY,0,0);

//向父窗口发出关闭对话框消息

DestroyWindow(); //关闭对话框窗口

}

voidCRegisterDialog::OnOK(){

//TODO:Addextravalidationhere

CString

str;

UpdateData();

m_pParent->GetWindowText(str);//获取父窗口中的显示文本

//在原来文本的基础上添加新文本

str+="\r\n";

str+="姓名:";str+=m_strName;str+="\r\n";

str+="性别:";str+=m_nSex?"女":"男";str+="\r\n";

str+="年龄:";

CStringstr1;

str1.Format("%d",m_nAge);

str+=str1;str+="\r\n";

str+="婚否:";str+=m_bMarried

?"已婚":"未婚";str+="\r\n";

str+="就业情况:";str+=m_nWork

?"下岗":"在职";str+="\r\n";

str+="工作单位:";str+=m_strUnit;str+="\r\n";

str+="单位性质:";str+=m_strKind;str+="\r\n";

str+="工资收入:";str+=m_strIncome;str+="\r\n";

m_pParent->SetWindowText(str);//在父窗口中显示添加后的文本

}voidCRegisterDialog::PostNcDestroy(){//TODO:Addyourspecializedcodehereand/orcallthebaseclass

deletethis; //删除对话框类对象

CDialog::PostNcDestroy();}⑹

对话框关闭时要求其父窗口类对象能及时将指向对话框的指针设置为NULL。实现方法和步骤如下:

·在CRegisterDialog

的定义文件中增加用户自定义消息:

constWM_DIALOG_DESTROY=WM_USER+100;·在CRegisterView

定义中增加消息处理函数声明:

afx_msgLRESULTOnDialogDestroy(WPARAM,LPARAM);·在CRegisterView

实现文件中添加消息响应函数定义代码:

LRESULTCRegisterView::OnDialogDestroy(WPARAMwParam, LPARAMlParam)

{

m_pRegisterDlg=NULL; return1;

}⑺编译运行5.3.2关于窗口对象的自动清除1MFC窗口类对象的组成一个MFC

窗口由两部分组成:

·描述窗口属性和功能的C++类——窗口类(CWnd

或CWnd

的派生类)。

·

Windows窗口,该窗口的句柄作为一个数据成员m_hWnd

被封装在窗口类中。2MFC

窗口类对象的创建

·首先创建窗口的窗口类对象;

·然后调用窗口类的创建函数(例如CWnd::Create)创建由 参数确定的Windows窗口,并使之与窗口类对象相关联; 或调用Windows窗口关联函数(例如CWnd::Attach)使窗口 类对象与一个已经存在的Windows

窗口相关联。3MFC

窗口类的删除⑴先调用CWnd::DestroyWindow

撤消Windows

窗口,并使窗口类

对象的Windows

窗口句柄属性m_hWnd=NULL(表示窗口已经无效)。一般情况下,该函数不需要直接调用,而是由应用程序框架自动调用,例如:关闭窗口的WM_CLOSE

消息、对话框的<OK>

或<Cancel>按钮发出的消息都会导致该函数被调用。如果DestroyWindow

所撤消的窗口拥有子窗口,则先删除所有的子窗口,然后删除父窗口。⑵然后删除窗口的窗口类对象。删除窗口类对象分两种情况:

·构建在堆栈上的窗口窗口类对象:该窗口类对象是作为某个类的数据成员或函数中的局部变量。它们的生存期总是有限的,随着拥有窗口类对象的撤消或函数的返回,被自动被删除,所以不必关心这类窗口类对象的删除。

·创建在堆上的窗口类对象:该窗口类对象是使用运算符new创建的,其生存期是任意的,因此,在关闭Windows

窗口后必须使用运算符delete从堆中删除窗口类对象,否 则不会被自动清除。4自动清除功能自动清除功能是指在Windows窗口被删除时,自动删除窗口

类对象。这是因为当CWnd::DestroyWindow

被调用撤消一个

Windows窗口时,与该窗口关联的窗口类对象的成员函数

PostNcDestroy

被调用。该函数是一个虚函数,MFC所提供的有些CWnd

的派生类重定义了该函数,在新版本函数中通过代码deletethis

来删除与被撤消窗口关联的窗口类对象本身,因此不需要显式使用delete

删除窗口类对象,即自动清除功能。具有自动清除功能的MFC

窗口类有:

·主框架窗口类(基类为CFrameWnd

·视图类(基类为CView)这些窗口类的对象在应用程序中一般都是动态在堆中创建

的。而对于其他MFC

窗口类的使用,由于大多数情况下是在

堆栈中创建的。所以MFC

没有为这些类提供自动清除功能。这些窗口类包括:

·所有标准的Windows

控件类

·从CWnd

直接派生的自定义窗口类(如用户定制的控件)

·切分窗口类CSplitterWnd·默认的控制栏类(如工具栏和状态栏)

·对话框类(从CDialog

派生)由此可见,要不要为自己的派生窗口类添加自动清除功能,取决于所派生的窗口类的创建方法,例如,我们前面的非模式对话框的实例中,由于对话框是在堆中创建的,要求其生存期任意,所以需要为它添加了自动清除功能。否则我们必须在对话框窗口被删除后,在对话框类对象的拥有者(父窗口类对象)中的适当位置(如析构函数中),调用delete

显式删除对话框类对象。5.4

创建属性表对话框

属性表是一种包含了多个对话框的对话框框架,这些包含在属性表中的对话框通常被称为属性页(Page)。每次只有一个属性页是可见的,在属性表的顶端有一行标签(tab)用于指示属性表中不同的属性页,用户通过单击这些标签可以切换到不同的属性页。使用属性表可以按操作功能将控件分类放在不同的属性页中,便于组织和构造需要大量控件的复杂对话框,也使得对话框的使用界面更加友好。5.4.1属性表对话框的创建

属性表是由多个属性页和一个能放置这些属性页,并且将这些属性页关联起来的对话框框架组成的,就像是一个夹有多页文件的文件夹。为了支持属性表,MFC

提供了两个类:·CPropertySheet

: 用于创建属性表框架和提供相应的属性和

操作。它的直接基类是CWnd,使该类在窗口的属性和功能的基础上增加了对属性页的创建、激活、切换、数据交换、撤消等功能。·CPropertyPage

:用于创建属性表的属性页和提供相应的属性

和操作。它的直接基类是CDialog,使该类在对话框的属性和功能的基础上增加了对属性表框架各项管理的响应操作。与普通对话框相比,属性表的设计和实现既有许多相似之处,又有一些不同。创建一个属性表一般包括以下几个步骤:1

创建属性页(对话框)资源创建所有属性页的资源,每个属性页的资源都是一个对话框模板。与创建普通对话框模板相比应注意以下几点:

·一般需要删除缺省的<OK>

和<Cancel>

按钮;

·每页的模板尺寸最好相同,如果不同,则在属性表创建时,系统会按最大的模板尺寸统一创建所有页的尺寸;

·指定标题(Caption)的内容将成为属性表中相应属性页的指示标签内容;

·属性页模板属性设定为TitleBar、Child、ThinBoder

和Disable

。2

生成属性页类使用ClassWizard

为每个属性页模板定义相应的属性页类,基类必须选择CPropertyPage。3

在属性页类中为属性页中的控件添加关联的数据对象与普通对话框一样,使用ClassWizard

的《MemberVariables》

属性页为每个属性页模板中的控件在相应的属性页类中添加所需要的数据对象。4

创建属性表创建属性表的方法两种:⑴直接使用CPropertySheet

类创建在需要使用属性表的码段中加入相应的创建代码。例如:

voidCMyView::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模态运行的属性表会自动在属性表的底部提供三个按钮,依次为<OK>或<确认>、<Cancel>或<取消>和<Apply>或

<应用>按钮(非模态运行的属性表不提供这些按钮)。其中:

·<OK>或<确认>按钮的作用是确认当前属性页的操作,并

关闭整个属性表。

·<Cancel>或<取消>按钮的作用是放弃当前属性页的操 作,关闭整个属性表。

·通过标签的属性页切换操作将确认被切换属性页的操作。

·<Apply>或<应用>按钮的作用是确认当前属性页操作,并 使属性表的所有操作生效,但不关闭该属性表。对这三个按钮作出处理的响应函数可由CPropertyPage

类提供的消息响应函数OnOK、OnCancel

和OnApply

(OnApply

会自动调用OnOK)完成。因此,我们可以在属性页派生类中重定义这三个消息响应函数完成所需要的特定操作。应当指出:对属性页的这三个消息响应函数的调用并非直接响应属性表的三个按钮的通知消息BN_CLICKED

的结果,而是通过CPropertySheet

类或派生类对这三个按钮的消息响应函数,间接调用上述CPropertyPage

的三个消息响应函数完成的。例如,定义响应<Apply>按钮的通知消息,完成特定的处理可以有两种方法:

⑴使用ClassWizard

为属性表中任意一个属性页类重定义虚函数OnApply,在该函数中定义要实现的特定功能代码。一般情况下所定义的功能将对整个属性表起作用,所以只须在一个属性页中重定义OnApply

即可,不必为每个属性页都重定义OnApply。⑵在CPropertySheet

的派生类中添加<Apply>按钮的通知消 息BN_CLICKED的消息映射和响应函数,在该函数中定义 想要实现的操作。注意,系统为<Apply>按钮定义的标识 为ID_APLLY_NOW,但在VC6.0

中ClassWizard

无法识别该 标识,因此上述添加操作只能手工完成。4CPropertyPage

类中几个与运行机制相关的重要成员函数:

voidSetModified(BOOLbChanged=TRUE);

该函数用于设置属性页的修改标志。参数bChange

为TRUE

表明属性页的属性已修改,否则表示未修改。调用该函数的主要功能是“允许”或“禁止”

<Apply>按钮:

·只要在一个属性页中调用了SetModified(),就会使<Apply>

按钮的状态变为“允许”;

·必须在所有的属性页中调用SetModified(FALSE),才能使

<Apply>

按钮的状态变为“禁止”。

virtualBOOLOnSetActive();

当属性页被激活或被创建时,该函数将被程序框架调用。该函数的缺省操作是:

BEGIN

if(属性页创建否?) then激活已创建的属性页

else创建属性页

endif

调用

UpdateData(FALSE)刷新属性页内的控件。

END

可以重定义该虚函数完成一些所需要的工作,但执行这些特定操作之前必须首先调用CPropertyPage::OnSetActive

virtualBOOLOnKillActive();

当切换属性页时,被切换的属性页会调用该函数。该函数的

缺省操作是调用UpdateData(TRUE)保存数据。可以重定义该虚函数完成一些特定数据的有效检查和操作。该函数的成功返回将导致程序框架调用OnOK

函数。注意:

·在属性表的一次调用中,表中的属性页并不一定都被创建过。那些从未打开过的属性页就不会被创建。

·只有确定属性页已经存在,才能调用与属性页和控件相关的函数。如果收到了控件通知消息或OnSetActive

函数被调用,则表明属性页已经创建。

·属性页之间的数据交换只能在OnSetActive

和OnKillActive

函数中进行。5.4.3属性表的具体实例创建一个属性表应用程序项目“Register3”

,在该项目中将“Register”

应用程序对话框中的两组控件“个人情况”和“单位情况”分解为属性表的两个属性页。创建属性表步骤如下:1

创建第一个属性页⑴创建属性页资源

·生成新对话框模板IDD_PERSONAL;

·去掉缺省的<OK>和<Cancel>

按钮;

·确定模板标题为“个人信息”作为该属性页的“标签”;

·设置模板属性:TitleBar、Child、ThinBoder

和Disable;

·添加所需要的控件:本例可以复制前例中对话框模板中的 相应的控件。控件类型控件标识显示标题其他属性静态文本缺省姓名缺省文本框IDC_NAME缺省复选框IDC_MARRIED婚否缺省静态文本缺省年龄缺省文本框IDC_AGE缺省组框缺省性别缺省单选按钮IDC_SEX男Group、Tabstop单选按钮IDC_SEX1女缺省组框缺省就业情况缺省单选按钮IDC_WORK在职Group、Tabstop单选按钮IDC_WORK1下岗缺省⑵定义属性页类CPersonalPage·使用ClassWizard

为IDD_PERSONAL

定义CPropertyPage

派生类CPersonalPage。

·使用ClassWizard

,为各控件添加对应的数据对象:控件ID数据对象类型数据对象名IDC_AGEIDC_MARRIEDIDC_NAMEIDC_SEXIDC_WORK

UINTBOOLCStringintint

m_nAgem_bMarriedm_strNamem_nSexm_nWork

·手工添加消息映射和处理函数:①在类定义文件中加入消息响应函数声明:

afx_msgvoidOnChangeProperties();

//处理相应控件的BN_CLICKED消息

afx_msgvoidOnChangeProperties1();

//处理相应控件的EN_CHANGE消息②在类实现文件的消息映射表中加入:

ON_CONTROL_RANGE(BN_CLICKED,IDC_MARRIED, IDC_WORK1,OnChangeProperties

)

ON_CONTROL_RANGE(EN_CHANGE,IDC_NAME, IDC_AGE,OnChangeProperties1)③在类实现文件中加入消息响应函数的定义代码:

voidCPersonalPage::OnChangeProperties()

{

SetModified(TRUE); //使能Apply按钮

UpdateData(TRUE); //确认数据设置

}

voidCPersonalPage::OnChangeProperties1()

{

SetModified(TRUE); //使能Apply按钮

UpdateData(TRUE); //确认数据设置

}2

创建第二个属性页⑴创建属性页资源

·生成新对话框模板IDD_UNIT;

·去掉缺省的<OK>

和<Cancel>

按钮;

·确定模板标题为“单位信息”作为该属性页的“标签”;

·设置模板属性: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_msgvoidOnChangeUnit();

afx_msgvoidOnSelchangeIncome();

afx_msgvoidOnEditchangeKind();

afx_msgvoidOnSelchangeKind(); ②在类实现文件的消息映射表中加入:

ON_EN_CHANGE(IDC_UNIT,OnChangeUnit)

ON_LBN_SELCHANGE(IDC_INCOME,OnSelchangeIncome)

ON_CBN_EDITCHANGE(IDC_KIND,OnEditchangeKind)

ON_CBN_SELCHANGE(IDC_KIND,OnSelchangeKind) ③在类实现文件中加入处理函数的定义代码:

voidCUnitPage::OnChangeUnit() {

SetModified(TRUE);

UpdateData(TRUE); } voidCUnitPage::OnSelchangeIncome() {

SetModified(TRUE);

UpdateData(TRUE); }

voidCUnitPage::OnEditchangeKind() {

SetModified(TRUE);

Upd

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论