天天看点

MFC 之 对话框编程

下面我们来设计这个程序的UI,首先在“视图”菜单中选择“工具箱”,这个工具箱窗口中,列出了很多常用的Windows控件(如按钮、复选框、编辑框、标签等),点击一个控件,此时鼠标变为十字型,像使用画图工具一样在设计器上绘出该控件,也可以同过拖拽的方法将控件直接拖到UI设计器中的对话框上,还可以直接双击工具栏中的控件。

在UI设计器上的静态标签“TODO: 在此放置对话框控件。”可以将其删掉。按照上述方法在对话框上绘制一个按钮控件,右击此控件,选择“属性”会出现属性窗口,在这个窗口中,左侧是控件的一种属性,右侧是该属性的当前值,在属性窗口的低端是属性的具体含义。对于按钮控件,我们现在关注Caption属性,即按钮上显示的文字,我们将其设为“Hello World”,输入完毕后按回车键确定。在UI设计器上的那个按钮已经变为“Hello World”了。在看一下按钮的ID属性,这个ID表示对话框每个控件唯一的标识(注意ID为IDC_STATIC的控件代表该控件为静态的,在程序运行时不可以动态的改变改控件的属性,如标签控件。但我们亦可以通过,修改标签的ID改为非IDC_STATIC值,这样这个控件就转为动态控件了,此种控件可以用IDC_STATIC来标识,IDC_STATIC没有唯一的限制,),我们一般需要将该属性值改为更有意义的,如IDC_HELLOWORLD。

下面我们为按钮控件编写事件响应代码,右击Hello World按钮,选择“添加事件处理程序”,依然弹出了一个向导对话框,在消息类型中选择选择按钮要响应的消息,在这个程序中我们是要响应按钮的单击事件(其实在一般情况下,按钮就是用来单击的),所以保持默认“BN_CLICKED”,在类列表中说明了响应代码是作为哪个类的成员函数,默认选择那个以“Dlg”结尾的类,这个类是继承于CDialog类的一个派生类。程序员可以设置自己的“函数处理程序名称”。完成后点击“添加编辑”按钮,进入代码编辑器编辑代码(对于按钮这种简单控件,我们要添加其响应代码,一般在UI设计器中直接双击这个控件即可直接进入到代码编辑器中编辑响应默认消息的函数)。

系统为我们自动在CHelloWorldDlg类中添加了OnBnClickedHelloworld()函数,该函数的声明部分在HelloWorldDlg.h文件中,在VS9开发环境的右侧“解决方案资源管理器”中双击该文件,或在HelloWorldDlg.cpp文件中右击,在弹出菜单中选择“转到头文件”。让我们来看一下这个MFC框架是如何构成的。

在这个头文件的头部有一行“#pragma once”,这是条编译命令,功能是让次头文件(Header)在编译时只被编译一次,因为同一个头文件可能被包含(include)过多次。在这个头文件中定义了CHelloWorld类,在这个类中声明了一个HICON类型的m_hIcon成员变量,这是个描述该应用程序图标的变量。如果你不了解HICON这个非标准类型,可以在代码的HICON处右击鼠标,然后选择“转到声明”或“转到定义”,这是VS9会自动定位光标到HICON的声明或定义代码处,笔者认为这是一个非常体贴的功能,为团队开发提供了很大的便利。

这里,我们顺便来简单谈谈MFC采用的“匈牙利标识符命名法”,这是一个约定,可以增加代码的可读性。如果你声明或定义了一个类,那么这个类可以一“C”(class)为前缀,如CHelloWorldDlg类,如果要定义一个无符号型的局部变量,那么可以用“u”(unsigned)为前缀,如UINT uPort; ULONG uFlags;如果是int或long类型的变量则以“n”为前缀,DWORD类型的变量前缀为“dw”,字符数组以“sz”作为前缀,CString类的对象以“str”作为前缀,指针以“lp”或“p”(long pointer或pointer,在WIN32环境下这两种指针并没有什么差别)作为前缀,引用以“r”为前缀,布尔型变量以“b”为前缀,句柄型的变量以“h”(handle)作为前缀。如果变量是全局的,那么以“g_”(global)开头,如BOOL g_bFlags;如果是类的成员变量则以“m_”(member)开头,如HICON m_hIcon;。

讨论完MFC的命名规则,让我们继续看这个头文件,在此类中声明了一个标准构造函数CHelloWorldDlg(CWnd* pParent = NULL);可选形参CWnd* pParent指定此对话框的父窗口。

