📄 08.1 “逃跑”按钮的实现.txt
字号:
第8章对话框(二)
图 8.2 AppWizard为基于对话框的应用程序创建的类
. CAboutDlg
派生于CDialog类,这个类与SDI应用程序中相应的类: CAboutDlg作用相同,也是用来显示一个关于对话框。
. CTestApp
这是MFC应用程序中必不可少的一个类,派生于CWinApp类,它的对象代表了应用程序本身。
. CTestDlg
也是从CDialog类派生的,这是基于对话框的MFC应用程序的主界面。
可以看到,基于对话框的应用程序中没有从 CView类派生出来的视类,也没有从 CFrameWnd类派生出来的框架类,以及从CDocument类派生的文档类,它只有从CDialog派生出来的一个对话框类: CTestDlg,这类应用程序的窗口就是一个对话框界面。如果运行此时的Test程序,其结果如图 8.3所示,可以看到其界面就是一个基于对话框的应用程序。
图 8.3 AppWizard创建的基于对话框的应用程序的初始界面
平时,我们经常会从网上下载一些小程序,当安装完成之后,它会在桌面上生成一个小人或小动物,当我们用鼠标去单击这个小人/小动物时,它会在屏幕上到处乱跑,我们始终也无法点中这个小人。下面,我们就要实现这种功能的一种简化版本。具体地说,就是在 Test程序的对话框主界面上增加一个按钮,当用鼠标单击这个按钮时,该按钮会
自动移动到另一个位置,就像一个"逃跑"的按钮。为了实现所需功能,首先删除MFC AppWizard自动创建的对话框资源:IDD-TEST-DIALOG上的所有控件,然后添加一个按钮控件,利用其属性对话框,将其Caption修改
为:"你能抓住我吗?"。接着我们想利用按钮的属性对话框改变按钮文本的字体,但发现
该属性对话框上并没有相应的选项,那么对话框资源的属性对话框中是否会有这样的选项
呢?打开IDD-TEST-DIALOG对话框的属性对话框,如图8.4所示,可以看到在其General选项卡上,有一个【Font】按钮,当单击此按钮时,就会弹出如图 8.5所示的对话框,利用此界面就可以设置对话框窗体及其上所有子控件的字体。
图 8.4对话框属性对话框
根据前面的知识,我们知道为了实现这种"逃跑"按钮,可以通过捕获鼠标移动的消息,井在此消息响应函数中让这个按钮的位置发生移动来实现。本例将为读者介绍一种巧妙的实现方法,在 IDD-TEST-DIALOG对话框资源窗口中,复制刚才添加的那个按钮,并在其下方进行粘贴操作,这样 IDD-TEST-DIALOG对话框资源中就有了两个外观相同的按钮,如图 8.6所示。在程序实现时,首先让其中的一个按钮隐藏,另一个按钮显示:当随后把鼠标移动到显示的按钮上时,将该按钮隐藏,把另一个显示出来。因为这两个按钮的外观是完全一样的,因此这样的效果给用户的感觉好像按钮是自动跑到新位置处的。
图 8.5 设置对话框字体的对话框 图 8.6 创建两个外观相同的按钮
为了实现上述所述功能, Test程序首先就要捕获鼠标移动消息,那么由谁来捕获这个消息比较合适呢?如果让对话框窗口 CCTestDlg类)来捕获,一旦鼠标在对话框窗口中移动,程序就会让按钮上下移动,这当然不是我们想实现的功能。我们想要的功能是当鼠标移动到按钮上时,按钮才上下移动。也就是说,鼠标移动的消息应该由按钮窗口来捕获,为此,在 MFC应用程序中,可以创建一个从CButton类派生的新类,然后将按钮控件与这种新类型的成员变量相关联,从而就把按钮控件与一个自定义的按钮窗口类关联起来。
下面,我们通过ClassWizard为 Test应用程序增加一个从CButton派生的新类,方法是打开ClassWizard,然后单击【AddClass..】按钮,从其下拉菜单中选择【New..】命令
(如图 8.7所示),即可弹出增加新类的对话框。在此对话框中,设置新类的名称 (Name )为: CNewButton,基类 ( Base class )为 : CButton,如图 8.8所示。然后单击【OK】按钮,从而完成新类的添加。
图 8 .7添加新类的方法
图 8 .8添加的新类
接下来,我们要把对话框中的两个按钮分别关联一个成员变量,这同样可以利用 ClassWizard来实现这一功能,方法是在按钮控件上单击鼠标右键,从弹出的快捷菜单中选择【 ClassWizard..】菜单命令,然后选择弹出的 ClassWizard对话框上的 Member Variables 选项卡,选中 IDC_BUTTON1 ,单击【 Add Variable...】按钮,在弹出的添加成员变量对话框中设置变量名称为: m_btnl,井且从变量类型的下拉列表框中选择 CNewButton,即将成员变量的类型设置为上面新添加的类,如图 8.9所示。
图 8 . 9为第一个按钮添加的成员变量
之后,单击【 OK】按钮,这时系统会弹出一个提示对话框(如图 8.10所示),让用户确保 TestDlg.h文件中包含了必要的头文件。因为我们现在为对话框上的一个子控件关联了一个成员变量,而这个成员变量的类型是 CNewButton类,这个类是刚刚创建的新类,如果在 CTestDlg类中想要识别这种类型的话,就必须在 CTes tDlg类中包含这个新类的头文件。
图 8 .10系统提供的提示对话框
单击提示对话框上的【确定】按钮,完成第一个按钮的关联变量的添加。接下来,运用同样的方法为第二个按钮添加一个同样类型的成员变量 : m btn2。
根据系统先前的提示,我们需要在 CTestDlg类的头文件中包含 CNewButton类的头文件,添加后的 CTestDlg类的头文件部分代码如例 8 -1所示,并且在 CTestDlg类的头文件中,我们也可以看到定义了两个 CNewButton类型的成员变量。
例 8-1
/ ////////////// ////////////////////////////////////////////////////////
// CTestDlg dialog
#include "NewButton .h "
class CTestDlg : public CDialog
{
1/ Construction
public :
CTestDlg(CWnd* pParent = NULL); /1 standard constructor
11 Dialog Data
/1 {{AFX_DATA(CTes tDlg)
enum { IDD =IDD_TEST_DIALOG }; .
CNewButton ID_btn2;
CNewButton ID_btnl;
// }}AFX_DATA
// Classwizard generated virtual function overrides
11{{AFX_VIRTUAL(CTestDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX) ; /1 DDX/DDV support
II}}AFX_V工RTUAL
下面就让CNewButton类捕获鼠标移动消息,方法是在VC++开发界面的ClassView选项卡上,在 CNewButton上单击鼠标右键,从弹出的快捷菜单上选择【Add Windows Message Handler.. .】菜单命令,这将显示添加消息响应函数的对话框。在此对话框左边的消息列表中,找到WM_MOUSEMOVE消息并选中 (如图8.11所示)。然后单击【Add and Edit】按钮,即可完成鼠标移动消息响应函数的添加,井定位到这个函数 (OnMouseMove)的定义处。
图 8.11添加消息响应函数的对话框
接下来,我们就在这个OnMouseMove响应函数内部,完成一个按钮显示和一个按钮隐藏的功能。关于按钮的隐藏功能比较容易实现。因为当鼠标移动到该按钮上时,就会由这个按钮的鼠标移动消息的响应函数 OnMouseMove来响应,如果在此函数中,以参数 SW HIDE去调用这个按钮的ShowWindow函数,即可将其隐藏。可是这时为了让另一个按钮显示出来,必须要知道另一个按钮所关联的那个对象的内存地址,然后才能调用该对象的ShowWindow函数,将其显示出来。为了在一个按钮对象中获取另一个按钮控件对象的地址,最简单的方式就是在CNewButton类中定义一个成员变量,让其指向另一个按钮对象的地址。因此,我们为 CNewButton类再添加一个 CNewButton*类型的成员变量: m_pBtn。这样,当用CNewButton类去实例化CTestDlg类的成员变量m btnl和m btn2
时,这两个对象内部就都有了一个m_btn成员变量,我们可以让这两个对象内部的m_Btn
变量分别保存对方的首地址,相当于这两个对象互相交换了自己的首地址。这个过程如图
8.12所示。于是,当 m btnl按钮隐藏时,就可以利用它的成员变量 m_pBtn去调用 ShowWinodw函数,将m_btn2按钮显示出来:同样地,当m btn2按钮隐藏时,可以利用它的成员变量m_pBtn去调用ShowWindow函数,将m bntl按钮显示出来。
图 8.12两个按钮对象互相保存对方的首地址
下面我们继续完善程序代码,在CTestDlg类中把m btnl和mbtn2这两个对象的首地址交换一下,这一工作可以放在OnInitDialog函数中实现,因为根据前面的知识,我们知道这个函数就是WM INITDIALOG消息的响应函数,该消息是在对话框要显示之前发送的。读者可以在CTestDlg类的OnInitDialog函数的最后,但要在return i吾句之前添加以下代码:
m_btn1 .m_pBtn = &m_btn2;
m_btn2.m_pBtn = &m_btn1;
然后在 CNewButton类的OnMouseMove函数中,先让对象自己隐藏起来,然后调用成员m_pBtn的ShowWindow函数将对方显示出来,具体实现代码如例8-2。
li 8-2
void CNewButton : :OnMouseMove(UINT nFlags , CPoint point)
.
// TODO: Add your message handler code here and/or call default
ShowWindow(SW_HIDE) ;
m_pBtn->ShowWindow(SW_SHOW) ;
CButton : :OnMouseMove(口Flags, po工nt) ;
我们可以分析上述例8-2所示的这段代码,当鼠标移动到第一个按钮对象 Cm_btnl ) 上时,程序就会调用该对象的OnMouseMove函数,在这个函数中,首先调用ShowWindow函数将自身隐藏。因为第一个按钮对象的成员 m_pBtn保存的是第二个按钮对象mJm2
的地址,所以接下来的m_pBtn->ShowWindow (SW_SHOW)的调用就将第二个按钮显示了出来。当随后鼠标移动到第二个按钮对象上时,实现原理相同,只是对m bnt2对象来说,它的m_pBtn成员变量保存的是m btnl按钮的地址。
Build并运行Test程序,可以看到,程序初始显示时有两个按钮(如图8.13所示),当鼠标移动到其中的一个按钮上时,它就消失了,另一个按钮处于显示状态(如图 8.14所示);当把鼠标移动到这个显示的按钮上时,它又消失了,另一个按钮又显示出来(如图8.15所示〕 ……给我们的感觉好像是这个按钮在上下移动似的,我们始终也无法抓住这个按钮。可见,程序实现了所需的"逃跑"按钮的效果。
但是,这个程序还有一个缺陷,就是初始显示时,两个按钮都是显示状态,这很容易让用户看出程序的实现方式。因此,在初始时应该隐藏一个按钮。为了解决这个问题,我们可以利用按钮属性对话框把第一个按钮的 Visible属性去掉。再次运行Test程序,将会看到这时只有一个按钮处于显示状态了,然后把鼠标移到这个按钮上,这个按钮就隐藏了,井显示出另一个按钮:再把鼠标移到这个显示的按钮上时,它又消失了,另一个又显示出来……这就是本例对"逃跑"按钮的一种巧妙的实现方式。当然,你也可以利用 SetWindowPos函数来设置按钮在屏幕上移动的新位置,读者可以自行尝试这种方法。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -