http://wenku.baidu.com/view/14407a10a216147917112885.html/
MFC入门指导
2011-3
说明
本文从零讲起,内容简单,旨在帮助MFC零基础的同学快速上手,可以短时间内做出简单的可视化界面。工程环境:Visual C++ 6.0;语言基础:C++。
1. 怎样创建MFC工程
打开VC6,新建工程,选择MFC AppWizard(exe),点击OK。
图 1 新建MFC工程
下面选择工程类型,分为三种:单文档、多文档、对话框。我们使用单文档及对话框较多。一般情况下,单文档类处理文字、绘图较多,对话框类更多是通过控件与用户交互,两者亦互通,没有绝对的选择规则。
图 2 选择工程类型
一路点击Next,直到如图3所示界面,最后一项选择MFC库的连接方式。默认为动态连接,这样做出的可执行文件较小,但发布时需要附带MFC库,或者需要在装有MFC库的机器上运行。也可以选择静态连接,这样的可执行文件较大,但对运行环境没有要求。对于较小的工程,推荐使用静态连接。选择完毕后,直接点击Finish,完成工程创建。
图 3 选择MFC库连接方式
2. 主函数在哪里
MFC工程不像控制台工程那样,有一个明确的主函数,作为程序的入口。MFC的运行是基于消息响应机制的。形象的说,一个MFC程序运行起来,完成了一系列初始化工作后,就静静的等在那里,等待用户的动作(譬如按下按钮,选择菜单项)。我们不需要追究代码运行的源头,只需要为相应的操作写对应的代码即可。
3. 在哪里添加自定义变量
首先要了解MFC自动生成了什么。如图4所示,为对话框和单文档自动生成的几个类。对话框中的C***Dlg类以及单文档的C***View类是我们添加代码较多的类,我们要重载的响应函数大多数都在这两个类中,所以可以将自定义变量写成他们的成员变量,既不影响使用的方便性,又保证了良好的封装性。
如果需要建立较复杂的数据结构,如队列、二叉树、链表等,建议单独自定义类,再将头文件添加到Dlg或View类中,声明其对象为成员变量。
图 4 MFC自动生成的类
4. 变量的初始化在哪里完成
对于单文档类,变量的初始化工作可以放在构造函数C***View::C***View()中;对于对话框类,初始化应在初始化函数BOOL C***Dlg::OnInitDialog()中完成。
5. 如何添加菜单
以单文档为例,找到Resouce中的Menu文件夹,双击里面的菜单对象,进入菜单编辑界面。双击空白菜单,可以新建母菜单栏,之后可以新建菜单项,注意菜单的ID要起好名字,以免写响应函数时混淆不清。
图 5 编辑菜单栏
图 6 添加菜单项
点击View->ClassWizard,或者Ctrl+W(强烈建议大家使用这个快捷键),打开ClassWizard。ClassWizard是VC6集各种强大功能于一身的特色工具,一定要熟练应用。在Message Maps中的Object IDs框内,选中刚刚建立的菜单项,再在Messages框内选中COMMAND,即鼠标点击后的消息响应,点击Add Function,MFC将自动添加这个响应函数,再点击Edit Code,即可跳到函数中编写代码了。
这里需要注意的是,要选好响应函数所在的类。函数生成到哪个类中都可以顺利运行,但一般我们集中放在View类中,方便共享View类中自定义的成员变量。
图 7 用ClassWizard添加消息响应函数
6. 如何用简单的对话框做数据输入
首先要创建新的对话框,并设计界面。在Resouce中,右键单击Dialog文件夹,选择InsertDialog,完成新对话框的创建。默认的对话框界面只包含两个按钮,OK和Cancel,保留他们不动。添加一个Edit Box控件,用来接受键盘输入的数据,一个Static Text控件做提示。分别修改两个控件的属性,Static控件只需修改Caption属性,Edit控件建议修改控件ID,方便程序阅读和管理。
图 8 添加控件并修改ID
Ctrl+W打开Class Wizard,会弹出提示是否创建新的类,点击OK,输入类名后再OK,完成对话框类的创建。在Class Wizard中,选择Member Variables选项卡,找到刚刚建立的Edit控件ID,点Add Variable,为控件添加变量。变量添加完毕后,退出ClassWizard。
图 9 为控件添加变量
建立变量后,Edit控件中输入的数值就与变量建立了连接,但并不是实时更新的,需要手动更新变量。更新通过UpdateData()来完成,默认参数为true,表示将Edit控件中的数值传递给控件的变量,参数为false则相反,用变量修改控件显示的数值。数据的更新可以放在OK按钮响应函数中,双击OK按钮重载OnOK函数,添加代码即可。
void CInputDlg::OnOK()
{
// TODO: Add extravalidation here
UpdateData(); //更新数据
CDialog::OnOK();
}
对话框功能设计完成后,剩下的工作就是如何调用并在主调函数中获取键盘输入的数值。首先声明一个对话框类的对象,并通过成员函数DoModal()显示对话框。示意代码如下:
CInputDlg dlg;
if(dlg.DoModal()==IDOK){
CString str;
str.Format("输入的数据是%d", dlg.m_edit_data);
MessageBox(str);
}
这里只举例讲解了Edit控件的使用方法,还有多种控件,使用起来大同小异,可以查阅其他资料自行学习。
7. 如何画图
建议自定义的绘图代码加入到系统重绘响应函数中,这样保证了系统自动重绘的时候,我们画的图不会被漏掉。单文档类,可以写在OnDraw函数中,对话框类,可以写在OnPaint函数中。下面以对话框为例,举几个简单的例子。
对话框中OnPaint函数,自定义的代码写在原代码的else部分。常用的设备类有CPen、CBrush、CFont等。设备的创建函数往往有多种重载方式供大家选择,根据需要选择一种使用即可。下面给一段示例,绘制一个矩形一条直线以及一段文字。
CPaintDC dc(this); //获取绘图DC
CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); //创建线宽为1,黑色的实线画笔
CBrush brush;
brush.CreateSolidBrush(RGB(100, 100, 100)); //创建灰色的画刷
CFont font;
font.CreatePointFont(400, "Arial"); //创建Arial点数为400的字体
dc.SelectObject(&pen); //将画笔选入设备
dc.SelectObject(&brush); //将画刷选入设备
dc.SelectObject(&font); //将字体选入设备
dc.SetBkMode(TRANSPARENT); //设置字体背景透明
//绘制矩形
dc.Rectangle(50, 50, 400, 200);
//绘制直线
dc.MoveTo(30, 20);
dc.LineTo(420, 230);
//写字
dc.TextOut(110, 20, "hello MFC!");
pen.DeleteObject();
brush.DeleteObject();
font.DeleteObject();
图 10 一个简单的示例
绘图代码写在OnPaint之后,系统只会在必要的时候调用刷新(比如移动窗口、窗口最小化后再打开等),如果需要手动刷新,可以使用函数Invalidate()进行强制重绘。默认参数为true,系统自动擦除背景后重绘,参数为false时,不会自动擦除背景。如果对刷新速度有较高要求,且不需求擦除背景的时候可以选择false。
8. 怎样解决高速刷新时屏幕闪烁的问题
上一段中使用Invalidate(false)强制重绘是一种简单的解决办法,省掉了系统擦除并重绘背景的时间。下面再从绘图时间入手,介绍一种非常常用的双缓冲技术。
所谓双缓冲,就是首先在内存中建立一个DC,然后将要绘制的图形先绘制到内存DC中,最后一次性从内存中拷贝出来到当前DC,可以有效的解决闪屏问题。以对话框为例,给出一段参考代码。
CPaintDC dc(this); //获取绘图DC
// 创建内存缓存DC
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bufferMemBitmap;
CRect rect;
GetClientRect(&rect);
bufferMemBitmap.CreateCompatibleBitmap(&dc, rect.Width(),rect.Height());
memDC.SelectObject(&bufferMemBitmap);
CBrush whiteBrush;
whiteBrush.CreateSolidBrush(RGB(255, 255, 255));
memDC.FillRect(CRect(0, 0, rect.Width(), rect.Height()),&whiteBrush);
// 使用memDC做绘图操作
// 将内存缓冲DC拷贝至当前DC
dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(),&memDC, 0, 0, SRCCOPY);
bufferMemBitmap.DeleteObject();
memDC.DeleteDC();
9. 怎样添加鼠标键盘响应
打开ClassWizard,在Messages框中可以找到所有的鼠标键盘消息响应。譬如鼠标移动WM_MOUSEMOVE,左键按下WM_LBUTTONDOWN,键盘按键按下WM_KEYDOWN,键盘字符消息WM_CHAR等,选中要添加的响应,点击Add Function,进入编写代码即可。 举个简单的例子,做一个跟随鼠标运动的空心圆。首先在dlg类头文件中添加成员变量m_mousePos。
CPoint m_mousePos;
添加鼠标移动响应,记录当前鼠标的位置。
void CDlgTestDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add yourmessage handler code here and/or call default
m_mousePos = point;
Invalidate(false);
CDialog::OnMouseMove(nFlags,point);
}
由于鼠标移动是高速刷新,为避免闪屏,强制重绘不擦除背景,且在OnPaint中使用双缓冲技术,并在绘图部分添加下述代码:
memDC.Ellipse(m_mousePos.x-50,m_mousePos.y-50,m_mousePos.x+50,m_mousePos.y+50);
这样就做出一个随鼠标移动,半径为50的圆了。
10. 怎样固定频率刷新图像
设定一个定时器,就可以每隔固定时间给出中断,进行对图像数据的更新,达到刷新图像的目的。开启定时器的函数为SetTimer,关闭定时器的函数为KillTimer。定时器消息WM_TIMER需要在ClassWizard中添加响应函数OnTimer。
举个简单的例子,从程序开始运行起,每1s刷新一次图像。
首先在OnInitDialog()函数中开启定时器。
SetTimer(1001, 1000, NULL); //开启定时器,ID为1001,中断间隔1000ms
打开ClassWizard,找到消息WM_TIMER,点Add Function,重载响应函数OnTimer。
void CDlgTestDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add yourmessage handler code here and/or call default
switch(nIDEvent){
case 1001: //ID为1001的定时器代码部分
{
//添加图像数据更新代码
//强制重绘
Invalidate(false);
break;
}
default:
break;
}
CDialog::OnTimer(nIDEvent);
}
小结
MFC入门简单,精通难,本文针对零基础的同学,列举了一些非常简单的操作。能够学习并合理运用上述知识点,已经可以做出简单的可视化人机交互界面。作者能力有限,所写不多,更深一步的学习,还需要多借助MSDN、参考书籍、网上资料的帮助。