已安装Visual Studio 2017(或以上版本);
已掌握“MFC开发经验系列文章中介绍的技能”;
已掌握“[MFC控件(3)] 响应按钮控件的热点变化通知[TZZ]”;
启动VS2017软件,创建一个名为“MFCControlDemo4”的MFC对话框程序项目。项目创建完毕后,打开主对话框资源,将其中的控件全选,然后删除;
从工具箱面板中拖动一个“Button”控件到对话框中,修改其大小和位置,然后将其“Caption”属性修改为“获得自定义分割矩形”;
通过类向导为“IDC_BUTTON1”按钮控件添加一个“NM_GETCUSTOMSPLITRECT”通知消息的处理程序;
关闭类向导后,在新添加的“OnGetcustomsplitrectButton1”处理函数中的第一行代码位置添加一个断点,然后调试运行程序;
程序启动后,主对话框正常显示,设置的断点并没有被命中。这表明,IDC_BUTTON1按钮并没有发送“NM_GETCUSTOMSPLITRECT”消息或者处理函数并没有关联到该消息(在这里是不可能的);
打开“Help Viewer”,在索引面板中搜索“NM_GETCUSTOMSPLITRECT”,然后打开该消息的说明文档。通过阅读文档可知,按钮控件会将NM_GETCUSTOMSPLITRECT消息发送给其父窗口,用于获取分割按钮的两个矩形尺寸。此消息的处理函数会收到一个“NMCUSTOMSPLITRECTINFO”指针,用于返回自定义分割矩形信息给按钮控件。如果此函数中返回CDRF_SKIPDEFAULT,则告诉控件使用NMCUSTOMSPLITRECTINFO结构中设置的值,如果返回CDRF_DODEFAULT,则告诉控件使用自身默认的值(即忽略NMCUSTOMSPLITRECTINFO结构)。另外,在文档的“Remarks”中,指出了“此通知是在按钮控件被绘制之前发送”,发送此通知的按钮必须具有BS_SPLITBUTTON或BS_DEFSPLITBUTTON风格,如果返回的矩形信息非法,则其值会被忽略(英文不好的朋友,读MSDN会出现很多歧义,务必以代码行为为准!);
在新选项卡中打开“NMCUSTOMSPLITRECTINFO”的说明文档。 “NMCUSTOMSPLITRECTINFO”结构中的hdr指向包含通信信息的“NMHDR”变量,rcClient用于保存整个按钮客户区区域,rcButton用于保存按钮中不包含下拉箭头的区域,而rcSplit则用于保存下拉箭头的区域。整个结构中保存的信息都是用于绘制按钮的;
回到“NM_GETCUSTOMSPLITRECT”选项卡中,在新选项卡中打开“CDRF_SKIPDEFAULT”的帮助文档。在这份文档中,解释了所有RF常量的用途。如果返回“CDRF_DODEFAULT”,则控件会负责绘制自身,并且不会在绘制过程中发送任何额外的“NM_CUSTOMDRAW”通知。如果返回“CDRF_SKIPDEFAULT”,则控件不会绘制自身,绘制任务交由程序负责;
回到VS2017中,在“OnInitDialog”函数中,为“IDC_BUTTON1”按钮添加“BS_DEFSPLITBUTTON”风格。然后调试运行程序;
程序启动后,一个对话框一闪而过!然后程序终于成功被断下来了。此时,可以点击“继续按钮”继续程序运行。可惜,当对话框即将显示时,这个断点又被捕获了。这是由于分割按钮在每次被绘制之前都会发送“NM_GETCUSTOMSPLITRECT”通知造成的。因此,只好点击工具栏中的“停止按钮”终止程序执行;
回到“MFCControlDemo4Dlg.cpp”文件中,去掉“OnGetcustomsplitrectButton1”函数中设置的断点,然后在该函数中添加打印“NMCUSTOMSPLITRECTINFO”结构中3个矩形的代码。为了避免重复代码,可以在该函数之上定义一个“TRACE_RECT”宏,专门用于输出RECT结构变量的值(逻辑很简单,应该能看懂吧!);
代码编写完毕后,调试运行程序,然后直接将弹出的对话框关闭,返回到VS2017中查看调试输出的内容。在输出面板中,会输出很多条打印信息(按钮每绘制一次,就会打印一堆信息),其中除了按钮客户区外,另外的两个区域的值都是非法的;
重新返回“MFCControlDemo4Dlg.cpp”文件的“OnGetcustomsplitrectButton1”函数中,修改原有的函数代码,手动为设置返回的rcButton和rcSplit的值,并且返回“CDRF_SKIPDEFAULT”给控件,告诉它采用代码中设置的分割按钮矩形绘制按钮;
调试运行程序,在弹出的主对话框中,你会发现“IDC_BUTTON1”按钮中的下拉箭头宽度已经占据了整个按钮客户区的一半,表示参数设置成功。至此,响应“NM_GETCUSTOMSPLITRECT”通知的代码就介绍完毕了,Enjoy!