MFC控件使用详细教程

MFC控件使用详细教程
MFC控件使用详细教程

使用Windows标准控件

我们在前面曾提到过,控件是一些行为标准化了的窗口,一般用于对话框或其它窗口中充当与用户交互的元素。在Visual C++中,可以使用的控件分成三类:

(1) Windows标准控件

Windows标准控件由Windows操作系统提供,在Windows 95中还提供了一些新增的控件。所有这些控件对象都是可编程的,我们可以使用Visual C++提供的对话框编辑器把它们添加到对话框中。Microsoft基础类库(MFC)提供了封装这些控件的类,它们列于表6.1。

表6.1 Windows标准控件

续表6.1

前面提到过,在MFC中,类CWnd是所有窗口类的基类,很自然的,它也是所有控件类的基类。Windows标准控件在以下环境下提供:

?Windows 95

?Windows NT 3.51及以后版本

?Win32s 1.3

?注意:

?Visual C++ 4.2及以后版本不再支持Win32s。

(2) ActiveX控件

ActiveX控件可用于对话框中,也可用于HTML文档中。这种控件过去被称为OLE 控件。本书将在专门的章节中来讲述关于ActiveX控件的知识。这里仅指出ActiveX控件使用了与标准控件完全不同的接口和实现方法。

(3) 其它MFC控件类

除了Windows标准控件和自己编写的或者来自于第三方软件开发商的ActiveX 控件以外,MFC还提供了另外三种控件,它们由下面的三个类进行封装:

?类CBitmapButton用于创建以位图作为标签的按钮,位图按钮最多可以包括四个位图图片,分别代表按钮的四种不同状态。

?类CCheckListBox用于创建选择列表框,这种列表框中的每一项前面有一个复选框,以决定该项是否被选中。

?类CDragListBox用于创建一种特殊的列表框,这种列表框允许用户移动列表项。

在本章我们仅讲述第一类控件,即Windows标准控件。所涉及的内容包括各个控件的使用及相应的技巧。

第一节使用对话框编辑器和ClassWizard

对于大多数Windows标准控件,我们一般都使用对话框编辑器来将它们添加到对话框中。

图6. 1 在ResourceView中选择对话框

IDD_DIALOGDEMO_DIALOG

图6. 2 控件的Properties对话框

图6. 3 对话框编辑器的Controls工具窗口

在下面的过程中,我们将一个编辑框控件添加到在第四章创建的基于对话框的MFC框架应用程序的主对话框窗口中。

1. 首先,在Workspace窗口的ResourceView选项内双击DialogDemo resources\Dialog节点下的IDD_DIALOGDEMO_DIALOG图标。上面的操作如图所示。

2. 用鼠标选中标有“要做……”的静态文本控件。右击鼠标,从上下文菜单中选择Properties,打开如图6.2所示的对话框,在Caption文本框中输入新的控件文本:“在下面的文本框中输入一些字符”,然后将静态文本控件拖动到对话框的左上角。

3. 从Controls工具窗口(如图6.3所示,如果在你的资源编辑器中看不到该工具窗口,可以在工具条上右击鼠标,从上下文菜单中选择Controls)中选择编辑

控件图标,在对话框中绘制一个编辑框控件,如图6.4所示。

在该编辑框控件的Properties窗口的General选项卡中输入其ID为IDC_EDIT。然后在Styles选项卡下将Multiline复选框划上勾,并消除Auto HScroll复选框前的勾。

4. 右击该编辑框控件,从上下文菜单中选择ClassWizard命令,打开ClassWizard对话框,该对话框看起来如图6.5所示。

图6. 4 向对话框中添加一个编辑框控件

图6. 5 ClassWizard对话框

单击Member Variables选项卡,确信在Project处选择了DialogDemo,在Class name处选择了CDialogDemoDlg。现在我们为刚才添加的编辑框控件IDC_EDIT

添加一个数据映射入口。在Control IDs处选择IDC_EDIT,单击右边的Add Viable 按钮。打开如图6.6所示的对话框。

在Member variable name处链接变量名m_strEdit (这里m表示该变量为类CDialogDemoDlg的一个成员变量,str表明其类型为字符串,即类CString),在Category下拉列表中选择Value (另一种选择是Control,两种选择的不同将在后面的内容中讲述),在Variable type下拉列表中选择CString (还有其它很多数据类型可供选择,但由于这里编辑框中的内容为一字符串,因此CString 是最恰当的选择)。单击OK关闭对话框。

图6. 6 为控件映射添加成员变量

5. 检查一下现在的ClassWizard对话框(图

6.7)与图6.5相比有何不同。在图

6.7所示的对话框中下方的Maximum characters文本框中输入50。由字面意思可以很容易猜出其含义,即将编辑框IDC_EDIT中可能的最长字符串的大小限制为50。单击OK关闭对话框。

图6. 7 使用ClassWizard设置数据验证方案

6. 从Workspace窗口的ClassView中双击类CDialogDemoDlg的OnInitDialog 成员函数,使用下面的代码来代替位于语句

return TRUE;

前的// TODO注释:

m_strEdit="您好! 请在这里输入一些字符串。";

UpdateData(FALSE);

7. 在ClassView中双击类CDialogDemoApp的InitInstance成员函数,使用下面的代码来找替位于选择支

if (nResponse == IDOK)

下的//TODO注释:

AfxMessageBox(dlg.m_strEdit);

然后将同一成员函数中的下面的代码行删掉(或注释掉):

m_pMainWnd = &dlg;

8. 编译并运行该应用程序。显示如图6.8所示的对话框。

图6. 8 示例程序DialogDemo的运行结果

在图6.8所示的文本框中输入一些字符,单击“确定”。随即弹出如图6.9所示的消息框。该消息框复述了用户在图6.8所示的对话框中的输入。我们还发现,在图6.8所示的对话框中,当输入字符串达到一定的长度之后,我们不可以再输入更多的字符,这是我们在前面设置了Maximum characters为50的结果。

图6. 9 以消息框的形式反馈输入的字符串

下面我们来看在上面的步骤中都完成了什么。首先我们使用资源编辑器向对话框模板中添加这些标准控件,这一步的概念很清晰,因此并不难理解。

然后,我们打开了所绘制的编辑框的Properties (属性)对话框。先将其控件ID 设置为IDC_EDIT。这时如果打开头文件Resource.h,就会发现宏IDC_EDIT被定义为常量1001。不过,事实上在很多情况下我们并不需要关心每一控件的ID的

具体值,而只需要记住相应的助记符。对于这里的编辑框控件,我们只需要记住IDC_EDIT即可,而不需要关心它等于1001。接着,我们在Styles选项卡中设置了Multiline属性,同时清除了Auto HScroll属性,两者共同作用使用得编辑框IDC_EDIT支持多行文本,并且如文本行的长度超过编辑框宽度时自动回行。

下面的步骤是最重要的一步,我们动用了功能强大的工具ClassWizard。首先,我们将编辑框与一个CString对象相关联,这使用了一种被称为Dialog Data Exchange (DDX)的机制。在这种机制中,我们先在处理函数OnInitDialog或对话框类的构造函数中对对话框对象的成员变量进行初始化,在对话框显示之前,框架的DDX机制将成员变量的值传递给对话框中的控件。这个过程在成员函数DoModal或Create被调用的过程中发生。类CDialog中对OnInitDialog成员函数的默认实现调用了类CWnd成员函数UpdateData来初始化对话框中的控件。这时我们就可以看到前面的第6步还可在具有下面的几种变通方案:

1. 将代码行

m_strEdit="您好! 请在这里输入一些字符串。";

移到对基类的OnInitDialog成员函数的调用之前,即位于下面的代码之前:CDialog::OnInitDialog();

2. 将代码

m_strEdit="您好! 请在这里输入一些字符串。";

移到类CDialogDemoDlg的构造函数中。

对于上面的两种方法,与前面第6步中使用的方法相比,我们没有必要调用类CWnd的成员函数UpdateData。因为该函数在类CDialog的成员函数OnInitDialog中将被调用。

这三种方法之间并没有明确的优劣之分,在很多情况下,它们分别适用于不同的场合。

这里我们说一下成员函数UpdateData。该函数带有一个布尔类型的参数,如果该参数为FALSE,函数UpdateData将成员变量的值传递给对话框的变量;而如果该参数为TRUE,函数UpdateData将进行相反的过程。

如果用户单击了对话框中ID为IDOK的按钮,或者以TRUE为参数调用函数UpdateData,DDX机制从控件中将值传递到成员变量,同时对话框数据验证(dialog data validation,DDV)机制根据设定的验证规则验证所有数据项。

在数据交换的过程中,成员函数UpdateData先创建一个CDataExchange对象,然后调用对话框对类CDialog成员函数DoDataExchange的重载版本。该

CDataExchange对象将作为成员函数DoDataExchange的一个参数,该参数定义了数据交换的上下文。

在DoDataExchange中,我们为每一个数据成员指定了一个对DDX函数的调用。每一个函数定义了基于由成员函数UpdateData所提供的CDataExchange参数所确定的上下文而进行的双向数据交换。

下面的代码摘自实现文件DialogDemo.cpp中对函数DoDataExchange的定义:void CDialogDemoDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CDialogDemoDlg)

DDX_Text(pDX, IDC_EDIT, m_strEdit);

DDV_MaxChars(pDX, m_strEdit, 50);

//}}AFX_DATA_MAP

}

在两行注释//{{AFX_DATA_MAP和//}}AFX_DATA_MAP之间的代码部分称作数据映射。函数DDX_Text使用CString对象m_strEdit与ID为IDC_EDIT的编辑框控件相关联。函数DDV_MaxChars设置与编辑框控件IDC_EDIT相关联CString对象m_strEdit的最大长度为50。

需要注意的是,如果用户在模式对话框中单击了“取消”(Cancel)按钮,DoModal 函数将返回值IDCANCEL,在这种情况下,在对话框和对话框对象之前的数据交换不会发生。

由于这个原因,如果DoModal函数返回了值IDOK,我们可以使用下面的代码来复述用户在对话框中所输入的值:

AfxMessageBox(dlg.m_strEdit);

?注意:

?在前面的第7步中有一个乍看起来有一些费解的过程,这就是我们为什么要将下面的代码从函数OnInitDialog中删除:

?m_pMainWnd = &dlg;

这基于下面的一个事实:

类CWinThread的数据成员m_pMainWnd有一个有用的特征,如果由

该成员所引用的窗口被关闭的话,MFC库将自动的终止CWinThread

对象所代表的线程。这样,如果我们将指向dlg的指针赋予了成员

变量m_pMainWnd,那么,无论我们单击了“确认”还是“取消”,

应用程序的主线程都将被自动终止,之后的代码当然不会得到执

行。而在本示例中,我们希望在对话框被关闭后程序继续运行(即

弹出一个消息重述用户所输入的内容),因此不应该将dlg对象的

指针赋予成员变量m_pMainWnd,从而需要将前面的代码从函数

OnInitDialog中删除。

第二节所有窗口类的基类:CWnd

在MFC中类CWnd是一个很重要的类,它封装了Windows窗口句柄HWND。在Windows 编程中,窗口句柄唯一的标识了一个窗口。然而,尽管类CWnd的对象和窗口句柄之间有着如此紧密的联系,但两者并不是等同的概念。CWnd对象通过类CWnd 的构造函数和析构函数创建和消毁,而Windows窗口是Windows内部的一种数据结构,在类CWnd中,它通过Create成员函数创建,通过其析构函数消毁。除此之外,成员函数DestroyWindow可以消毁Windows窗口,而不需要消毁CWnd对象。

传统的Windows应用程序中,消息是通过一个称作窗口过程(window procedure,通常具有WndProc之类的函数名)的回调函数来处理的。这种方式在MFC中仍然使用,但为CWnd类及其消息映射所隐藏。在类CWnd中,Windows通知消息会被自动的通过消息映射传递到类CWnd中合适的OnMessage成员函数(这里OnMessage是指这些函数具有的以On为前缀的函数名,如OnPaint和前面接触到的OnInitDialog等)进行处理。通常我们都在类CWnd的派生类中重载需要处理的特定消息所对应的OnMessage成员函数。除了直接从CWnd派生新的窗口类以外,我们更倾向于从MFC中定义的其它类,如CFrameWnd、CMIDFrameWnd、CMDIChileWnd、CView和CDialog以及CButton之类的控件类派生新的窗口类。在MFC中定义的这些类本身也是从CWnd派生的。

通常我们使用两个步骤来创建一个窗口:首先,调用类CWnd的构造函数来构造一个CWnd对象,然后调用其成员函数Create来创建窗口并将该窗口与所创建的CWnd对象相关联。

当用户终止该窗口时,消毁与之相关联的CWnd对象,或者调用CWnd对象的成员函数DestroyWindow删除窗口并消毁其数据结构。

大多数以HWND为参数的Win32 API函数都已作为类CWnd的成员函数进行了封装,事实上,很多时候我们通过类Wnd的派生类调用的成员函数并不是由派生类本身所提供的,而是在类CWnd中进行定义的。下面我们分类给出在CWnd类中定义的各类成员函数。完整而详尽的说明每一个成员函数在本书中是不现实的,这里我

们仅给出对每一个成员函数的简短说明,以便读者在编程时能够很快的查找到所需的函数,这时再去查找有关于该函数的详细的说明就不是一件困难的事了。

1. 类CWnd的数据成员(表6.2):

表6. 2 类CWnd的数据成员

2. 构造函数/析构函数(表6.3):

表6. 3 类CWnd的构造函数和析构函数

5. 窗口大小和位置函数(表

6.6):

表6. 6 类CWnd的窗口大小和位置成员函数

6. 窗口访问函数:

表6. 7 类CWnd的窗口访问成员函数

续表6.7

7. 更新和绘制函数(表6.8)

表6. 8 类CWnd的更新和绘制函数

续表6.8

8. 坐标映射函数(表6.9)

表6. 9 类CWnd的坐标映射函数

续表6.9

9. 窗口文本函数(表6.10)

表6. 10 类CWnd的窗口文本函数

10. 滚动函数(表6.11)

表6. 11 类CWnd的滚动成员函数

11. 拖放函数(表6.12)

表6. 12 类CWnd的拖放成员函数

12. 插入符函数(表6.13)

表6. 13 类CWnd的插入符成员函数

13. 对话框项函数(表6.14)“

表6. 14 类CWnd的对话框项函数

续表6.14

14. 数据绑定函数(表6.15):

表6. 15 类CWnd的数据绑定成员函数

15. 菜单函数(表6.16)

表6. 16 类CWnd的菜单成员函数

续表6.16

16. 工具提示函数(表6.17)

表6. 17 类CWnd的工具提示函数

17. 计时器函数(表6.18)

表6. 18 类CWnd的计时器成员函数

18. 提示函数(表6.19)

表6. 19 类CWnd的提示成员函数

19. 窗口消息函数(表6.20)

表6. 20 类CWnd的窗口消息成员函数

续表6.20

20. 剪贴板函数(表6.21)

表6. 21 类CWnd的剪贴板函数

相关主题
相关文档
最新文档