庖丁解MFC--(一)文档视图与应用框架剥离

存在的必是合理的,都值得我们学习。学什么不重要,重要的是有一技之长。
如果你认为MFC垃圾请不要继续看。
如果你认为文档视图结构丑陋请不要继续看。
如果你认为ATL过时了请不要继续看。

庖丁解牛的艺术
庖丁为文惠君解牛,分解牛体时手接触的地方,肩靠着的地方,脚踩踏的地方,膝抵住的地方,都发出砉砉的声响,快速进刀时刷刷的声音,无不像美妙的音乐旋律,符合桑林舞曲的节奏,又合于经首乐曲的乐律。庖丁的解释是:我所喜好的是摸索事物的规律,比起一般的技术、技巧又进了一层。我开始分解牛体的时候,所看见的没有不是一头整牛的。几年之后,就不曾再看到整体的牛了。现在,我只用心神去接触而不必用眼睛去观察,眼睛的官能似乎停了下来而精神世界还在不停地运行。最喜欢这句:以无厚入有间,恢恢乎其于游刃必有余地矣。

我的MFC经历
我的大学专业是软件工程,作为一个C++开发人员学习MFC是必然的。记得上大二的时候,有些课程和大三师哥们一起上,总能看到牛师哥们手里捧一本《深入浅出MFC》,羡慕不已。当时数据结构实习,人家都基于Windows做,而自己只能在Console下做,十分尴尬,觉得自己很落后,现在想来有点可笑。
这里要说的是MFC是对WIN32 API的封装,为什么不从WIN32 API开始学呢?很多人是这样做,也见过一些人在这上面气馁而放弃C++转向Java。我是从MFC开始的,当我对MFC比较熟悉的时候,我心里就明白MFC是如何对WIN32 API封装的,进入MFC源码,里面全部调用的API,自然明白了API的功能及用法。学习编程就得由简到难,对MFC深入后,WIN32 API自然浅出。
我是大三开始接触MFC的,当时感觉是MFC是一个庞然大物,拿一些快速进阶之类的书籍东拼西凑学一点技巧,三个月后也没有入门。VC6向导生成的一个多文档程序代码比起自己写过的数据结构代码是一个天文数字,不知道如何下手。看着旁边宿舍非计算机专业的学生用VB就能写一个时钟,羡慕啊,自卑啊,气愤之下开始VB的学习。然而倔强的心里告诉自己怎么可以这样认输呢?在一个星期过去后,我重新开始了MFC的学习。MFC向导可以生成三种应用工程:对话框、单文档、多文档。默认的是微软最为骄傲的多文档,但是这个确实害人不浅。其他编程开发环境没有文档视图的概念,都是窗体、Form,这个结构要简单不少,可以快速入门,找到快感,不至于气馁。我当时就是从对话框开始学习的,向导生成的类比较简单,一个App,一个Dlg。我开始在对话框上学习各种控件,Button、Edit、ListCtrl等,终于算是Windows编程了。大概用了半年时间学习对话框,在对话框编程有一定了解的时候可以用它来做一些数据库、Socket编程,经典例子就是管理系统和聊天工具。对MFC有一些了解后开始学习文档视图模型,开始从单文档入手,然后进入到多文档。文档视图主要是开发企业应用,要考虑菜单、工具栏、停靠条、文档数据到视图更新以及鼠标键盘交互等,考虑很多消息事件处理,掌握起来确实要花不少功夫。
大四我进入公司实习,当时以为对MFC很了解,觉得自己很了不起。当遇到稍微深一点的问题,断点进入MFC源码的时候便手无所措,原来自己只是对MFC的轮廓有一点了解,并不解其内部规律。当时受了打击,这使我静下心来深入研究MFC,再一次看《深入浅出MFC》、操练各种类型应用程序、分类学习WIN32 API、看MFC源码(不敢说很仔细看,至少比较细的浏览一边),当我可以进入到MFC源码调试的时候,我很高兴,因为我进步了。当我接触到BCG库和XTREME库的时候,我都要进入它的源代码看,发现里面很多代码和MFC源码一样,我想写库的那些人对MFC应该算是很了解。而后我又花了半年时间学习COM,用ATL做项目。
现在每当我拿到一款软件的时候,我总要用Spy++去看看它的构造,想一下如果是自己能否做出这样的软件。软件设计不是靠蛮力,而是靠技巧,懂得借力省力。软件好比一头牛,模块之间的松耦合就像牛骨头依靠筋皮肉连接一样,当你了解其构造后,你看到的是每一个模块具有什么功能,可以拿来做什么,一个软件可以有那些模块拼凑,彼此间如何通信。恢恢乎其于游刃必有余地矣!

文档视图的剥离(App平台 文档视图插件)
软件开发讲究分工合作,因为要考虑开发周期及市场。一个企业软件有几种业务:三维浏览、二维数据编辑、网络控制。作为企业软件应该有统一的框架,像.NET集成VB.NET、C#、VC.NET一样,这样就需要把框架和文档视图分离,在一个框架上,不同开发部门开发不同应用最后集成到统一应用框架。
首先来看看用向导生成的一个多文档程序,主框架和文档视图子框架的联系其实只有很少代码:

CMultiDocTemplate *  pDocTemplate;
pDocTemplate 
=   new  CMultiDocTemplate(
 IDR_TESTTYPE,
 RUNTIME_CLASS(CTestDoc),
 RUNTIME_CLASS(CChildFrame), 
//  custom MDI child frame
 RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);

我们把这一段代码删除,并且删除关联的CTestDoc、CChildFrame、CTestView类。这时候我们得到一个空框架的应用程序。
很明显框架需要的只是文档视图子框架运行时的类信息,我们的文档视图插件需要遵守这样一个规则,于是我们定义一个插件接口:

interface  IDocView : IUnknown
{
 [id(
1 ), helpstring( " method GetDocCls " ), hidden] HRESULT GetDocCls([ out , retval] long *  pDocCls);
 [id(
2 ), helpstring( " method GetViewCls " ), hidden] HRESULT GetViewCls([ out , retval] long *  pViewCls);
 [id(
3 ), helpstring( " method GetChldFrm " ), hidden] HRESULT GetChldFrm([ out , retval] long *  pChldFrm);

为了插件查找以及管理,需要一个类别,所有支持的插件都属于这个类别:

BEGIN_CATEGORY_MAP(CManager)
 IMPLEMENTED_CATEGORY(CATID_DocViewCategory)
END_CATEGORY_MAP()

下面就可以实现文档视图插件了,生产一个ATL项目,添加文档视图子框架类,添加一个组件Manager,实现插件接口:

STDMETHODIMP CManager::GetDocCls( long *  pDocCls)
{
 
* pDocCls  =  ( long )RUNTIME_CLASS(CMyDocument);
 
return  S_OK;
}


STDMETHODIMP CManager::GetViewCls(
long *  pViewCls)
{
 
* pViewCls  =  ( long )RUNTIME_CLASS(CMyFormView);
 
return  S_OK;
}


STDMETHODIMP CManager::GetChldFrm(
long *  pChldFrm)
{
 
* pChldFrm  =  ( long )RUNTIME_CLASS(CChildFrame);
 
return  S_OK;
}


这样就主框架就可以遍历组件类别下面所有文档视图插件,通过

pDocTemplate  =   new  CMultiDocTemplate(
  IDR_MAINFRAME,
  (CRuntimeClass
* )nDocCls,
  (CRuntimeClass
* )nChldFrm,
  (CRuntimeClass
* )nViewCls);
AddDocTemplate(pDocTemplate);

加入到主框架。
这里有一个问题:一个应用可能有20种文档视图,是不是应用一起来就查找所有支持插件并加载呢?从内存消耗和人的习惯思考来看都不应该是这样的。可能一个用户打开应用只编辑一类文档,这时候其他19种文档就不需要加载。这就需要一点技巧来处理这类问题。
简单做法可以这样处理:应用框架加载的时候便利所有插件,这时候不加载。当新建文件的时候查找新建的插件是否已经加载,如果没有加载则先加载。打开文件的时候做类似操作。有如下三个函数完成操作:

//  遍历所有文档插件,不加载
void  EnumAllDocViews();
//  根据插件CLSID加载文档视图插件
CMultiDocTemplate *  AddDocViewByCLSID(CLSID clsid);
//  根据CLSID查找插件是否加载
DocMgrData FindDocByCLSID(CLSID clsid);

插件管理之插件类别:

打开界面:

打开文档:

代码下载。里面有说明文件。

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值