对话框与子窗口控件(写给大忙人看的快速复习掌握)
- 1、对话框的概念
- 2、控件的概念
- 我更喜欢称控件为预定义的窗口类
- 3、我们一步一步写代码熟悉常用的预定义的窗口类
- 3.1 什么叫模板呢?
- 3.2 什么是资源文件
- 4、消息处理函数(有这么几个消息比较重要)
- 4.1 对于对话框里面到底是返回TRUE好呢还是返回FALSE呢?
- 4.2 什么叫ID号呢?
- ID号有什么作用呢?ID号本质是啥呢?
- 5、讲解子窗口的WM_COMMAND消息的原理
- 5.1 原理
- 5.2 关于VC6静态文本的一个BUG
1、对话框的概念
模态对话框就是这个对话框你给它打开了,你要先把它给关闭你才能干其他事,不然这个对话框一直在这挡着你,不让你干其他事;
非模态对话框正好跟模态对话框相反,就是你给它打开了你也可以干其他事,你也可以在上面点来点去的,它不影响你使用其它的。
例如:
非模态对话框就是那种我们在word上按Ctrl+F
弹出的查找对话框,你可以点击该查找对话框后面的页面编辑文字,也可以随时点击该查找对话框搜索内容。
模态对话框就是那种我们在word上按Ctrl+O
弹出的打开对话框,鼠标点这个对话框后面的东西点不动(会发出声音提醒),你非得把它关闭了才会允许你干其他事(编辑文字)。
2、控件的概念
我更喜欢称控件为预定义的窗口类
我不喜欢控件这个词,因为控件这个概念有点抽象,很容易让人摸不到头脑(控件到底是什么东西,它跟窗口有什么关系呢?),我更喜欢叫它预定义的窗口类。
大家还记得么,我们在Windows 程序设计应用开发(上部) 第四章 windowsSDK编程
给大家写的精简的windows窗口程序的时候,里面有一个窗口类,你先定义个WNDCLASS那个类之后,注册一下,然后你在后面调用CreateWindow创建窗口的时候,你根本就没有用WNDCLASS那个类的变量的相关东西,你只是在CreateWindow的第一个参数里面提供了一个类名。
从上面MSDN的介绍中我们可以看到:
CreateWindow函数的第一个参数是个字符串,即类名。
WNDCLASS最后一个成员也是这个类名,当时说了用RegisterClass跟操作系统注册一下这个类名
,注册之后这个类名就被操作系统给保存起来了,然后你通过CreateWindow传入这个类名给第一个参数的时候,操作系统就知道你需要使用的是哪个类了;
这个类有什么作用呢?
我们可以根据这个类找到它的窗口回调函数、以及其他相关的内容(如窗口背景颜色等信息)。
总之,预定义的窗口类(控件)就是已经把WNDCLASS结构体做好了,它也跟操作系统注册过了,你直接用就可以了,同时预定义的窗口类也提供了窗口回调函数,我们只要直接用它就行了。
3、我们一步一步写代码熟悉常用的预定义的窗口类
1、建一个空的Win32 Application项目
我们注意到调用的DialogBox函数的第4个参数也是一个回调函数,跟我们之前的WNDCLASS里面的窗口回调函数是一个意思。
DialogBox:从对话框模板资源创建模式对话框。 在指定的回调函数通过调用 EndDialog 函数终止模式对话框之前,DialogBox 不会返回控件。
我们复制该回调函数的定义到我们的代码中:
此时还差一个东西,DialogBox函数的第二个参数:对话框模板资源。
3.1 什么叫模板呢?
给我们的win32程序插入一个对话框:
这个就是模板,模板有个好处就是,所见即所得。
我们可以把这个模板保存为资源脚本Script1.rc:
然后把头文件resource.h给添加进来,再把资源文件Script1.rc给添加进来。
(注意,这是VC6的情况,如果是新的VS版本,会自动生成resource.h和相应的.rc资源文件)
我们看到Dialog第二个参数,要通过MAKEINTRESOURCE宏进行转换,这是为什么呢?我们看MSDN的解释:
第二个参数[in] lpTemplate,类型: LPCTSTR。
对话框模板。 此参数是指向指定对话框模板名称的以空字符结尾的字符串的指针,或者是指定对话框模板的资源标识符的整数值。 如果参数指定了资源标识符,则其高位字必须为零,其低位字必须包含该标识符。 你可以使用 MAKEINTRESOURCE 宏来创建此值。
也就是说,此时需要将新建的对话框模板的ID号通过MAKEINTRESOURCE宏进行转换。
我们复制该对话框模板的ID号以便进行转换:
编译运行效果如上图,我们发现还有很多问题,显示不正常,还有就是只能通过任务管理器强制关闭该对话框等问题。
3.2 什么是资源文件
我们打开Script1.rc资源脚本文件:
DialogBox根据ID号(IDD_DIALOG1)拿到这个资源文件,进行解析。
通过这个资源文件我们可以看到,这个对话框的坐标、长宽、这个对话框窗口的类型、标题、字体,以及两个按钮(一个OK、一个Cancel),这个DialogBox函数会自动解析这个文本,然后根据这个文本把这个窗口给创建出来。
VC6解析该资源文件为图形方式的效果如上图所示。
4、消息处理函数(有这么几个消息比较重要)
WM_INITIDIALOG(对话框初始化消息)
这个消息就是对话框已经创建出来了,但是还没有显示出来,你可以在对话框里面做一些初始化的动作,比如说设置一些文本的信息,设置一些初始化的选项等等。
WM_COMMAND(命令消息)
命令消息里面又对按钮进行划分(例如IDOK、IDCANCEL)
4.1 对于对话框里面到底是返回TRUE好呢还是返回FALSE呢?
如果这个消息你已经处理了,那你就返回TRUE,操作系统一看这个消息已经处理了就不需要动它了;
如果返回FALSE表示这个消息你并没有处理,那么操作系统就会帮你处理。
4.2 什么叫ID号呢?
这个对话框有一个ID号,这个按钮控件也是个窗口,它也有一个ID号。
ID号有什么作用呢?ID号本质是啥呢?
ID就是窗口唯一的标识,它就是一个整型的数字。
我们打开resource.h文件:
该文件中没有OK和CANCEL两个按钮,因为这两个按钮是在插入Dialog对话框的时候,对话框默认的。
此时你可以将这两个默认按钮删除,自己在拖放上两个按钮,如下图:
保存后resource.h就有这两个新放上去的按钮了:
我们给这两个按钮起有意义的ID号和标题:
修改回调函数,对这两个按钮的点击事件进行处理:
此时点右上角的X是退不了的,只能点退出按钮正确关闭该对话框,那么现在就想通过X这个关闭按钮来关,怎么办呢?
添加WM_CLOSE消息的处理代码即可:
不想让该对话框一直显示在左上角,可以在对话框属性里面设置坐标X Pos和Y Pos即可。
5、讲解子窗口的WM_COMMAND消息的原理
5.1 原理
按钮也是一个窗口,我用鼠标点击它的时候,它的窗口回调函数会被调用,因为按钮是预定义的窗口类,预定义的窗口类里面已经把窗口回调函数全部设置好了,我鼠标在按钮上面点的时候,操作系统肯定会调用按钮的回调函数,它的回调函数很简单,就是给按钮的父窗口(是上图中的标题为Dialog的窗口)发送一个命令消息WM_COMMAND消息。
父窗口Dialog拿到WM_COMMAND消息后,它是怎么区分显示标题和退出这两个按钮发来的消息呢?
就是靠父窗口回调函数中的wParam参数,wParam参数低2个字节就可以区分各个子窗口按钮,wParam参数低2个字节就是子窗口按钮的ID号。
5.2 关于VC6静态文本的一个BUG
我们再添加一个按钮和静态控件(用来显示一些文本):
设置静态控件的字体居中显示:
修改新增按钮的ID和标题:
为了设置静态文本框的内容,怎么获得静态文本框的句柄呢?
我们可以通过GetDlgItem函数传入静态文本框的ID号,就可以获得它的句柄,但是我们也可以一步到位,直接通过SetDlgItemText函数设置该控件的文本内容:
但是此时点击修改内容按钮,静态文本框内容没变化,为什么不显示呢?下个断点,单步调试找原因,也没发现问题所在,怎么回事呢?
我们换一种方式来设置试试:
我们发现还是不行,到底是怎么回事呢?你们以后也可能会遇到这种问题。
再拖一个Static控件放上去,并修改它的ID为ID_STATICWND
:
重新编译运行程序,这个新增的控件IDC_STATICWND上面的文本就正常显示了;我们再换回SetDlgItemText函数设置这个新放置上去的Static控件里的内容也没有问题。
为了查找此问题的原因,我们查看resource.h头文件:
我们发现此ID的值竟然是-1。
我们再拖放上去一个Static控件,再查看resource.h头文件以进行对比分析:
我们发现没有变化,通过这些操作我想表达的是:
什么叫静态控件,就是只显示文本,不参与你们任何的动作,你开始把它的文本内容设定好了,那么你为什么还要修改它呢,你就不要再改它了,它就是这么一个目的。
它不需要被改变,所以我们这里的几个Static控件的ID就都给弄成一样的了,ID都是IDC_TEXTSHOW。
(注意,这里的问题也是VC6的BUG,在新版的VS中已经解决了,不管你复制static控件还是拖进新的static控件,它们的ID会自动增加)
你如果想让该Static控件显示文本的话,很简单,如下面所做:
先把它的ID给删了不要它,然后自己给它起个新的ID:
这个问题是一大巨坑!微软这样做是别有用意的,就是上面所说的目的(它不需要被改变,所以这里的几个Static控件的ID就都给弄成一样的了)。