原文地址:http://topic.csdn.net/u/20090918/09/1fb4c3a4-0f6f-45d9-8ac1-b241850e3077.html
因为上半年自己刚刚学了点MFC,算是对MFC有了点了解。但也只算是会使用,至于对其更多的细节算不上了解。所以其实所知甚少。这里所要讲的,只是帮助从未接触过MFC的人更快地了解和能使用MFC,减少入门的时间,至于其核心原理,我爱莫能助。另外,因为我在深圳学习MFC期间做的笔记无缘丢失,所以当时总结的很多东西都没有保存下来。所以很多东西都有点忘了,有些想提醒的东西都记不太清楚。所以本文的水准就大为下降。
这里得先提一个东西。就是VC与MFC的关系。VC是一个windows开发的一个软件开发平台,在里面可以进行与C和C++相关的各类工程的代码编写。而MFC只不过是其中的一种工程。所有讲VC的书,除了会讲软件如何使用之外,一般大部分的内容都会围绕MFC的使用来讲。可能是因为MFC是易上手,而最流行。所以如果在入门时想找一些书籍,只要找VC的教程就行了。反而以MFC做关键字得到的搜索结果的那些书对入门帮助不大。
下面讲一下初接触MFC的困难。第一,MFC是一个大的框架。其的运行由内部控制。东西很多。而且系统会自动添加代码,所以当在看一个MFC程序时,很多人往往不知道哪些代码是人写的,哪些是系统自动生成的。第二,因为MFC的运行流程由框架在内部控制,通过表面的代码看不出来程序是如何运行的。这往往会导致一个困惑,就是不知道某部分代码为什么样要加在这里。自己要实现某个功能应该说哪个地方添加代码。第三、MFC一般会建立一个工程,里面一般包括三类文件,APP,VIEW ,DOC。这就更增加了初学者的难度。因为这三种文件各有什么样用,他们之间是什么样关系也是让人困惑的问题。
记得高中语文老师讲过读书分为三个境界。第一重境界,是把书读薄。刚接触一个新事物,读了一些相关的书,便觉得自己知道得已经很多。没有太多需要再学习的了,这叫“把书读薄”。第二重境界,是把书读厚。对该事物的了解越多,发现自己不懂的也越多。需要学习的也越多。这个时候会发现自己不懂的太多了,需要读得书也越多了。所以叫“把书读厚”。第三重,是把书读薄。当读的书越来越多。对某事物的了解也越来越多以后,一切都能成竹在胸。这时候去看相关的书,会发现大部分书讲的东西自己已经全然了解,而且很多书讲的东西都极为类似,只是说法不同。这时候会发现已经没有那么多需要读的书了。所以叫“把书读薄”。
学习MFC亦是如此。我们开始的工作也是要“把书读薄”。先求对MFC有个了解。而不是一开始就捧着大部头的书啃。当能够比较熟练地使用MFC之后。再追求把书读厚。这是后话。这里主要讲怎么从0读薄的问题。
下面讲一下从0接触MFC应该注意和了解的一些问题。
1、不要碰那些MFC的经典书籍,比如《MFC深入浅出》之类的。这应该说是第二重境界要看的书。所谓的名著,都是讲原理的。不讲原理的,难成名著。我们从0开始要着重学习的,是使用方法,而不是深入的原理。
2、找一本薄一点的书。最好不超过300页。一方面,太厚的书往往讲的东西太多。往往令初学者找不着北,而且 看着如此厚的一本书,人的压力会骤增。不必一开始就给自己人这么大的压力。我当时用的是《visual C++实用教程》,人民邮电出版社出的。其实这种书很多,不必非用这个。孙鑫写了本<VC++深入详解>,写得确实不错,但如果你对windows编程一点基础没有,就不要一上来用这本书,因为这本书为了追求知识的系统化,就没有遵循初学者学东西应该先易后难,层层深入的原则。所以不适合0基础的人用。
3、学习要循序渐进。不要期望自己一次性把所有东西都学到。这不现实。这里有必要解释下“循序渐进”这个词的意思。循序渐进,是指从总体到局部,从框架到细节,从战略到战术。我怀疑很多人会觉得这个词是地毯式推进的意思。比如一个画家画一个画,是先在画纸的各个地方都描上几笔。把架构画好。再丰富细节。而不是从左画到右,从上画到下,一次性的完成。很多人,包括我,学习时总想大而全,而且想一次性全消灭。这样往往最后要不坚持不到最后,要么学完的全都忘了。所以我们要做的,就是一开始先找一本薄一点的书,把框架性的东西学一学。懂得皮毛,再去看一些更细的东西。而不应一开始就捧一本砖头啃。
4、要动手操作,熟能生巧。对于书上所讲一些东西。比如添加菜单之类的,看起来很简单,但还是要自己动手操作。另外,可以自己也可以动手去实现一些日常中常见到的程序,比如安装文件的流程(不是实现安装文件的功能,而是把安装文件的对话框实现)
5、不要过分追究细节,刚开始遇到难懂的原理,可以略过去。一般讲VC的书一开始都会简单讲一下MFC的内部运行机制和原理。如果没有看懂,或者自己发现了一些不懂的地方,不必细抠。我们刚开始学习所追求的就是,我知道这样做能达到这样的效果和目的。至于为什么能达到这样的效果和目的,以后再研究。这就够了。
6、如果时间允许,可以先看一下孙鑫的API视频教程。最好能对windows API,HANDLE,消息映射这些基本概念有一些了解。这样学起来会容易很多。不看其实也可以,到时候可以到网上查一下。
7、要用MSDN和网络。书上永远不会把什么都讲到。而且你在动手的过程中可能会碰到各种问题。书上可能都不会讲。这时候就需要MSDN和网络了。而且书上用到一些函数时,不会每个函数都介绍,这时候就需要MSDN,这是最权威,信息量最大的了。要习惯查这个。
8、要多读代码。对书上例子。除了要自己动手编之外,还要以搞懂,这个程序是如何动作的。从哪一块函数开始运行,又跳转到哪个函数,最后从哪个函数结束。哪个函数先调用,哪个函数后调用。什么情况下会调用哪个函数。哪个函数什么时候会调用。所有的代码,哪些是你自己写的,哪些是框架加上去的。框架加上去的那些代码,大概有什么用。你不必知道每个函数的细节,只需要知道哪一部分大概是干啥的。只有这些弄懂了,你才能从总体上把握一个程序,当你想达到一个目的,或者添加一段代码的时候,好知道应该改哪里。
9、早一点学习一下VC里如何调度程序。比如CTRL+F10,F10,F11,设置断点之类的,都是最基本的。调程序也有很多技巧。这个网上都有,可以自己查下。
10、要保持连续学习,在把MFC的框架搞懂之前,或者说对MFC有所感觉之前,最后不要中断学习,而且最好每天都能拿出比较多的时候来学。最简单的例子,你读一个程序的代码,如果把第8条讲到的都弄懂,可能就需要一天的时间。如果中间断了,以后再连起来,需要更多时间,而且效果也不好。
11、要做简单的笔记。这很重要。比如你在看代码时,自己人想明白了一个东西。或者发现一个书上没讲的东西,或者出现了某个错误自己解决掉了,最好做个简单的记录。MFC东西太多。
12、温故而知新。你按照书上一个例子操作了一次以后,可能当时觉得挺好,也挺会。当过上两天以后,就又忘了怎么操作了。所以要经常温习。
13、要对类的知识有所了解。因为MFC是通过类实现的,所以不懂类的话很难进行。请先花一到两周的时间(如果有更多的时间更好),学一下C++的知识。至少要学会类、类的继承(多重继承可以先不学),虚函数和多态。如果不懂这些,你很难理解MFC的框架。
14、如果时间允许,最好先学一下孙鑫的视频教程<VC++API>部分,可以先让你对windows的handle和消息映射有一些概念,MFC就是对这些API的封装。为什么推荐看孙鑫的视频而不推荐先看他的书,是因为相对于一本766页的书来说,几个小时的视频显然更生动而信息量更少,更易于接受。
下面推荐一下学MFC要使用的一些相关工具。
1、用VC6.0。它的界面和操作比较简单,容易上手。比VS2005什么的简单的多。虽然没有它好用。
2、MSDN,一定要装一个MSDN。
3、书,我觉得我用的那本《visual C++实用教程》就挺好的。其实这种书很多,不必非用这个。
4有个visual assistx 可以在写代码的时候自动给出可选择的函数,或者补全函数。听说挺方便的,没用过。
在MFC上花多小功夫,得看你想学到什么程度。如果你只是以MFC为框架搭一个东西。那么你需要学以下几部分:1、MFC程序的基本框架。2、菜单和加速键。3、对话框。4、常用控件。如文本控件,CEDIT,CBUTTON之类的。有些控件也不用学,比如树形控件什么样的,基本不会用到。有这几部分,就基本够用了。如果需要进行图象操作,就再学下:图形图象部分。如果需要与很多文件打交道。还需要学下文件操作部分就够了。基本上会以上这些在日常的开发中就基本够用,如果什么不会可以现学。
学习分为两种,一种是学了再用。另一个是需要用的时候再学。两者各有优劣。我觉得以上介绍的部分是在“学了再用”的阶段应该学的。而如果这些都学的差不多,就可以等到需要的时候再学。因为这时候对MFC的框架已经有所了解,可以举一反三了。学起来就快了,另外,提早学太多东西性价比低,太容易忘。在“学了再用”的阶段完成后,就应该大量的写一些程序,熟练操作,加深理解。边写边学。等到各部分知识成了体系就基本达到本文的目的了。
MFC因为函数和类众多,很容易让初学者茫然不知所措。其实根本不用担心。因为里面的大部分类和函数我们根本不会用到。我们需要记住的,只是一些非常基本的函数。然后对于有一部分函数,我们只需要知道他们是干什么的,到时候看到了懂就行。还有一部分函数,只需要知道有这些个函数,至于具体怎么回事,可以等碰到或用到的时候再去查。再剩下的函数,你根本不用去关心它们。因为他们在一般的程序中根本不会出现。你也根本就用不到。
下面讲一点关于MFC框架的东西。在MFC是将SDK(就是纯用windows API编出来的程序)中的API进行了类的封装。比如在SDK编程时一个windows程序大概如下:
WinMain(…)
{
MSG msg;
RegisterClass(…); // 注册窗口类
CreateWindow(…); // 创建窗口
ShowWindow(…); // 显示窗口
UpdateWindow(…);
While(GetMessage(&msg,…)){ // 消息循环
TranslateMessage(…);
DispatchMessage(…);
}
return msg.wParam;
}
以上各函数的作用这里不解释。有兴趣看一下孙鑫的视频或者到网上查一下。基本上MFC就是将一个MFC窗口中可以动态显示的部分封装成VIEW类。将菜单栏,工具栏这类的基本位置和形式固定的部分封成MAINFRM类,将与文档操作有关的封装成DOC类。然后将While(GetMessage(&msg,…)) // 消息循环
{
TranslateMessage(…);
DispatchMessage(…);
}
即消息循环部分封换成message map(即消息映射)即大部分程序中有的BEGIN_MESSAGE_MAP(CAaaView, CView)
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
这里的ON_COMMAND(A,B)中,A就是代表消息名称,B代表消息的处理函数。另外系统还对一些消息有默认的处理。比如当碰到WM_QUIT消息时,消息循环就中止,不再接受新的消息,同时整个程序开始退出操作,比如释放内存之类的。另外WinMain部分也被隐去。
大概来说,一个MFC程序运行如下:1、所有MFC程序必须从CWinApp类中派生出一个新类。然后生成一个该派生类的实例(一般叫theApp)。2、然后系统会自动调用(实际是在隐藏的WinMain里调用)该派生类的InitInsance()函数,在InitInstance()函数中调用CMainFrm类的派生类进行主窗口的初始化。然后调用ShowWindow和UpdateWindow函数显示主窗口。其中UpdateWindow函数会发送一个WM_PAINT消息。然后退出InitInstance()函数。3、进入Run函数,进入消息循环。Run会处理刚才的WM_PAINT消息,怎么处理呢?就是调用View类的OnDraw函数(如果没有重新定义OnDraw函数。系统就会按照默认的操作,将屏幕清成白色:注意,为简化开发者的负担,有一些函数有默认的操作,虽然没有任何代码(相关代码被隐藏了),有一此消息有默认的处理函数,当然你也可以重新定义)),OnDraw会执行开发者定义的操作,如果开发者没有重写该函数,系统会默认地用白色清屏。这就是为什么很多MFC程序主窗口是白色的。
然后整个系统的所有操作都是Run函数在处理消息。比如你按一个菜单项,会向消息序列中发送一个菜单消息。Run函数会根据指定的该消息的处理函数去调用这个函数来处理相关消息。按一下按钮也会生一个控件消息(按钮属于控件),其实系统一直在不断地生成消息。按下键盘,会根据键的不同,而分别生成WM_CHAR,WM_SYSCHAR消息。移动鼠标会生成WM_MOUSEMOVE消息。按下鼠标左键会生成WM_LBUTTONDOWN 消息。双击鼠标左键会生成WM_LBUTTONDBCLK消息。当你点击滚动条时,会发送WM_PAINT消息。4、当按下窗口右上角的关闭按钮时,会生成一个WM_CLOSE消息,该消息的默认操作是生成一个WM_QUIT消息。(另外,你也可以主动地发送WM_QUIT消息)Run函数收到该消息后,会结束消息循环,并调用ExitInstance()函数,对各个类的实例的进行析构。并注销掉窗口,退出进程。程序就彻底结束了。
一个MFC程序的大概流程就是这样子。这里的Run()函数,ExitInstance()一般会隐含调用,读者不必关心这个函数在哪。这样设计的好处是,开发者只需要定义消息和消息的处理函数,。不必操心整个程序的运转。大大减少开发的工作量。另外开发者可能还需要根据自己的需要添加一些新类。
在很多类中。比如View,MainFrm类中都有消息映射函数。如果要添加消息映射函数应该加到哪个类中呢。MFC中会按照一个消息处理流程(具体这里不讲)依次问询相关的类。如果在某个类中找到相应的处理函数,就不再向下搜索。所以只需要在一个类中定义某个消息的消息映射就好。一般来说是根据最大相关性来添加的,也就是说跟哪个相关最大,就在哪个类里定义消息映射。
另外,在MFC中有一些函数会被默认调用(很像回调函数)。比如在系统在调用Create()函数创建主窗口之前,会默认调用preCreateWindow函数。所以你如果想对窗口进行一些初始化,可以加在这个函数中。这些东西要注意收集和了解。另外,系统也会默认发送一些消息。就像刚才说的,点击滚动条,最大最小化窗口,会发送WM_PAINT消息。这些课本上不会讲,都需要自己到网上和MSDN上查,也需要自己悟。想想看,点了滚动条之后,系统当然需要重绘窗口,想当然的要发送WM_PAINT,但做学问要严谨,想当然的事,也要去确认下,这样自己心里才会有底和踏实。
OK,讲得差不多了。下面讲一个更具体的学习流程。
1、装一个VC6。0,MSDN,找一本讲VC的书。
2、按书上的办法新建一个MFC的工程。(先选single Document)
3、依次看里面的各个文件,看里面都有些什么东西。每一部分大约作用是什么。书上一般会有讲解,对照书看。并注意VC开发环境里的各个菜单,工具栏,窗口都是干什么的。
4、对照课本学习MFC程序的基本框架(课本)
5、学习菜单和加速键。按照课本上例子操作。
6、学习对话框的操作。包括模态对话框,非模态对话框,属性页对话框,通用对话框中的文件对话框,颜色对话框,字体对话框。学完这些就OVER了,这些比较重要和基本,剩下的以后再说。(注意,学完这部分应该知道single document, dialog based 的区别 )
7、常用控件。包括,CEdit, CStatic,CScroolbar, CButton, CListBox。先学完这几个就OVER了,剩下的以后再说。
学到这一步,我觉得就应该说对MFC有一定的理解和感觉。这个时候基本上过了学了再用的阶段,可以进入“需要的时候再学”的阶段了,另外如果想深入,这个时候也可以看孙鑫的<VC++深入详解>,这里面对MFC的很多功能进行了系统的介绍,查阅起来方便。再深就是《MFC深入浅出》了。如果没有,说明你没有用心学,只学了一些皮毛。
对于0基础的MFC学习者来说,最难把握的,是求甚解和不求甚解之间的平衡。如果一味的不求甚解,那到了最后大脑里还是空白一片,成不了体系,学到的只是课本上的东西。对于MFC是什么,还是不懂。而如果一味地求甚解,会发现要查的东西实在太多。到最后会陷于细节而崩溃。我觉得 基本的原则是:如果说你觉得 这个细节有助于你理解整个框架,那么就去查一下。如果没有,就跳过。
最后,我非常非常强调一点,这一点重要到会影响学习MFC的成败。就是一定要保证当你学到第7步时,你所学到的东西只有不到三分之一是从课本上学来的,剩下的超过三分之二是从网上和MSDN上学到的。一定要记住这一点。课本上的东西太框架了。只学课本上的东西有此重要的细节学不到。学到最后,要自己能总结出东西,自己人总结不出东西,实在是很大的失败。
因为自己有两个月没接触这个了。所以有很多东西变得模糊。也许将来会进一步写一些更具体的东西。水平实在是不限,也只能指导下场基础的了。