📄 03.3.2 在窗口中显示按钮(2).txt
字号:
根据运行结果,我们可以看到该按钮显示在工具栏上了,这是因为按钮当前的父窗口是CMainFrame类窗口,即主框架窗口。该窗口中,标题栏和菜单都位于非客户区,而工具栏位于它的客户区(关于窗口的客户区和非客户区的内容将在下一章讲解)。我们程序中的按钮是在主框架窗口的客户区出现的,并且其位置由CRect(0,0,100,100)参数指定,说明其左上角就是其父窗口客户区的(0,0)点,因此,该按钮就在程序的菜单下、工具栏上显示出来了。
读者可以设想一下,如果我们改在CTestView类中创建这个按钮,会是什么样的结果呢?首先,我们把CMainFrame中创建按钮的代码(即上述例3-25所示代码中第1行和第2行代码)注释起来,然后为CTestView类定义一个CButton类型的成员变量m_btn。但是接下来,我们发现CTestView类中没有OnCreate函数。我们知道,Windows下的程序都是基于消息的,无论MFC程序,还是SDK程序都是这样的。既然窗口在创建时都会产生一个WM_CREATE消息,那么就可以让CTestView响应这个消息,也就是为这个类添加WM_CREATE消息的处理函数。
在VC++中,为一个类添加某个消息的处理函数的方法是:在ClassView标签页上,在该类名上单击右键,从弹出的快捷菜单上选择【Add Windows Message Handler…】菜单命令,这时将弹出如图3.27所示的添加消息处理函数的窗口。
图3.27 添加消息处理函数的窗口
在该窗口左边的Windows消息列表中找到并选中WM_CREATE消息,然后单击Add Handler按钮,接着再单击Edit Existing按钮,或者在选中需要处理的消息之后,直接单击Add and Edit按钮。这时,就为CTestView类添加了WM_CREATE消息的处理函数OnCreate,并且光标将定位于该函数的定义处。我们就在该函数的尾部添加显示按钮的代码,与CMainFrame中的代码相同,可以直接复制过来,结果如例3-26所示。
例3-26
int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
m_btn.Create("按钮",WS_CHILD | BS_DEFPUSHBUTTON,CRect(0,0,100,100), this,123);
m_btn.ShowWindow(SW_SHOWNORMAL);
return 0;
}
编译并运行Test程序,结果如图3.28所示。
图3.28 在视窗口中显示按钮
我们可以看到按钮显示出来了,但位置发生了变化。因为这时给按钮的Create函数传递的this指针指向的是CTestView类的对象,因此,这时按钮的父窗口就是视类窗口,所以按钮在视窗口的客户区中显示。如果这时仍想让按钮的父窗口为CMainFrame类窗口,即视类窗口的父窗口,可以调用GetParent函数来获得视类的父窗口对象的指针,并将该指针传递给按钮的Create函数。这时的CTestView类OnCreate函数定义代码如例3-27所示。
例3-27
int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
m_btn.Create("按钮",WS_CHILD | BS_DEFPUSHBUTTON, CRect(0,0,100,100), GetParent(), 123);
m_btn.ShowWindow(SW_SHOWNORMAL);
return 0;
}
运行Test程序,读者会发现按钮的位置与在CMainFrame中创建按钮的位置一样,可见按钮的位置与其父窗口有关,而不是与创建它的代码所在的类有关。
另外,如果想在创建按钮之后立即显示,可以将其窗口风格指定为WS_VISIBLE,这时,就不需要再调用ShowWindow函数了。即此时按钮的创建和显示只需要下面这一条代码即可:
m_btn.Create("按钮",WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, CRect(0,0, 100,100), GetParent(),123);
小技巧:Windows中很多函数名都是一些有意义的单词的组合,并且每个单词的首字母大写。例如,如果想要得到某个类的父窗口,我们可以猜想这个函数名应该是Get再加上ParentWindow这样的。打开MSDN的索引标签页,键入GetParentWindow,发现没有这个函数,但有一个GetParent函数。打开这个函数,发现就是我们所要的函数。在编程时,通过这种方法,可以快速找到所需要的函数。
本例中,我们选择的是BS_DEFPUSHBUTTON按钮风格类型,读者可以试着使用其他类型的风格,例如BS_AUTORADIOBUTTON、BS_CHECKBOX等,看看结果如何。
通过这个CButton对象的创建,希望读者能更好地理解C++窗口类对象和窗口之间的关系。当我们将按钮窗口销毁,它所对应的m_btn这个C++对象并没有销毁,因为它是CTestView类的一个成员变量,它的生命周期与CTestView对象是一致的。只要CTestView对象没有销毁,该按钮对象就一直存在,在程序中仍可以访问这个对象。
另外,我们发现在调用CButton的ShowWindow函数时,也没有传递一个窗口句柄,因为CButton类是CWnd类的子类,因此,它已有一个用于保存窗口句柄的成员变量m_hwnd。这样,CButton的成员函数可以直接使用这个变量,并不需要再传递窗口句柄了。
另一点需要注意的是,按钮的父窗口不同,其显示位置也会有所差异。
最后,我们在写程序时,如果不知道某个函数的名称,可以凭感觉利用单词的组合来拼写,通过这种方法一般都能在MSDN中找到需要的函数。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -