6.3 技术总结
在上一章和本章中,读者已经接触和使用了各种控件。这些控件虽然五花八门,但它们却具有一些共同的特点。本节的目的就是讨论这些共同点,以使读者能在概念上更好地理解控件。
6.3.1 所有的控件都是窗口
确切地说,所有的控件都是子窗口。控件窗口都具有WS_CHILD风格,它们总是依附于某一个父窗口。所有MFC的控件类都是基本窗口类CWnd的直接或间接派生类,这意味着可以调用CWnd类的某些成员函数来查询和设置控件。常用于控件的CWnd成员函数在表6.34列出,这些函数对所有的控件均适用。
表6.34 常用于控件的CWnd成员函数
函数名
用途
ShowWindow
调用ShowWindow(SW_SHOW)显示窗口,调用ShowWindow(SW_HIDE)则隐藏窗口.
EnableWindow
调用EnableWindow(TRUE)允许窗口,调用EnableWindow(FALSE)则禁止窗口.一个禁止的窗口呈灰色显示且不能接受用户输入.
DestroyWindow
删除窗口.
MoveWindow
改变窗口的位置和尺寸.
SetFocus
使窗口具有输入焦点.
例如,如果想把一个编辑框控件隐藏起来,可以用下面这行代码完成.
m_MyEdit.ShowWindow(SW_HIDE);
6.3.2 控件的创建方法
控件的创建有自动和手工两种常用方法.
控件的自动创建是通过向对话框模板中添加控件实现的.到目前为止,读者所使用的控件都是用这种方法创建的.当调用对话框类的DoModal和Create显示对话框时,框架会根据对话框模板资源提供的控件信息自动地创建控件.这种方法的优点是方便直观,用户可以在对话框模板编辑器的控件面板中选择控件,可以在对话框模板中调整控件的位置和大小,还可以通过属性对话框设置控件的风格.
手工创建控件是一种比较专业的方法,包括下面两步:
构建一个控件对象.通常的做法是把控件对象嵌入到父窗口(如对话框)对象中,即以成员变量的形式定义一个控件对象.这样,在构建父窗口对象时,控件对象会被自动构建.程序也可以用new操作符创建控件对象,但要注意MFC的控件对象不具有自动清除的功能,因此需要在关闭父窗口时用delete操作符删除控件对象(参见5.4.2).
调用控件对象的Create成员函数创建控件.一般来说,如果要在对话框中创建控件,那么应该在OnInitDialog函数中调用Create,如果要在非对话框窗口中创建控件,则应该在OnCreate函数中调用Create.
清单6.3是一个手工创建控件的实例.
清单6.3 控件的手工创建
#define ID_EXTRA_EDIT 100
class CMyDialog : public CDialog
{
protected:
CEdit m_edit; // Embedded edit object
public:
virtual BOOL OnInitDialog();
};
. . . . . .
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
CRect rect(85, 110, 180, 210);
m_edit.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP |
ES_AUTOHSCROLL | WS_BORDER, rect, this, ID_EXTRA_EDIT);
m_edit.SetFocus();
return FALSE;
}
不难看出,控件的手工创建是在程序中通过控件对象完成的,与对话框模板无关.在Create函数中,需要提供控件的风格,控件的尺寸和位置,控件的ID等信息.手工创建实际上是一种动态创建过程,程序可以在任何时侯根据需要来创建,不一定非要在OnInitDialog中进行.
控件并不是对话框所专有的,某些非对话框窗口也可以拥有控件.如果要在象工具条或状态栏这样的非对话框窗口中创建控件,就必需用手工创建方法.自动创建由于要依赖对话框模板,因此只适用于对话框.
6.3.3 访问控件的方法
控件是一种交互的工具,应用程序需要通过某种方法来访问控件以对其进行查询和设置.访问控件有四种方法:
利用对话框的数据交换功能访问控件.这种方法适用于自动创建的控件.先用ClassWizard为对话框类加入与控件对应的数据成员变量,然后在适当的时侯调用UpdateData,就可以实现对话框和控件的数据交换.这种方法只能交换数据,不能对控件进行全面的查询和设置,而且该方法不是针对某个控件,而是针对所有参与数据交换的控件.另外,对于新型的Win32控件,不能用ClassWizard创建数据成员变量.因此,该方法有较大的局限性.
通过控件对象来访问控件.控件对象对控件进行了封装,它拥有功能齐全的成员函数,用来查询和设置控件的各种属性.通过控件对象来访问控件无疑是最能发挥控件功能的一种方法,但这要求程序必需创建控件对象并使该对象与某一控件相连.对于自动创建的控件,可利用ClassWizard方便地创建与控件对应的控件对象.对于手工创建的控件,因为控件本身就是通过控件对象创建的,所以不存在这一问题.
利用CWnd类的一些用于管理控件的成员函数来访问控件.这些函数已在表5.5列出.只要向这些函数提供控件的ID,就可以对该控件进行访问.使用这些函数的好处是无需创建控件对象,就可以对控件的某些常用属性进行查询和设置.该方法对自动和手工创建的控件均适用.
用CWnd::GetDlgItem访问控件.该函数根据参数说明的控件ID,返回指定控件的一个CWnd型指针,程序可以把该指针强制转换成相应的控件类指针,然后通过该指针来访问控件.该方法对自动和手工创建的控件均适用.在上一章中就曾经使用过这种方法,读者可参见5.3.6.其实该方法与通过控件对象来访问控件的方法在本质上是一样的,在表5.5中亦包括GetDlgItem函数,但为了强调其重要性,这里把它单独列为一种方法.
6.3.4 控件及控件对象的删除
当关闭父窗口时,控件会被自动删除,因此在一般情况下不必操心删除问题.如果由于某种需要想手工删除控件,可以调用CWnd::DestroyWindow来完成.
对于控件对象的删除,有两种情况.若控件对象是以成员变量的形式创建的,那么该对象将会随着父窗口对象的删除而被删除,因此在程序中无需操心.若控件对象是用new操作符在堆中创建的,则必需在关闭父窗口时用delete操作符删除对象,这是因为所有MFC的控件类都是非自动清除的(参见5.4.2).
6.3.5 控件通知消息
传统控件和Win32 控件采用了不同的通知消息机制,请参见6.1.1和6.2.1。