第四章 对话框和控件
对于Win32 GUI的程序设计来说,其实大部分的情况下我们都不需要自己进行窗口类的设计,而是可以使用Win32中与用户交互的标准方式——对话框(Dialog Box)。我们可以在VC IDE的资源设计器中设计对话框资源,并在其上放置各种控件资源——的确是非常方便。在本章里,李马将要向诸位介绍如何利用ATL来操作对话框,以及如何操作对话框上的各种控件。
题外话先
ATL,是的,正是由于我所讲的是“ATL的GUI程序设计”,所以我才可能将内容直接经由CWindowImpl过渡到CDialogImpl——而不是过渡到你先前所熟悉的CFrameWnd和Doc/View体系。况且,即使这之后我深入到了CDialogImpl之中,我也不会讲到你所熟悉的DDX/DDV机制。再三考虑之下,我还是决定把这些东西在CDialogImpl前一并当作题外话说出来,先。
再来回顾一下ATL的性质。它是一个被设计用来开发COM组件的Framework,所以对GUI部分的支持——套用一句2006年的流行语来说:那是相~~当~~(加重且延长声音地)少。于是,它没有“框架窗口”这个概念,更不会有Doc/View体系。其实我对MFC的这一设计特点感觉不错,毕竟它可以通过一个简单的CFrameWnd类来实现一个标准的SDI/MDI框架,而且其中带有工具栏、状态栏和一个用来容纳视图的标准的工作区域。我们可以通过控制框架窗口中的View及其相关的Doc类型来完成特定文档类型的读写与显示。——但是,很不幸,这一切都只属于伟大的MFC;在ATL中,我们什么都没有。
另外,在对话框的技术领域中,使用ATL的我们也不会享有数据交换与验证(DDX/DDV)的支持。这一所谓的缺憾我并不想多加评价,一是因为我并不了解MFC中DDX/DDV的内部机制,二是因为我直觉上认为这是影响MFC效率的罪魁之一。在MFC中,我们可以通过向导的支持轻易地为表单的输入域加入输入校验与限制,而且表现在源代码上的仅仅是几个宏而已——我自认天下没有免费的午餐,这几个简单的宏既然能为我们包办一切,那我们势必会相应地失去些东西,要不然忒便宜了也就。
题外话的最后不免落入俗套,我将会向诸位介绍解决以上缺憾的方法。——也许你猜到了,就是从WTL中寻找解决方案。WTL是对ATL的扩展,所以它的很多代码可以直接拿过来用(当然可能需要一些小小的修改)。而且,不知道WTL的设计者是不是为了拉拢MFC的开发人员,总之它里面添加了很多与MFC相似的元素,例如以上所说的框架窗口和DDX/DDV。
CDialogImpl
与ATL窗口类CWindowImpl相对应,ATL的对话框类名为CDialogImpl。它的定义如下:
template<classT,classTBase=CWindow> classATL_NO_VTABLECDialogImpl:publicCDialogImplBaseT<TBase> { //... }; |
你可以从上面的代码看到,CDialogImpl与CWindowImpl类似,也经历了一系列的继承链。不过,它较之CWindowImpl的模板参数要简单得多——毕竟是标准对话框,有些东西是不用操心的。
CDialogImpl的使用方法大致如下:
classCYourDlg:publicCDialogImpl<CYourDlg> { public: enum{IDD=IDD_YOUR_DLG}; public: BEGIN_MSG_MAP(CYourDlg) //消息映射 END_MSG_MAP() public: //消息响应函数 /////////////////// //其余的部分... }; |
和CWindowImpl不一样,CDialogImpl不需要使用DECLARE_WND_CLASS来定义窗口类。在原来DECLARE_WND_CLASS的位置,一个枚举代替了原来窗口类定义的部分。这里的枚举列表必须有一个被命名为IDD,并且它的值要被设置为相应的对话框资源ID。呃……写到这里,我仿佛已经感觉到了你的不快,但CDialogImpl的实现即是如此(以CDialogImpl<T>::DoModal为例):
//fromCDialogImpl<T>::DoModal return::DialogBoxParam(_Module.GetResourceInstance(),MAKEINTRESOURCE(T::IDD), hWndParent,(DLGPROC)T::StartDialogProc,dwInitParam); |
当然,如果你不喜欢这么做的话,也可以自己从CDialogImplBaseT派生出属于你的对话框类。
再回到CDialogImpl的话题上来。这个类主要有以下几个常用的成员函数:
成员函数 |
说明 |
DoModal |
显示一个模态对话框 |
EndDialog |
销毁一个模态对话框 |
Create |
创建一个非模态对话框 |
DestroyWindow |
销毁一个非模态对话框 |
这样看来是不是和MFC十分相似?事实上,如果你已经定义好了一个对话框类,那么它的使用和MFC的对话框类的确没什么两样:
CYourDlgdlg; dlg.DoModal(); |
控件的使用
从与用户交互的角度来看,控件是对话框上必不可少的元素。在Win32 GUI程序设计中,对控件的操作大可归为两个方面:一是对控件进行操作,二是响应控件的事件。排除子类化的事件响应(后面我会专门介绍如何在ATL中进行控件的子类化),那么这两方面的具体实现就是:
- 使用窗口操作的API函数或发送消息来操作控件。
- 处理WM_COMMAND或WM_NOTIFY来响应控件的事件。
根据顺序,李马来为大家介绍一下如何对控件进行操作先。这通常可以经由CWindow及其派生类实现,以下代码示范了如何禁用一个控件:
CWindowctrl=GetDlgItem(IDC_CONTROL); ctrl.EnableWindow(FALSE); |
如果你要操作的控件需要用到特定的特性(也就是通过发送消息来实现的特有行为),当然你可以通过使用CWindow::SendMessage来实现,不过我并不推荐你使用这种方法,因为SendMessage是不会对消息参数进行类型检查的。而且,考虑到代码的可复用性,你可以对CWindow进行派生以达到目的。例如,对于列表控件的封装可以是类似下面这个样子:
classCListBox:publicCWindow { public: intAddString(LPCTSTRlpszString) { return::SendMessage(m_hWnd,LB_ADDSTRING,0,(LPARAM)lpszString); } }; |
然后,这样进行调用:
CListBoxlist; list.Attach(GetDlgItem(IDC_LIST)); list.AddString(_T("Thisisatestline")); |
可能你会有所疑问:为什么CWindow的例子直接使用了“=”来进行赋值,而CListBox则要使用Attach来初始化。当然,其实这两者并没有实质上的区别,只不过是CWindow重载了operator=操作符,而CListBox没有这样做罢了(严格说来,派生自CWindow的CListBox当然继承了CWindow的operator=,但是它并不能用于CListBox对象,如果强行使用则会得到一个“error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'struct HWND__ *' (or there is no acceptable conversion)”的错误)。如果你也希望CListBox支持operator=的初始化方式,可以这样来对CListBox进行封装:
classCListBox:publicCWindow { public: CListBox&operator=(HWNDhWnd) { m_hWnd=hWnd; return*this; } public: intAddString(LPCTSTRlpszString) { return::SendMessage(m_hWnd,LB_ADDSTRING,0,(LPARAM)lpszString); } }; |
下面来介绍对控件事件的处理。通常控件在某些事件发生时会以发送WM_COMMAND(普通控件)或WM_NOTIFY(公共控件)消息的方式通知其父窗口,然后我们在其父窗口的窗口过程中处理这些消息即可。WM_COMMAND和WM_NOTIFY的参数意义如下:
|
WM_COMMAND |
WM_NOTIFY |
wParam |
HIWORD(wParam)为通知消息代码,LOWORD(wParam)为控件ID |
发生通知消息的控件ID,不过仍建议使用lParam参数中的ID |
lParam |
发生通知消息的控件句柄 |
一个指向NMHDR结构的指针,这个结构中包含了通知消息的各种信息 |
在ATL中,可以使用如下的宏来进行各种消息的分流(在此将Windows消息分流的宏也一并加上):
消息分流宏 |
说明 |
MESSAGE_HANDLER |
用于将某个特定消息分流至一个消息处理函数。 |
MESSAGE_RANGE_HANDLER |
用于将某个范围内的消息一并分流至同一个消息处理函数。 |
COMMAND_HANDLER |
用于将来自特定ID、特定通知码的WM_COMMAND消息分流至一个消息处理函数。 |
COMMAND_ID_HANDLER |
用于将来自特定ID的WM_COMMAND消息分流至一个消息处理函数。 |
COMMAND_CODE_HANDLER |
用于将来自特定通知码的WM_COMMAND消息分流至一个消息处理函数。 |
COMMAND_RANGE_HANDLER |
用于将来自某个ID范围内的WM_COMMAND消息分流至一个消息处理函数。 |
NOTIFY_HANDLER |
用于将来自特定ID、特定通知码的WM_NOTIFY消息分流至一个消息处理函数。 |
NOTIFY_ID_HANDLER |
用于将来自特定ID的WM_NOTIFY消息分流至一个消息处理函数。 |
NOTIFY_CODE_HANDLER |
用于将来自特定通知码的WM_NOTIFY消息分流至一个消息处理函数。 |
NOTIFY_RANGE_HANDLER |
用于将来自某个ID范围内的WM_NOTIFY消息分流至一个消息处理函数。 |
另外,处理Windows消息、WM_COMMAND消息、WM_NOTIFY消息的消息处理函数应该分别满足如下规格要求:
//atlwin.h //Handlerprototypes: //LRESULTMessageHandler(UINTuMsg,WPARAMwParam,LPARAMlParam,BOOL&bHandled); //LRESULTCommandHandler(WORDwNotifyCode,WORDwID,HWNDhWndCtl,BOOL&bHandled); //LRESULTNotifyHandler(intidCtrl,LPNMHDRpnmh,BOOL&bHandled); |
李马牌通讯录管理系统
别误会,这并不是什么正儿八经的所谓“信息管理系统”,而只是我为本章写下的一个简单示例而已。这里面并不涉及数据的存储,而只是为演示本章的内容而实现了必要的流程而已。在此李马并不打算对这个程序的代码进行过多解说,仅仅点出几点需要特殊说明的。
- 由于程序中使用了公共控件ListView,所以在WinMain的开头需要对公共控件库进行初始化:
//初始化公共控件先 INITCOMMONCONTROLSEXinit; init.dwSize=sizeof(init); init.dwICC=ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&init); |
在此我有必要指出,对公共控件库的初始化应该尽量使用InitCommonControlsEx,即使InitCommonControls貌似更加方便一些。我曾经做过测试,一个使用了DateTime控件并由InitCommonControls初始化的应用程序在WinXP sp2 + VC 6.0编译完成后,在Win2K下是不能运行的。
- CMainDlg::OnRadioSex是为了演示COMMAND_RANGE_HANDLER而写的一个消息处理函数,其实针对这个示例并不用编写之——因为Windows系统会自动对Radio按钮进行检选状态的处理;但如若考虑到多组Radio按钮存在的情况,CMainDlg::OnRadioSex这样的处理函数便会凸显出它的用处。
- LListView::GetSelectionMark并不能用来准确判断ListView的选中项,尤其是在选中项被删除之后。
分享到:
相关推荐
MFC开发人员的WTL编程简介。
ATL 测试程序 ,简单方便,比较实用。
PDF ATL程序设计 500页 比较清楚 可以下载学习ATL
atl开发指南 atl开发指南 atl开发指南 atl开发指南 atl开发指南
这个程序是基于VC的 ATL的简单程序,程序生成一个DLL的activex空件, 只包含一个函数 add 可以用下面代码实现网页调用(当然要先注册控件了) <TITLE>ATL 3.0 test page for object MyFunAdd ...
ATL服务编程入门参考 启动 停止 安装 卸载
本书主要介绍了ATL技术的原理、内部实现和应用技巧,由当今4 位顶尖的 Windows技术专家联合撰写。全书内容丰富,深入浅出,主要涵盖了ATL内部架构和实现方法、运用向导简化ATL开发、C++/COM/ATL中字符串的使用技巧、...
ATL编写的Windows服务小程序 虽然只是个入门级程序 但是掌握入门知识的感觉 仍然是老帅了。。。
ATL技术内幕ATL技术内幕ATL技术ATL技术内幕内幕
使用ATL设计组件,写了例子和测试例子,简单明了。
第一部分 - ATL 中的 GUI 类 • 下载示例工程 - 45.5 KB 本章内容 • README.TXT • 本系列介绍 • 第一部分介绍 • ATL 背景知识 o ATL 和 WTL 的历史 o ATL 风格的模板 • ATL 窗口类 • 定义窗口实现 o 填充消息...
ATL开发指南。我自己放在这里备份用。 本书是介绍使用ATL进行软件开发的参考用书。全书分为十三章:第一章...本书的主要对象是程序设计或开发人员,同时也可以作为大专院校计算机专业师生和计算机爱好者的参考资料。
collection.zip:包含VC Atl开发的集合的源代码(组件程序和测试程序) enum.zip:包含VC Atl开发的枚举器的源代码(组件程序和测试程序) event.zip:包含VC Atl开发的事件的源代码(组件程序和测试程序) win....
在ATL服务器DLL嵌入MFC GUI接口
刚开始学习ATL,做了一个简单的小程序练习练习,还有很多不详尽之处
VS2003下编译通过,包含两个ATL的例子,1)创建一个简单ATL对象,目的弹出一个Messagebox输出一句话,附加测试程序。程序中要注意COM的初始化。 2)创建一个ATL控件,嵌入到网页中,实现功能为,点击控件中三角形...
适合人群: 对森林、地形、冰川等地物高度研究的人群 如何将星载lidar ICESAT-2的atl8和atl3结合,综合利用地理定位信息与地面高度信息
ATL开发指南.rar 完整的 ATL开发指南 提供下载 学习com atl编程非常好的资料
Snap-In Interface Technology + Embedded MFC GUI into ATL Server DLL在ATL服务器DLL嵌入MFC GUI接口
对应用惯了MFC方式建立对话框的程序员,使用ATL类库建立对话框及各种程序,算是一种新鲜玩意。ATL创建的包含界面EXE程序,体积超小,在对程序体积要求严格网络软件中常使用,在国内使用较少。