您的当前位置:首页正文

MFC教程对话框的编辑

来源:帮我找美食网
第五章对话框

5.1 使用资源编辑器编辑对话框

在Windows 开发中弹出对话框是一种常用的输入/输出手段,同时编辑好的对话框可以保存 在资源文件中。Visual C++提供了对话框编辑工具,利用编辑工具可以方便的添加各种控件 到对话框中,而且利用ClassWizard 可以方便的生成新的对话框类和映射消息。

首先资源列表中按下右键,可以在弹出菜单中选择“插入对话框”,如图1。然后再打开该 对话框进行编辑,你会在屏幕上看到一个控件板,如图2。你可以将所需要添加的控件拖到 对话框上,或是先选中后再在对话框上用鼠标画出所占的区域。 接下来我们在对话框上产生一个输入框,和一个用于显示图标的图片框。之后我们使用鼠标 右键单击产生的控件并选择其属性,如图3。我们可以在属性对话框中编辑控件的属性同时 也需要指定控件ID,如图4,如果在选择对话框本身的属性那么你可以选择对话框的一些属 性,包括字体,外观,是否有系统菜单等等。最后我们编辑图片控件的属性,如图5,我们 设置控件的属性为显示图标并指明一个图标ID。

接下来我们添加一些其他的控件,最后的效果如图6。按下CtrlT

可以测试该对话框。此外

在对话框中还有一个有用的特性,就是可以利用Tab 键让输入焦点在各个控件间移动,要达 到这一点首先需要为控件设置在Tab 键按下时可以接受焦点移动的属性Tab Stop,如果某一 个控件不打算利用这一特性,你需要清除这一属性。然后从菜单“Layout”选择Tab Order 来确定焦点移动顺序,如图7。使用鼠标依此点击控件就可以重新规定焦点移动次序。最后 按下CtrlT 进行测试。

最后我们需要为对话框产生新的类,ClassWizard 可以替我们完成大部分的工作,我们只需 要填写几个参数就可以了。在编辑好的对话框上双击,然后系统回询问是否添加新的对话框, 选择是并在接下来的对话框中输入类名就可以了。ClassWizard 会为你产生所需要的头文件 和CPP 文件。然后在需要使用的地方包含相应的头文件,对于有模式对话框使用DoModal() 产生,对于无模式对话框使用Create()产生。相关代码如下; void CMy51_s1View::OnCreateDlg() {//产生无模式对话框

CTestDlg *dlg=new CTestDlg; dlg>

Create(IDD_TEST_DLG); dlg>

ShowWindow(SW_SHOW); }

