文章来自:http://sakura006.blog.hexun.com/31435400_d.html
写下这篇小结,最主要的目的,是让自己复习一下这两日的所学,并方便以后记忆;另外,也希望跟一些曾经跟我一样经历弯路,对 MFC 有兴趣的人分享一下(按着我的思路,我自己倒是挺明白的,不知道别人能否看得懂。下面的心得部分可以跳过的!)。
这两日的心得
这两天没什么事儿可做,从上头儿也得不到任务,总不能够别人天天儿的晾着你,你就不争气的也得过且过,自己晾着自己吧!碰巧昨天早上拎起来一本书(忘了是出于什么样的目的了),然后拿到手里之后,就没再搁下。
其实一直想入门 MFC 的,常常听人说“ VC 只是一个开发环境, MFC 才是它的精华”。可是自从开学以来,近一个月内,尝试了很多途径,看书,边看边自己动手编,电子的和书面的都看过,只是还是觉得书本上的代码,在自己真正去尝试的时候,也是晦涩难懂,不易操作的。印象较深刻的几本书是孙鑫的《 VC++ 深入浅出》、《 MFC 深入浅出》、《 windows 程序设计》,这些都是口碑不错的书,只能说自己的根基太差,所以也不得领悟吧。也在网上看一些“过来人”的 blog , 看相关的日志,连载就是十几篇,一般都是开始的时候,兴致还不错,越到后边,遇到的错误也越来越多,还无人能给以解答,直到最终难以继续往下,不得不搁 浅。可惜,通过这一切都没能走上所谓的“正途”(正确的途径啦)!没想到偶然间瞥见的那一眼,却给我带来了不小的帮助,让我从一个彻彻底底的门外汉踏进了 MFC 的大门(也许还是有些片面吧,说到底,还是只学了两天呀)。
有一些体会,也许到现在才体会出来,有些晚了,但是至少还是明白了,也不错,就这么一点一点的坚持到底,也是好的!
切身体会,首先值得明确的是, C++ 和 VC++ 是不同的。我用了两个星期的时间把 C++ 的书翻了一遍,对 C++ 的 基本内容有了一个整体上的了解,如类、继承、封装、多态、模板等,但是当自己刚刚编程序的时候,还是不能领悟“面向对象编程”的真谛。甚至还曾误认为自己 编出来的第一个程序就面向对象的程序,后来我承认那不过是一种最简单的面向过程的编程方法。我为自己曾经萌生的那么一点点小骄傲的情感,实感到惭愧。 C++ 只是一门语言, VC++ 则是 C++ 语言的一个编译环境,软件开发工具。而 MFC 则是 VC++ 的本质和精华所在。所以学习 VC ,必须得掌握 MFC 。
曾经看到过这样一些经验的话,大概是这样说的:学习 VC ,不要像我们学英语那样的方法,而要像婴儿学说话一样。想象确实有道理,我们在最初学 C 的 那种模式(详细的弄懂每个细节,在课本上纠错,记下所有的语法规则,却很少上机实践,不着重于解决实际问题,最终以考试成绩来断定学习成果)。这种模式的 后果是很可怕的,考试完了,能有几人能够上机实现一个简单的程序?就像我们学了十几年的英语,虽然单词背了几千个,我们会做题,会写作文,但是真正接触外 国人的时候,却只能当个哑巴和聋子。而像婴儿一样学习,则是在使用中学会语法和词汇,一开始可能很糟糕,但是学习效率却是高的。学习 VC 就应该从最简单的应用程序开始,由简单向复杂迈进,就像滚雪球一般,越滚越大。而不应该着重于那些语法、句法的细节之上。所以迈进 MFC 的大门,也从最简单的开始
也正是现在的我,当我再次想起这些话的时候,才颇有些感触,因为似乎这一个多月以来,一直在走弯路,而此刻却有一种走上正途的感觉,虽然前面的路是未知的,也许存在艰难,但是艰难却阻挡不了前路的一片光明。
下面总结一下这两天来,入门的一些历程和知识——
编写visual C++ 程序首先要创建一个良好的可视化界面,而每个程序界面是由对话框( Dialog )和一些必要的控件( Control )构成的。界面设计的一般步骤:在对话框上布置控件;设置各控件属性;设置对话框属性。
Example:设计如下图所示的计算器:
下面来创建一个对话框应用程序,步骤如下:
第 1 步:创建新项目 new project
1. 在 IDE 环境中,选择 File->New ,弹出 New 对话框。
2. 在对话框的左侧列表框中选择 MFC AppWizard (exe) 。
3. 在 Project Name 文本框中输入项目名称: Example ;在 Location 文本框中输入项目存盘路径,或单击右边的按钮选择相应的路径 ;选中 Create new workspace 单选按钮 ;在 Platforms 列表中选中 Win32 选项 ;点击 OK 按钮。弹出 MFC AppWizard 对话框。
如图1所示:
图1
第 2 步: MFC AppWizard 向导
在 MFC AppWizard 对话框中:
1. step1 of 4 选择程序的文档支持类型:选择 Dialog based 、语言支持选择简体中文、单击 Next 按钮,如图2示;
2. step2 of 4 中,选择程序的界面选项:取消对 About box 的选择 , 单击 Next 按钮,如图3示。
3. step3 of 4 ,选择程序的其他选项:一般选择 As a statically linked library,如图4示。
4. step4 of 4, 确认文件和类名:这里选择默认即可,点击 Finish 按钮,如图5示。
本对话框中,显示了 AppWizard 为用户所产生的类,及该类的基类和存储该类的头文件和源代码文件。
5. 弹出 New project information 对话框:显示用户在前面的选择,如图6示。
6. 点击 OK 按钮, AppWizard 将按设定的选项为用户产生代码。此时开发环境中会显示当前应用程序。创建对话框应用程序成功,如图7示。
图2
图3
图4
图5
图6
图7
第 3 步:定制对话框
新建一个基于对话框的应用程序,删除向导自动添加的控件。定制对话框一般需要的步骤如下:设置对话框本身的属性;向对话框放置控件;通过控件属性对话框设置各个控件的属性。
1. 设置对话框本身的属性(右键 ->Properities )
属性对话框中,默认 caption 为 EXAMPLE 更改为“简易计算器”
Font 选项,更改字体风格和大小,默认为宋体、小五,如图8所示:
图8
2. 添加控件
添加控件,借助于控件工具箱,如图9所示,具体按钮的功能,见后。
图9
选取适当的控件,改变控件位置,调整控件大小
注意 1 :想要删除控件时:选中控件,选择菜单 Edit|Delete 即可!!或者直接使用键盘上的【 delete 】键。
注意 2 :在对话框上显示网格,这些网格可以方便用户调整控件的位置和大小,且控件总是自动对齐网格: layout->Guide Settings , 默认设置为 Rulers and guides ,更改为 Grid 就会在对话框的设计阶段显示网格。另外,通过 layout 菜单中的命令,可以对控件进行快速布局,如对齐、统一大小、调整控件之间的水平间距和垂直间距等操作。注意控制布局的基准控件的选择秘密就在控件的选择顺序上,使用【 shift 】 + 鼠标单击选择多个控件时,最后一个被选中的控件为基准控件。
网格只在程序的设计阶段出现,运行时不显示。如图10所示:
图10
本例中,需要添加 2 个 Buttton 控件、 1 个 Group Box 控件、 4 个 Radio Button 控件、 3 个 Edit Box 控件、 2 个 Static Text 控件。如图11所示.
图11
3. 设置控件属性
使用属性窗口,对于对话框窗口及各种控件,单击鼠标右键选择 Properties,
四种属性设置的类型: ID 为按钮的 ID , Caption 为按钮在界面上显示的内容, Font 属性,改变命令按钮的字体名称、字体样式,如图8所示。
设置完毕后各控件的属性如下:
控件类型 | ID 属性 | Caption 属性 | 附加 |
Button | IDCANCEL | 退出 |
|
Button | IDC_BUTTON_CAL | 计算 |
|
Group Box | IDC_STATIC | 选择运算类型 |
|
Radio Button | IDC_RADIO_ADD | 加 | 选择 Group 属性复选框 |
Radio Button | IDC_RADIO_SUB | 减 |
|
Radio Button | IDC_RADIO_MULTI | 乘 |
|
Radio Button | IDC_RADIO_DIV | 除 |
|
Edit Box | IDC_EDIT_VALUE1 |
|
|
Edit Box | IDC_EDIT_VALUE2 |
|
|
Edit Box | IDC_EDIT_RESULT |
|
|
Static Text | IDC_STATIC_FLAG | + |
|
Static Text | IDC_STATIC | = |
|
Check Box | IDC_CHECK | 立即显示运算结果 |
|
设置完成后如图12所示:
图12
定制完毕后,可以通过 layout->Test 菜单预览对话框,按【 ESC 】退出预览,如图13所示。
图13
第 4 步:定义成员变量
“增加了这么多的控件,那么如何在程序中协调和控制这些控件呢?如何建立控件之间的联系?程序如何得知用户对控件的操作并做出响应呢?
解决这个问题的方法是:定义一些与控件相联系的变量,在程序中,通过这些变量来完成对控件的控制;为控件添加事件处理函数,将用户对控件的操作(如单击按钮、改变单选按钮的选择等)作为一个事件通知给程序,由程序的事件处理函数完成对用户操作的处理。”
为控件定义变量的简单方法是:通过 ClassWizard 。即选择菜单 View|ClassWizard ,弹出 MFC ClassWizard 对话框,选择其 Member Variables 窗口。选中 Control ID 中的一行,双击鼠标或单击 Add Variable… 按钮,在弹出的对话框中为对话框增加与控件相联系的成员变量。如图14所示。
选择变量的类别 category 为 value 表示所定义的变量为与控件相联系的一个值,这个值的含义随不同类型的控件而不同 变量类型、变量名(如编辑框,变量表示在编辑框所输入的内容;复选框,表示该复选框是否被选择);选择 category 为 control ,则表示所定义的变量是控件类的一个对象(如编辑框, control 变量的类型为 CEdit )。
图14
注意 1 :对于一个控件,可以同时定义它的 value 变量和 control 变量,但是不能为一个控件定义两个 value 变量或两个 control 变量。
注意 2 :没有必要为按钮增加关联变量。
注意 3 :对于 value 类型的数值,可以设定数值的范围;对于 CString 类型可以设定最大字符的长度,如图。。所示,如密码操作时可以使用;而编辑框,则可以设置为隐藏。
本例中,需要增加的变量如下图15中所示:
图15
注意 Class name 选择 CExampleDlg
第 5 步:增加事件处理函数
可以在 ClassWizard 的 Message Maps 选项卡为控件添加事件处理函数,也可以通过专门的事件处理对话框。
如选中“计算”按钮,单击鼠标右键,在弹出的快捷菜单上选择 Events ,弹出的对话框,用于添加、删除窗口的消息和事件处理函数。如图所示,本程序需要在鼠标单击“计算”按钮时计算运算结果,故需要为此按钮增加单击事件处理函数,方法是双击左边列表框中的 BN_CLICKED 一行,这时弹出一个对话框可修改事件处理函数的名字,如图所示。保持默认名字,直接单击 OK 按钮即可。
增加了处理函数的事件名移动到了右边的列表框中,单击 Edit Existing 按钮(直接双击“计算”按钮)可以直接进入到对话框的源文件中,为事件响应函数增加代码。如 下图16、17所示
图16
图17
增加了处理函数的事件名移动到了右边的列表框中,单击 Edit Existing 按钮(直接双击“计算”按钮)可以直接进入到对话框的源文件中,为事件响应函数增加代码。
同样的方法为下列的事件增加处理函数:
IDC_EDIT_VALUE1 的 EN_CHANGE
IDC_EDIT_VALUE2 的 EN_CHANGE
IDC_RADIO_ADD 的 BN_CLICKED
IDC_RADIO_SUB 的 BN_CLICKED
IDC_RADIO_MULTI 的 BN_CLICKED
IDC_RADIO_DIV 的 BN_CLICKED
BN_CLICKED 事件当鼠标单击按钮类控件(单选按钮、复选框都属于此类)时触发;而当用户双击按钮类控件时, BN_DOUBLECLICKED 事件被触发; EN_CHANGE 指编辑框的内容被用户改变了; EN_KILLFOCUS 指控件即将失去输入焦点; EN_HSCROLL 指用户单击了控件的水平滚动条。
第 6 步:增加程序代码
为对话框增加了控件事件处理函数后,这些函数的函数体都是空的,需要手工添加代码来实现用户想要的功能。
打开 ExampleDlg.cpp 文件,为对话框添加的所有事件处理函数都在这个文件中实现(在 ExampleDlg.h 中声明),
各关联变量的初值在函数 ExampleDlg::ExampleDlg (它是本程序主对话框类 Example 的构造函数)中初始化,我们需要在这儿修改某些变量的初值。把 m_dOpr 的初值由 -1 改为 0 ( -1 表示没有单选按钮被选中, n 表示第 n-1 个单选按钮被选中);把 m_strOpr 的初值变为“ + ”,以便在程序开始显示“ + ”号而不是别的运算符。
双击“计算”按钮,在相应的代码块添加代码:
void CExampleDlg::OnButtonCal()
{
// TODO: Add your control notification handler code here
UpdateData(true);
switch(m_dOpr)
{
case 0: // 加
m_fResult=m_fValue1 + m_fValue2;
break;
case 1: // 减
m_fResult=m_fValue1 - m_fValue2;
break;
case 2: // 乘
m_fResult=m_fValue1 * m_fValue2;
break;
case 3: // 除
m_fResult=m_fValue1 / m_fValue2;
break;
}
UpdateData(false);
}
同理,点击 edit1 、 edit2 、加、减、乘、除,在相应的代码部分,增加以下代码
void CExampleDlg::OnChangeEditValue1()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialog::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Add your control notification handler code here
UpdateData();
if(m_bAtOnce)
{
OnButtonCal();
}
}
void CExampleDlg::OnChangeEditValue2()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialog::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Add your control notification handler code here
UpdateData();
if(m_bAtOnce)
{
OnButtonCal();
}
}
void CExampleDlg::OnRadioAdd()
{
// TODO: Add your control notification handler code here
UpdateData(true);
m_strOpr="+";
UpdateData(false);
}
void CExampleDlg::OnRadioDiv()
{
// TODO: Add your control notification handler code here
UpdateData(true);
m_strOpr="÷";
UpdateData(false);
}
void CExampleDlg::OnRadioMulti()
{
// TODO: Add your control notification handler code here
UpdateData(true);
m_strOpr="*";
UpdateData(false);
}
void CExampleDlg::OnRadioSub()
{
// TODO: Add your control notification handler code here
UpdateData(true);
m_strOpr="-";
UpdateData(false);
}
OnButtonCal() 用于当用户单击“计算”按钮时计算两个数的运算结果,并显示到结果编辑框中; OnRadioAdd() 、 OnRadioDiv() 、 OnRadioMulti() 、 OnRadioSub()4 个事件处理函数用于当用户通过单选按钮改变所要进行的运算类型时(如由加变为除),程序控制 IDC_STATIC_FLAG 静态文本框中显示的运算符也做相应的改变(如由“+”变为“÷”); OnChangeEditValue1() 、 OnChangeEditValue2() 用在当“立即显示运算结果”复制框被选择时,这是每当左边的两个编辑框中的内容有所改变(表示参与运算的两个数之一已经改变)程序就应该立刻重新计算运算结果。
以上代码频繁的使用函数 UpdateData() ,格式如下:
BOOL UpdateData(BOOL bSaveAndValidate=TRUE);
UpdateData 是 MFC 类 CWnd 的成员函数, CWnd 类是重要的 MFC 类,所有窗口类都直接或间接的继承于它,例如视类 CView 、工具栏类 CToolBar 、对话框类 CDialog 、按钮类 CButton 及编辑框类 CEdit 等。
本程序的 CExampleDlg 继承了 CDialog 、而 CDialog 又继承了 CWnd ,故可以在程序中使用函数 UpdateData 。
以参数 TRUE 来调用函数 UpdateData() 的作用是更新所有与对话框控件相关联的变量值;而以参数 FALSE 调用此函数则更新与变量相关的控件的显示状态,使之与变量一致。即以 TRUE 和 FALSE 为参数分别实现空间关联变量的“里传”和“外传”,默认参数为 TRUE 。
各关联变量的初值在函数 CExampleDlg::CExampleDlg (本程序主对话框类 CExampleDlg 的构造函数)中初始化,需要在此修改某些变量的初值。如下代码:
CExampleDlg::CExampleDlg(CWnd* pParent /*=NULL*/)
: CDialog(CExampleDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CExampleDlg)
m_fValue1 = 0.0;
m_fValue2 = 0.0;
m_fResult = 0.0;
m_bAtOnce = FALSE;
m_dOpr = 0;
m_strOpr = _T("+");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
这里 m_dOpr 初值由 -1 改为 0 , -1 表示没有单选按钮被选中, n 表示第 n-1 个单选按钮被选中;把 m_strOpr 初值变为“ + ”,以便在程序开始时显示“ + ”号而不是别的运算符。
第 7 步:运行程序
程序编写完成,按 [F5] 观察运行结果。
程序运行的初始画面如图所示。选择“除”,计算“ 100 ÷ 11 ”,点击“计算”按钮,记得结果,如图18;若选中“立即显示运算结果”,则在修改文本框中的数字后,程序会自动运算并将结果显示出来,如图19。
图18
图19