代码enum { IDD = IDD_HELLOWORLD_DIALOG };的意思是MFC巧妙的将ID为IDD_HELLOWORLD_DIALOG的对话框资源与CHelloWorldDlg类绑定在一起。

这些除外的成员函数都是对父类CDialog中成员函数的重载。

成员函数virtual void DoDataExchange(CDataExchange* pDX);是用来支持DDX(对话框数据交换,将一个变量和一个控件进行绑定的时候用DDX)和DDV(对话框数据效验,检验该控件是否为你所需要的时候用DDV)机制的成员函数。

成员函数virtual BOOL OnInitDialog();是在对话被创建(Create)后立即被执行的函数,因此在这里可以添加对话框的初始化所需要的自定义代码。

成员函数afx_msg void OnSysCommand(UINT nID, LPARAM lParam);是对话框的处理WM_SYSCOMMAND消息的函数。WM_SYSCOMMAND消息是关于系统控制的消息,如鼠标在标题栏上的操作等。

成员函数afx_msg void OnPaint();是对话框处理WM_PAINT的函数,当对话框窗体发生重绘时有WM_PAINT消息到达程序。

成员函数afx_msg HCURSOR OnQueryDragIcon();当用户拖动最小化窗口时系统调用此函数取得光标显示。

接下来是一个DECLARE_MESSAGE_MAP()宏,这是MFC处理消息用的,具体资料可以查阅MSDN。

最下一行afx_msg void OnBnClickedHelloworld();就是刚自动生成的处理按钮单击消息的处理函数。

读者可能注意到了afx_msg,在MFC中,只要是处理消息的响应函数,在声明时,都要加上afx_msg。通过查找其定义,笔者认为这个并没有什么功能性意义,只是一个占位用的或是一个说明标识。

我们分析完HelloWorldDlg.h文件后,再来看看HelloWorldDlg.cpp文件。在文件的include部分,包含了stdafx.h文件,读者可以在“”stdafx.h””上右击鼠标,打开该头文件,在这个头文件中包含了该MFC程序必要的文件,如果程序员要添加自定义类,如果类中还需要MFC支持,那么必须包含这个头文件。HelloWorld.h文件中定义了ChelloWorldApp类(继承于CWinApp),是MFC的应用程序类。一个MFC WIN32应用程序必须是在CWinApp对象之上的,在CHelloWorldApp类中的InitInstance()成员函数的定义中,有相应加载该对话框的代码(CHelloWorldDlg dlg;之后的代码)。

然后是一组宏定义#ifdef _DEBUG #define new DEBUG_NEW #endif,_DEBUG宏对应前面提到的Debug模式。

接下来定义了另一个对话框类CAboutDlg,这是个“关于”对话框的类,用来显示程序版权信息的。

来看看BEGIN_MESSAGE_MAP() …… END_MESSAGE_MAP()这组宏定义,在这中间的代码就是MFC用来指定哪个消息用哪个成员函数来响应。对于那些无需指定的,MFC则没有明确指定,如ON_WM_PAINT()是指定处理WM_PAINT消息的成员函数是OnPaint(),但,这个用户一般不会去更改,所以在这里就没有去明确指定。但ON_BN_CLICKED()中,却指定了具体的成员函数:ID值为IDC_HELLOWORLD的控件的BN_CLICKED消息由CHelloWorldDlg类的OnBnClickedHelloworld()成员函数来处理。

在看看OnInitDialog()函数的定义部分,首先调用了父类了初始化函数,接下来到SetIcon前,是将“关于”加载到了系统菜单中,这样用户在单击程序窗体左上角的图标时,或右击标题栏时,或右击任务栏长条图标时,弹出的菜单就会包含“关于”的菜单项。

SetIcon()的后面有相应注释:“设置大图标”和“设置小图标”。

在OnSysCommand()函数中,处理了有关标题栏上发生的事件,代码(nID & 0xFFF0) == IDM_ABOUTBOX表示,如果用户点击了标题栏系统菜单中的“关于”菜单项,显示“关于”对话框。

在OnPaint()函数中的代码,是用来实现重绘操作的。

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

编辑框(Edit)控件。以ID为IDC_INPUT的编辑控件为例:

获取/设置编辑框内的文字、设置其可用状态和设置其可见状态等方法与操作按钮的方法类似,在次就不再赘述了。