void CMy51_s1View::OnDoModal() {//产生有模式对话框 CTestDlg dlg;

int iRet=dlg.DoModal();

TRACE(\"dlg return %d\\n\;

}

下载例子。如果你在调试这个程序时你会发现程序在退出后会有内存泄漏,这是因为我没有 释放无模式对话框所使用的内存,这一问题会在以后的章节5.3 创建无模式对话框中专门讲 述。

关于在使用对话框时Enter 键和Escape 键的处理:在使用对话框是你会发现当你按下Enter

键或Escape 键都会退出对话框,这是因为Enter 键会引起CDialog::OnOK()的调用,而Escape

键会引起CDialog::OnCancel()的调用。而这两个调用都会引起对话框的退出。在MFC 中这 两个成员函数都是虚拟函数,所以我们需要进行重载,如果我们不希望退出对话框那么我们 可以在函数中什么都不做,如果需要进行检查则可以添加检查代码,然后调用父类的OnOK() 或OnCancel()。相关代码如下; void CTestDlg::OnOK()

{

AfxMessageBox(\" 你选择确定\"); CDialog::OnOK(); }

void CTestDlg::OnCancel() {

AfxMessageBox(\" 你选择取消\"); CDialog::OnCancel(); }

5.2 创建有模式对话框

使用有模式对话框时在对话框弹出后调用函数不会立即返回,而是等到对话框销毁后才会返 回(请注意在对话框弹出后其他窗口的消息依然会被传递)。所以在使用对话框时其他窗口 都不能接收用户输入。创建有模式对话框的方法是调用CDialog::DoModal()。下面的代码演 示了这种用法:

CYourView::OnOpenDlg() {

CYourDlg dlg;

int iRet=dlg.DoModal();

}

CDialog::DoModal()的返回值为IDOK,IDCANCEL。表明操作者在对话框上选择“确认” 或是“取消”。由于在对话框销毁前DoModal 不会返回,所以可以使用局部变量来引用对 象。在退出函数体后对象同时也会被销毁。而对于无模式对话框则不能这样使用,下节5.3 创 建无模式对话框中会详细讲解。

你需要根据DoModal()的返回值来决定你下一步的动作,而得到返回值也是使用有模式对话 框的一个很大原因。

使用有模式对话框需要注意一些问题,比如说不要在一些反复出现的事件处理过程中生成有 模式对话框,比如说在定时器中产生有模式对话框,因为在上一个对话框还未退出时,定时 器消息又会引起下一个对话框的弹出。

同样的在你的对话框类中为了向调用者返回不同的值可以调用CDialog::OnOK() 或是 CDialog::OnCancel()以返回IDOK 或IDCANCEL,如果你希望返回其他的值,你需要调用 CDialog::EndDialog( int nResult );其中nResult 会作为DoModal()调用的返回值。 下面的代码演示了如何使用自己的函数来退出对话框:下载例子 void CMy52_s1View::OnLButtonDown(UINT nFlags, CPoint point) {//创建对话框并得到返回值

CView::OnLButtonDown(nFlags, point); CTestDlg dlg;

int iRet=dlg.DoModal();

CString szOut;

szOut.Format(\"return value %d\; AfxMessageBox(szOut); }

//重载OnOK,OnCancel void CTestDlg::OnOK() {//什么也不做

}

void CTestDlg::OnCancel() {//什么也不做 }

//在对话框中对三个按钮消息进行映射 void CTestDlg::OnExit1() {

CDialog::OnOK(); }

void CTestDlg::OnExit2() {

CDialog::OnCancel(); }

void CTestDlg::OnExit3()

{

CDialog::EndDialog(0XFF); }

由于重载了OnOK 和OnCancel 所以在对话框中按下Enter 键或Escape 键时都不会退出,只 有按下三个按钮中的其中一个才会返回。 此外在对话框被生成是会自动调用BOOL CDialog::OnInitDialog(),你如果需要在对话框显 示前对其中的控件进行初始化,你需要重载这个函数,并在其中填入相关的初始化代码。利 用ClassWizard 可以方便的产生一些默认代码,首先打开ClassWizard,选择相应的对话框类,

在右边的消息列表中选择WM_INITDIALOG 并双击,ClassWizard 会自动产生相关代码,代 码如下:

BOOL CTestDlg::OnInitDialog() {

/*先调用父类的同名函数*/ CDialog::OnInitDialog(); /*填写你的初始化代码*/ return TRUE; }

有关对对话框中控件进行初始化会在5.4 在对话框中进行消息映射中进行更详细的讲解 5.3 创建无模式对话框

无模式对话框与有模式对话框不同的是在创建后其他窗口都可以继续接收用户输入,因此无 模式对话框有些类似一个弹出窗口。创建无模式对话框需要调用

BOOL CDialog::Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );之后还需要调用

BOOL CDialog::ShowWindow( SW_SHOW);进行显示,否则无模式对话框将是不可见的。相 关代码如下:

void CYourView::OnOpenDlg(void) {

/*假设IDD_TEST_DLG 为已经定义的对话框资源的ID 号*/ CTestDlg *dlg=new CTestDlg; dlg>

Create(IDD_TEST_DLG,NULL); dlg>

ShowWindows(SW_SHOW); /*不要调用delete dlg;*/ }

在上面的代码中我们新生成了一个对话框对象,而且在退出函数时并没有销毁该对象。因为 如果此时销毁该对象(对象被销毁时窗口同时被销毁),而此时对话框还在显示就会出现错 误。那么这就提出了一个问题:什么时候销毁该对象。我时常使用的方法有两个:

在对话框退出时销毁自己:在对话框中重载OnOK 与OnCancel 在函数中调用父类的同名函 数,然后调用DestroyWindow()强制销毁窗口,在对话框中映射WM_DESTROY 消息,在消 息处理函数中调用delete this;强行删除自身对象。相关代码如下: void CTestDlg1::OnOK() {

CDialog::OnOK();

DestroyWindow(); }

void CTestDlg1::OnCancel() {

CDialog::OnCancel(); DestroyWindow();

}

void CTestDlg1::OnDestroy() {

CDialog::OnDestroy();

AfxMessageBox(\"call delete this\"); delete this; }

这种方法的要点是在窗口被销毁的时候,删除自身对象。所以你可以在任何时候调用 DestroyWindow() 以达到彻底销毁自身对象的作用。(DestroyWindow() 的调用会引起 OnDestroy()的调用)

通过向父亲窗口发送消息,要求其他窗口对其进行销毁:首先需要定义一个消息用于进行通 知,然后在对话框中映射WM_DESTROY 消息,在消息处理函数中调用消息发送函数通知 其他窗口。在接收消息的窗口中利用ON_MESSAGE 映射处理消息的函数,并在消息处理 函数中删除对话框对象。相关代码如下:

/*更改对话框的有关文件*/

CTestDlg2::CTestDlg2(CWnd* pParent /*=NULL*/) : CDialog(CTestDlg2::IDD, pParent)

{/*m_pParent 为一成员变量,用于保存通知窗口的指针,所以该指针不能是一个临时指针*/

ASSERT(pParent); m_pParent=pParent;

//{{AFX_DATA_INIT(CTestDlg2)

// NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT }

void CTestDlg2::OnOK() {

CDialog::OnOK(); DestroyWindow();

}

void CTestDlg2::OnCancel() {

CDialog::OnCancel(); DestroyWindow(); }

void CTestDlg2::OnDestroy() {

CDialog::OnDestroy();

/*向其他窗口发送消息,将自身指针作为一个参数发送*/ m_pParent>

PostMessage(WM_DELETE_DLG,(WPARAM)this); }

/*在消息接收窗口中添加消息映射*/ /*在头文件中添加函数定义*/

afx_msg LONG OnDelDlgMsg(WPARAM wP,LPARAM lP); /*添加消息映射代码*/

ON_MESSAGE(WM_DELETE_DLG,OnDelDlgMsg) END_MESSAGE_MAP() /*实现消息处理函数*/

LONG CMy53_s1View::OnDelDlgMsg(WPARAM wP,LPARAM lP) {

delete (CTestDlg2*)wP; return 0; }

/*创建对话框*/

void CMy53_s1View::OnTest2() {

CTestDlg2 *dlg=new CTestDlg2(this); dlg>

Create(IDD_TEST_DLG_2); dlg>

ShowWindow(SW_SHOW);

}

在这种方法中我们利用消息来进行通知,在Window 系统中利用消息进行通知和传递数据的 用法是很多的。

同样无模式对话框的另一个作用还可以用来在用户在对话框中的输入改变时可以及时的反 映到其他窗口。下面的代码演示了在对话框中输入一段文字,然后将其更新到视图的显示区 域中,这同样也是利用了消息进行通知和数据传递。

/*在对话框中取出数据,并向其他窗口发送消息和数据,将数据指针作为一个参数发送*/ void CTestDlg2::OnCommBtn() {

char szOut[30];

GetDlgItemText(IDC_OUT,szOut,30);

m_pParent>

SendMessage(WM_DLG_NOTIFY,(WPARAM)szOut); }

/*在消息接收窗口中*/

/*映射消息处理函数*/

ON_MESSAGE(WM_DLG_NOTIFY,OnDlgNotifyMsg) /*在视图中绘制出字符串m_szOut*/ void CMy53_s1View::OnDraw(CDC* pDC) {

CMy53_s1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here pDC>

TextOut(0,0,\"Display String\"); pDC>

TextOut(0,20,m_szOut); }

/*处理通知消息,保存信息并更新显示*/

LONG CMy53_s1View::OnDlgNotifyMsg(WPARAM wP,LPARAM lP) {

m_szOut=(char*)wP; Invalidate(); return 0; }

此外这种用法利用消息传递数据的方法对有模式对话框和其他的窗口间通信也一样有效。下 载本节例子

5.4 在对话框中进行消息映射

利用对话框的一个好处是可以利用ClassWizard 对对话框中各个控件产生的消息进行映射, ClassWizrd 可以列出各种控件可以使用的消息,并能自动产生代码。在本节中我们以一个例

子来讲解如何在对话框中对子窗口消息进行映射同时还讲解如何对对话框中的子窗口进行 初始化。

ID 类型

IDC_RADIO_TEST_1 圆形按钮 IDC_RADIO_TEST_2 圆形按钮 IDC_BUTTON_TEST 按钮 IDC_CHECK_TEST 检查按钮 IDC_TREE_TEST 树形控件 IDC_LIST_CTRL List Ctrl IDC_TAB_CTRL Tab Ctrl IDC_LIST_TEST 列表框 IDC_COMBO_TEST 组合框 IDC_EDIT_TEST 输入框

首先我们产生编辑好一个对话框,如图,在对话框中使用的控件和ID 号如下表: 首先我们需要在对话框的OnInitDialog()函数中对各个控件进行初始化,这里我们使用 CWnd* GetDlgItem( int nID )来通过ID 号得到子窗口指针。( 类似的函数还有UINT GetDlgItemInt( int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE ) 通过ID 号得到子

窗口中输入的数字,int GetDlgItemText( int nID, CString& rString ) 通过ID 号得到子窗口中

输入的文字)。代码如下:

BOOL CMy54_s1Dlg::OnInitDialog() {

CDialog::OnInitDialog(); /*添加初始化代码*/ //初始化输入框

((CEdit*)GetDlgItem(IDC_EDIT_TEST))> SetWindowText(\"thisis a edit box\"); //初始化列表框

CListBox* pListB=(CListBox*)GetDlgItem(IDC_LIST_TEST); pListB>

AddString(\"item 1\"); pListB>

AddString(\"item 2\"); pListB>

AddString(\"item 3\");

//初始化组合框

CComboBox* pCB=(CComboBox*)GetDlgItem(IDC_COMBO_TEST); pCB>

AddString(\"item 1\"); pCB>

AddString(\"item 2\"); pCB>

AddString(\"item 3\"); //初始化Tab Ctrl

CTabCtrl* pTab=(CTabCtrl*)GetDlgItem(IDC_TAB_TEST);

pTab>

InsertItem(0,\"TabPage1\"); pTab>

InsertItem(1,\"TabPage2\"); pTab>

InsertItem(2,\"TabPage3\"); //初始化ListCtrl

CListCtrl* pList=(CListCtrl*)GetDlgItem(IDC_LIST_CTRL); pList>

InsertColumn(0,\"Column 1\; pList>

InsertItem(0,\"Item1\"); pList>

InsertItem(1,\"Item2\"); pList>

InsertItem(2,\"Item3\");

//初始化TreeCtrl

CTreeCtrl* pTree=(CTreeCtrl*)GetDlgItem(IDC_TREE_TEST); pTree>

InsertItem(\"Node1\; HTREEITEMhNode=pTree> InsertItem(\"Node2\; pTree>

InsertItem(\"Node21\0,0,hNode);

pTree>

InsertItem(\"Node22\0,0,hNode); pTree>

Expand(hNode,TVE_EXPAND);

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

接下来我们需要利用ClassWizard 对控件所产生的消息进行映射,打开ClassWizard 对话框,

选中相关控件的ID,在右边的列表中就会显示出可用的消息。如我们对按钮的消息进行映 射,在选中按钮ID(IDC_BUTTON_TEST)后,会看到两个消息,如图,一个是BN_CLICKED, 一个是BN_DOUBLECLICKED。双击BN_CLICKED 后在弹出的对话框中输入函数名,

ClassWizard 会产生按钮被按的消息映射。

然后我们看看对TabCtrl 的TCN_SELCHANGE 消息进行映射,如图,在TabCtrl 的当前页选 择发生改变时这个消息会被发送,所以通过映射该消息可以在当前页改变时及时得到通知。 最后我们对输入框的EN_CHANGE 消息进行映射,如图,在输入框中的文本改变后该消息 会被发送。相关的代码如下:

//头文件中相关的消息处理函数定义 afx_msg void OnButtonTest();

afx_msg void OnSelchangeTabTest(NMHDR* pNMHDR, LRESULT* pResult);

afx_msg void OnChangeEditTest(); //}}AFX_MSG

5.5 在对话框中进行数据交换和数据检查

MFC 提供两种方法在对话框中进行数据交换和数据检查( Dialog data exchange/Dialog data

validation),数据交换和数据检查的思想是将某一变量和对话框中的一个子窗口进行关联, 然后通过调用BOOL UpdateData( BOOL bSaveAndValidate = TRUE )来指示MFC 将变量中 数据放入子窗口还是将子窗口中数据取到变量中并进行合法性检查。 DECLARE_MESSAGE_MAP() //CPP 文件中消息映射代码

ON_BN_CLICKED(IDC_BUTTON_TEST,OnButtonTest)

ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_TEST,OnSelchangeTabTest) ON_EN_CHANGE(IDC_EDIT_TEST,OnChangeEditTest) //}}AFX_MSG_MAP END_MESSAGE_MAP()

//消息处理函数

void CMy54_s1Dlg::OnButtonTest() {

AfxMessageBox(\"you pressed a button\"); }

void CMy54_s1Dlg::OnSelchangeTabTest(NMHDR* pNMHDR, LRESULT* pResult) {

TRACE(\"Tab Select changed\\n\"); *pResult = 0;

}

void CMy54_s1Dlg::OnChangeEditTest() {

TRACE(\"edit_box text changed\\n\"); }

对于其他的控件都可以采取类似的方法进行消息映射,下载例子。此外如果你对各种控件可 以使用的消息不熟悉,你可以通过使用对话框,然后利用ClassWizard 产生相关代码的方法 来进行学习,你也可以将ClassWizard 产生的代码直接拷贝到其他需要的地方(不瞒你说, 我最开始就是这样学的:D

这也算一个小窍门)。

在进行数据交换时一个子窗口可以和两种类型的变量相关联,一种是控件(Control)对象, 比如说按钮子窗口可以和一个CButton 对象相关联,这种情况下你可以通过该对象直接控制 子窗口,而不需要象上节中讲的一样使用GetDlgItem(IDC_CONTROL_ID)来得到窗口指针; 一种是内容对象,比如说输入框可以和一个CString 对象关联,也可以和一个UINT 类型变 量关联,这种情况下你可以直接设置/获取窗口中的输入内容。

而数据检查是在一个子窗口和一个内容对象相关联时在存取内容时对内容进行合法性检查, 比如说当一个输入框和一个CString 对象关联时,你可以设置检查CString 的对象的最长/最

小长度,当输入框和一个UINT 变量相关联时你可以设置检查UINT 变量的最大/最小值。在

BOOL UpdateData( BOOL bSaveAndValidate = TRUE )被调用后,合法性检查会自动进行, 如果无法通过检查MFC 会弹出消息框进行提示,并返回FALSE。

设置DDX/DDV 在VC 中非常简单,ClassWizard 可以替你完成所有的工作,你只需要打开 ClassWizard 并选中Member Variables 页,如图,你可以看到所有可以进行关联的子窗口ID

列表,双击一个ID 会弹出一个添加变量的对话框,如图,填写相关的信息后按下确定按钮 就可以了。然后选中你刚才添加的变量在底部的输入框中输入检查条件,如图。

下面我们看一个例子,对话框上的子窗口如图设置,各子窗口的ID 和关联的变量如下表: ID 关联的变量作用

IDC_CHECK_TEST BOOL m_fCheck 检查框是否被选中 IDC_RADOI_TEST_1 int m_iSel 当前选择的圆形按钮的索引

IDC_COMBO_TEST CString m_szCombo 组合框中选中的文本或是输入的文本 IDC_EDIT_TESTCString m_szEdit 输入框中输入的文本,最大长度为5 IDC_LIST_TESTCListBox m_lbTest 列表框对象

这时候ClassWizard 会自动生成变量定义和相关代码,在对话框的构造函数中可以对变量的 初始值进行设置,此外在BOOL CDialog::OnInitDialog()中会调用UpdateData(FALSE),即会

将变量中的数据放入窗口中。相关代码如下: //头文件中的变量定义,ClassWizard 自动产生 // Dialog Data

//{{AFX_DATA(CMy55_s1Dlg)

enum { IDD = IDD_MY55_S1_DIALOG }; CListBox m_lbTest; int m_iSel;

CString m_szEdit; CString m_szCombo; BOOL m_fCheck;

//}}AFX_DATA

//构造函数中赋初值

CMy55_s1Dlg::CMy55_s1Dlg(CWnd* pParent /*=NULL*/) : CDialog(CMy55_s1Dlg::IDD, pParent) {

//{{AFX_DATA_INIT(CMy55_s1Dlg) m_iSel = 1;

m_szEdit = _T(\"\"); m_szCombo = _T(\"\"); m_fCheck = FALSE; //}}AFX_DATA_INIT .....

}

//ClassWizard 产生的关联和检查代码

void CMy55_s1Dlg::DoDataExchange(CDataExchange* pDX) {

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CMy55_s1Dlg)

DDX_Control(pDX, IDC_LIST_TEST,m_lbTest); DDX_Radio(pDX, IDC_RADIO_TEST_1, m_iSel); DDX_Text(pDX, IDC_EDIT_TEST,m_szEdit); DDV_MaxChars(pDX, m_szEdit, 5);

DDX_CBString(pDX, IDC_COMBO_TEST, m_szCombo); DDX_Check(pDX, IDC_CHECK_TEST, m_fCheck); //}}AFX_DATA_MAP }

//在OnInitDialog 中利用已经关联过的变量m_lbTest BOOL CMy55_s1Dlg::OnInitDialog() {

CDialog::OnInitDialog(); ...

// TODO: Add extra initialization here //设置列表框中数据

m_lbTest.AddString(\"String1\"); m_lbTest.AddString(\"String2\"); m_lbTest.AddString(\"String3\");

m_lbTest.AddString(\"String4\");

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

//对两个按钮消息处理

//通过UpdateData(TRUE)得到窗口中数据 void CMy55_s1Dlg::OnGet() {

if(UpdateData(TRUE))

{

//数据合法性检查通过,可以使用变量中存放的数据 CString szOut;

szOut.Format(\"radio =%d \\ncheck is %d\\nedit input is %s\\ncomboBox input is %s\\n\m_iSel,m_fCheck,m_szEdit,m_szCombo); AfxMessageBox(szOut); }

//else 未通过检查 }

//通过UpdateData(FALSE)将数据放入窗口 void CMy55_s1Dlg::OnPut() {

m_szEdit=\"onPut test\"; m_szCombo=\"onPut test\";

UpdateData(FALSE); }

在上面的例子中我们看到利用DDX/DDV 和UpdateData(BOOL)调用我们可以很方便的存取

数据,而且也可以同时进行合法性检查。下载例子代码

5.6 使用属性对话框

属性对话框不同于普通对话框的是它能同时提供多个选项页,而每页都可以由资源编辑器以 编辑对话框的方式进行编辑,这样给界面开发带来了方便。同时使用上也遵守普通对话框的 规则,所以学习起来很方便。属性对话框由两部分构成:多个属性页(CPropertyPage)和属

性对话框(CPropertySheet)。

首先需要编辑属性页,在资源编辑器中选择插入,并且选择属性对话框后就可以插入一个属 性页,如图,或者选择插入一个对话框,然后将其属性中的Style 设置为Child,Border 设置

为Thin 也可以,如图。然后根据这个对话框资源生成一个新类,在选择基类时选择 CPropertyPage,ClassWizard 会自动生成相关的代码。

而对于CPropertySheet 也需要生成新类,并且将所有需要加入的属性页对象都作为成员变 量。属性对话框也分为有模式和无模式两种,有模式属性对话框使用DoModal()创建,无模 式属性对话框使用Create()创建。下面的代码演示了如何创建属性对话框并添加属性页: //修改CPropertySheet 派生类的构造函数为如下形式 CSheet::CSheet()

:CPropertySheet(\"test sheet\{

m_page1.Construct(IDD_PAGE_1); m_page2.Construct(IDD_PAGE_2); AddPage(&m_page1); AddPage(&m_page2); }

//创建有模式属性对话框 void CMy56_s1Dlg::OnMod() {

CSheet sheet; sheet.DoModal(); }

//创建无模式属性对话框 void CMy56_s1Dlg::OnUnm() {

CSheet *sheet=new CSheet; sheet> Create(); }

对于属性对话框可以使用下面的一些成员函数:

CPropertyPage* CPropertySheet::GetActivePage( )得到当前活动页的指针。 BOOL CPropertySheet::SetActivePage( int nPage )用于设置当前活动页。 int CPropertySheet::GetPageCount()用于得到当前页总数。

void CPropertySheet::RemovePage( int nPage )用于删除一页。 而对于属性页来将主要通过重载一些函数来达到控制的目的:

void CPropertyPage::OnOK() 在属性对话框上按下“确定”按钮后被调用

void CPropertyPage::OnCancel() 在属性对话框上按下“取消”按钮后被调用

void CPropertyPage::OnApply() 在属性对话框上按下“应用”按钮后被调用

void CPropertyPage::SetModified( BOOL bChanged = TRUE ) 设置当前页面上的数据被修改

标记,这个调用可以使“应用”按钮为允许状态。

此外利用属性对话框你可以生成向导对话框,向导对话框同样拥有多个属性页,但同时只有 一页被显示,而且对话框上显示的按钮为“上一步”,“下一步”/“完成”,向导对话框 会按照你添加页面的顺序依次显示所有的页。在显示属性对话框前你需要调用void CPropertySheet::SetWizardMode() 。使用向导对话框时需要对属性页的BOOL CPropertyPage::OnSetActive( ) 进行重载, 并在其中调用void

CPropertySheet::SetWizardButtons( DWORD dwFlags )来设置向导对话框上显示的按钮。 dwFlags 的取值可为以下值的“或”操作: PSWIZB_BACK 显示“上一步”按钮 PSWIZB_NEXT 显示“下一步”按钮

PSWIZB_FINISH 显示“完成”按钮

PSWIZB_DISABLEDFINISH 显示禁止的“完成”按钮

void CPropertySheet::SetWizardButtons( DWORD dwFlags )也可以在其他地方调用,比如说在

显示最后一页时先显示禁止的“完成”按钮,在完成某些操作后再显示允许的“完成”按钮。 在使用向导对话框时可以通过重载一些函数来达到控制的目的:

void CPropertyPage::OnWizardBack() 按下了“上一步”按钮。返回0 表示有系统决定需要显

示的页面,1

表示禁止页面转换,如果希望显示一个特定的页面需要返回该页面的ID 号。

void CPropertyPage::OnOnWizardNext() 按下了“ 下一步” 按钮。返回值含义与void CPropertyPage::OnWizardBack()相同。

void CPropertyPage::OnWizardFinish() 按下了“完成”按钮。返回FALSE 表示不允许继续,

否则返回TRUE 向导对话框将被结束。

在向导对话框的DoModal()返回值为ID_WIZFINISH 或IDCANCEL。下面的代码演示了如 何创建并使用向导对话框: //创建有模式向导对话框 void CMy56_s1Dlg::OnWiz() {

CSheet sheet;

sheet.SetWizardMode();

int iRet=sheet.DoModal();//返回ID_WIZFINISH 或IDCANCEL }

//重载BOOL CPropertyPage::OnSetActive( )来控制显示的按钮 BOOL CPage1::OnSetActive() {

((CPropertySheet*)GetParent())>

SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT); return CPropertyPage::OnSetActive();

}

BOOL CPage2::OnSetActive() {

((CPropertySheet*)GetParent())>

SetWizardButtons(PSWIZB_BACK|PSWIZB_FINISH); return CPropertyPage::OnSetActive(); }

下载本节例子。

5.7 使用通用对话框

在Windows 系统中提供了一些通用对话框如:文件选择对话框如图,颜色选择对话框如图, 字体选择对话框如图。在MFC 中使用CFileDialog,CColorDialog,CFontDialog 来表示。一

般来讲你不需要派生新的类,因为基类已经提供了常用的功能。而且在创建并等待对话框结 束后你可以通过成员函数得到用户在对话框中的选择。

CFileDialog 文件选择对话框的使用:首先构造一个对象并提供相应的参数,构造函数原型 如下:

CFileDialog::CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY |

OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );参 数意义如下:

bOpenFileDialog 为TRUE 则显示打开对话框,为FALSE 则显示保存对话文件对话框。 lpszDefExt 指定默认的文件扩展名。 lpszFileName 指定默认的文件名。 dwFlags 指明一些特定风格。

lpszFilter 是最重要的一个参数,它指明可供选择的文件类型和相应的扩展名。参数格式如:

\"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files

(*.*)|*.*||\";文件类型说明和扩展名间用| 分隔,同种类型文件的扩展名间可以用; 分割,每

种文件类型间用| 分隔,末尾用|| 指明。 pParentWnd 为父窗口指针。

创建文件对话框可以使用DoModal(),在返回后可以利用下面的函数得到用户选择:

CString CFileDialog::GetPathName( ) 得到完整的文件名, 包括目录名和扩展名如: c:\est\est1.txt

CString CFileDialog::GetFileName( ) 得到完整的文件名,包括扩展名如:test1.txt CString CFileDialog::GetExtName( ) 得到完整的文件扩展名,如:txt

CString CFileDialog::GetFileTitle ( ) 得到完整的文件名,不包括目录名和扩展名如:test1

POSITION CFileDialog::GetStartPosition( ) 对于选择了多个文件的情况得到第一个文件位

置。

CString CFileDialog::GetNextPathName( POSITION& pos ) 对于选择了多个文件的情况得到

下一个文件位置, 并同时返回当前文件名。但必须已经调用过POSITION

CFileDialog::GetStartPosition( )来得到最初的POSITION 变量。

CColorDialog 颜色选择对话框的使用:首先通过CColorDialog::CColorDialog( COLORREF clrInit = 0, DWORD dwFlags = 0, CWnd* pParentWnd = NULL )构造一个对象,其中clrInit 为

初始颜色。通过调用DoModal() 创建对话框, 在返回后调用COLORREF CColorDialog::GetColor( )得到用户选择的颜色值。

CFontDialog 字体选择对话框的使用:首先构造一个对象并提供相应的参数,构造函数原型 如下:

CFontDialog::CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL );构造一个对象,其中参数lplfInitial 指向一个LOGFONG 结构(该结构介绍请见2.2 在窗 口中输出文字),如果该参数设置为NULL 表示不设置初始字体。pdcPrinter 指向一个代表 打印机设备环境的DC 对象,若设置该参数则选择的字体就为打印机所用。pParentWnd 用 于指定父窗口。通过调用DoModal()创建对话框,在返回后通过调用以下函数来得到用户选 择:

void CFontDialog::GetCurrentFont( LPLOGFONT lplf );用来获得所选字体的属性。该函数有一

个参数,该参数是指向LOGFONT 结构的指针,函数将所选字体的各种属性写入这个 LOGFONT 结构中。

CString CFontDialog::GetFaceName( ) 得到所选字体名字。

int CFontDialog::GetSize( ) 得到所选字体的尺寸(以10 个象素为单位)。 COLORREF CFontDialog::GetColor( ) 得到所选字体的颜色。 BOOL CFontDialog::IsStrikeOut( ) BOOL CFontDialog::IsUnderline( ) BOOL CFontDialog::IsBold( ) BOOL CFontDialog::IsItalic( )

得到所选字体的其他属性,是否有删除线,是否有下划线,是否为粗体,是否为斜体 5.8 建立以对话框为基础的应用

我认为初学者使用以对话框为基础的应用是一个比较好的选择,因为这样一来可以摆脱一些 开发界面的麻烦,此外也可以利用ClassWizard 自动的添加消息映射。

在VC 中提供了生成“以对话框为基础的应用”的功能,你所需要选择的是在使用AppWizard 的第一步选择“对话框为基础的应用”,如图。VC 会生成包含有应用派生类和对话框派生 类的代码。在应用类的InitInstance()成员函数中可以看到如下的代码: BOOL CMy58_s1App::InitInstance() {

CMy58_s1Dlg dlg;

m_pMainWnd = &dlg;

int nResponse = dlg.DoModal(); if (nResponse == IDOK) {

// TODO: Place code here to handle when the dialog is // dismissed with OK }

else if (nResponse == IDCANCEL)

{

// TODO: Place code here to handle when the dialog is // dismissed with Cancel }

return FALSE; }

这是产生一个有模式对话框并创建它,在对话框返回后通过返回FALSE 来直接退出。在设 计时通过编辑对话框资源你可以设计好界面,然后通过ClassWizard 映射消息来处理客户的 输入,由于前几节已经讲过本节也就不再重复。

同样基于对话框的应用也同样可以使用属性对话框做为界面,或者是通过使用经过派生的通 用对话框作为界面。

提示:当你使用有模式对话框时最开始是无法隐藏窗口的,而只能在对话框显示后再隐藏窗 口,所以这会造成屏幕的闪烁。一个解决办法就是采用无模式的对话框,无模式的对话框在 创建后是隐藏的,直到你调用ShowWindow(SW_SHOW)才会显示。相关代码如下: BOOL CMy58_s1App::InitInstance() {

//必须新生成一个对象,而不能使用局部变量 CMy58_s1Dlg* pdlg=new CMy58_s1Dlg; m_pMainWnd = pdlg; pdlg>

Create(); return TRUE; }

5.9 使用对话框作为子窗口

使用对话框作为子窗口是一种很常用的技术,这样可以使界面设计简化而且修改起来更加容 易。

简单的说这种技术的关键就在于创建一个无模式的对话框,并在编辑对话框资源时指明 Child 风格和无边框风格,如图。接下来利用产生一个CDialog 的派生类,并进行相关的消 息映射。在创建子窗口时需要利用下面的代码:

int CMy59_s1View::OnCreate(LPCREATESTRUCT lpCreateStruct) {

if (CView::OnCreate(lpCreateStruct) == 1) return 1;

//创建子窗口

m_dlgChild.Create(IDD_CHILD_DLG,this); //重新定位

m_dlgChild.MoveWindow(0,0,400,200); //显示窗口

m_dlgChild.ShowWindow(SW_SHOW); return 0;

}

此外还有一中类似的技术是利用CFormView 派生类作为子窗口,在编辑对话框资源时也需 要指明Child 风格和无边框风格。然后利用ClassWizard 产生以CFormView 为基类的派生

类,

但是由于该类的成员函数都是受保护的,所以需要对产生的头文件进行如下修改: class CTestForm : public CFormView {

//将构造函数和构析函数改为共有函数 public:

CTestForm();

virtual ~CTestForm();

DECLARE_DYNCREATE(CTestForm) ... }

有关创建子窗口的代码如下:

int CMy59_s1View::OnCreate(LPCREATESTRUCT lpCreateStruct) {

if (CView::OnCreate(lpCreateStruct) == 1) return 1;

//对于CFormView 派生类必须新生成对象而不能使用成员对象 m_pformChild = new CTestForm;

//由于CFormView 的成员受保护,所以必须对指针进行强制转换 CWnd* pWnd=m_pformChild; pWnd>

Create(NULL,NULL,WS_CHILD|WS_VISIBLE,CRect(0,210,400,400),this,1001,NULL); return 0; }

最后你会看到如图的窗口界面,上方的对话框子窗口和下方的FormView 子窗口都可以通过 资源编辑器预先编辑好,下载代码。

提示:对于CFormView 派生类必须新生成对象而不能使用成员对象,因为在CView 的 OnDestroy()中会有如下代码:delete this;所以使用成员对象的结果会造成对象的二次删除而

引发异常。__

因篇幅问题不能全部显示,请点此查看更多更全内容

Top