我们知道我们通过GetDlgItem()函数是获取的其文本内容,如果想要获取其数值,则可以用int nValue = _ttoi(strInput);的方法来转换。这里介绍一个DDX的方法,将一个成员变量与一个目标控件进行绑定,这样变量的值就可以用来反应控件的值了,同样,也可以通过设置这个变量的值,来控制控件的值。下面来看具体步骤:在工具箱中,拖拽一个编辑框到对话框上,ID设为IDC_INPUT。右键点击这个控件,选择“添加变量”菜单项,在弹出的“添加成员变量向导”对话框中设置其访问属性(笔者建议用protected);在类别列表框中选择Value(默认是Control);在变量类型组合框中选择一种变量类型,这里选择int;输入变量名m_nValue;最小值、最大值、最大字符数和注释,根据实际情况选填(这里略过);点击完成按钮。完成“添加变量”向导后,在HelloWorldDlg.cpp文件中的CHelloWorldDlg::DoDataExchange()中,系统自动添加了一条语句:DDX_Text(pDX, IDC_INPUT, m_nValue);这条语句的意思是将ID为IDC_INPUT的控件与m_nValue成员变量进行绑定。当程序执行UpdateData()的时候,数据便开始进行交换,数据交换方向由UpdateData的参数确定。

在对话框上绘制一个按钮,Caption设为“判断”,双击编写其消息响应代码:

void CHelloWorldDlg::OnBnClickedButton1()

{

               // TODO: 在此添加控件通知处理程序代码

               UpdateData(TRUE);

               if (m_nValue >= 0) MessageBox(_T("大于等于零"));

               else MessageBox(_T("小于零"));

          }

这里,UpdateData(TRUE);就是让文本框的值更新到m_nValue里,如果是UpdateData(FALSE);就是将变量中的数据返回给文本框里。在对话框初始化时,MFC会默认调用一次UpdateData(FALSE);,用来初始化控件中的显示内容;在对话框结束时,MFC会调用一次UpdateData(TRUE);,用来更新控件数据到成员变量中去。

还可以为为控件添加控件变量,即在“添加变量向导”中的变量类别列表中选择Control,则系统会添加一个MFC的控件类的对象作为改对话框的成员变量,并且将这个控件对象与响应的控件进行绑定,这样操作这个控件对象就相当于操作相应的控件。

复选框(Check)和单选框(Radio)控件。分别以ID为IDC_CHECK1和IDC_RADIO1的控件为例:

获取/设置其选中状态:

成员函数原型:

int GetCheck() const;

     void SetCheck(int nCheck);

这两个函数都是CButton类的成员函数,所以在用GetDlgItem()获取CWnd指针后,一定要强制转换为CButton的指针类型。

例子:

              BOOL bState;

          bState = ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck();     //获取复选框状态

     ((CButton*)GetDlgItem(IDC_RADIO1))->SetCheck(1);             //设置单选框状态为选中

注意,当复选框的Tri-State属性为True的时候,那么复选框可以有三种状态,在SetCheck()的时候可以通过传给0、1或2来设置状态。

操作复选框和单选框的状态,同样也可以采用DDX的方法,添加一个int型的成员变量来与控件进行绑定。

列表框(List)和组合框(Combo),在此只介绍成员函数原型:

列表框常用成员函数:

void ResetContent();                                                                    //清空列表

         int AddString(LPCTSTR lpszItem);                                             //追加列表项

         int DeleteString(UINT nIndex);                                                   //删除指定项

         int SetSel(int nIndex, BOOL bSelect = TRUE);                           //设置指定项的选择状态

         int SetCurSel(int nSelect);                                                            //设置当前选中项

         int GetCurSel() const;                                                                  //获取当前选中项

         int GetSelItems(int nMaxItems, LPINT rgIndex) const;               //获取所有选中项

         int GetSelCount() const;                                                              //获取选中项总数

         int GetTextLen(int nIndex) const;                                                 //获取指定项的文本长度

         int GetText(int nIndex, LPTSTR lpszBuffer) const;                      //获取指定项文本

         void GetText(int nIndex, CString &rString) const;

int FindString(int nStartAfter, LPCTSTR lpszItem) const;          //查找指定项

对于组合框来说,可以理解为由编辑框和列表框组合而成的复合控件,因此其MFC中的成员函数与也可以参照编辑框和列表框控件类的成员函数。

对话框编程就介绍到此了,更多具体资料还请参看Microsoft的MSDN,MSDN的简体中文网址为:http://msdn.microsoft.com/。MSDN是Microsoft最大的资料库。

继续阅读