存在的必是合理的,都值得我們學(xué)習(xí)。學(xué)什么不重要,重要的是有一技之長。
如果你認(rèn)為MFC垃圾請(qǐng)不要繼續(xù)看。
如果你認(rèn)為文檔視圖結(jié)構(gòu)丑陋請(qǐng)不要繼續(xù)看。
如果你認(rèn)為ATL過時(shí)了請(qǐng)不要繼續(xù)看。
庖丁解牛的藝術(shù)
庖丁為文惠君解牛,分解牛體時(shí)手接觸的地方,肩靠著的地方,腳踩踏的地方,膝抵住的地方,都發(fā)出砉砉的聲響,快速進(jìn)刀時(shí)刷刷的聲音,無不像美妙的音樂旋律,符合桑林舞曲的節(jié)奏,又合于經(jīng)首樂曲的樂律。庖丁的解釋是:我所喜好的是摸索事物的規(guī)律,比起一般的技術(shù)、技巧又進(jìn)了一層。我開始分解牛體的時(shí)候,所看見的沒有不是一頭整牛的。幾年之后,就不曾再看到整體的牛了。現(xiàn)在,我只用心神去接觸而不必用眼睛去觀察,眼睛的官能似乎停了下來而精神世界還在不停地運(yùn)行。最喜歡這句:以無厚入有間,恢恢乎其于游刃必有余地矣。
我的MFC經(jīng)歷
我的大學(xué)專業(yè)是軟件工程,作為一個(gè)C++開發(fā)人員學(xué)習(xí)MFC是必然的。記得上大二的時(shí)候,有些課程和大三師哥們一起上,總能看到牛師哥們手里捧一本《深入淺出MFC》,羨慕不已。當(dāng)時(shí)數(shù)據(jù)結(jié)構(gòu)實(shí)習(xí),人家都基于Windows做,而自己只能在Console下做,十分尷尬,覺得自己很落后,現(xiàn)在想來有點(diǎn)可笑。
這里要說的是MFC是對(duì)WIN32 API的封裝,為什么不從WIN32 API開始學(xué)呢?很多人是這樣做,也見過一些人在這上面氣餒而放棄C++轉(zhuǎn)向Java。我是從MFC開始的,當(dāng)我對(duì)MFC比較熟悉的時(shí)候,我心里就明白MFC是如何對(duì)WIN32 API封裝的,進(jìn)入MFC源碼,里面全部調(diào)用的API,自然明白了API的功能及用法。學(xué)習(xí)編程就得由簡到難,對(duì)MFC深入后,WIN32 API自然淺出。
我是大三開始接觸MFC的,當(dāng)時(shí)感覺是MFC是一個(gè)龐然大物,拿一些快速進(jìn)階之類的書籍東拼西湊學(xué)一點(diǎn)技巧,三個(gè)月后也沒有入門。VC6向?qū)傻囊粋€(gè)多文檔程序代碼比起自己寫過的數(shù)據(jù)結(jié)構(gòu)代碼是一個(gè)天文數(shù)字,不知道如何下手。看著旁邊宿舍非計(jì)算機(jī)專業(yè)的學(xué)生用VB就能寫一個(gè)時(shí)鐘,羨慕啊,自卑啊,氣憤之下開始VB的學(xué)習(xí)。然而倔強(qiáng)的心里告訴自己怎么可以這樣認(rèn)輸呢?在一個(gè)星期過去后,我重新開始了MFC的學(xué)習(xí)。MFC向?qū)Э梢陨扇N應(yīng)用工程:對(duì)話框、單文檔、多文檔。默認(rèn)的是微軟最為驕傲的多文檔,但是這個(gè)確實(shí)害人不淺。其他編程開發(fā)環(huán)境沒有文檔視圖的概念,都是窗體、Form,這個(gè)結(jié)構(gòu)要簡單不少,可以快速入門,找到快感,不至于氣餒。我當(dāng)時(shí)就是從對(duì)話框開始學(xué)習(xí)的,向?qū)傻念惐容^簡單,一個(gè)App,一個(gè)Dlg。我開始在對(duì)話框上學(xué)習(xí)各種控件,Button、Edit、ListCtrl等,終于算是Windows編程了。大概用了半年時(shí)間學(xué)習(xí)對(duì)話框,在對(duì)話框編程有一定了解的時(shí)候可以用它來做一些數(shù)據(jù)庫、Socket編程,經(jīng)典例子就是管理系統(tǒng)和聊天工具。對(duì)MFC有一些了解后開始學(xué)習(xí)文檔視圖模型,開始從單文檔入手,然后進(jìn)入到多文檔。文檔視圖主要是開發(fā)企業(yè)應(yīng)用,要考慮菜單、工具欄、??織l、文檔數(shù)據(jù)到視圖更新以及鼠標(biāo)鍵盤交互等,考慮很多消息事件處理,掌握起來確實(shí)要花不少功夫。
大四我進(jìn)入公司實(shí)習(xí),當(dāng)時(shí)以為對(duì)MFC很了解,覺得自己很了不起。當(dāng)遇到稍微深一點(diǎn)的問題,斷點(diǎn)進(jìn)入MFC源碼的時(shí)候便手無所措,原來自己只是對(duì)MFC的輪廓有一點(diǎn)了解,并不解其內(nèi)部規(guī)律。當(dāng)時(shí)受了打擊,這使我靜下心來深入研究MFC,再一次看《深入淺出MFC》、操練各種類型應(yīng)用程序、分類學(xué)習(xí)WIN32 API、看MFC源碼(不敢說很仔細(xì)看,至少比較細(xì)的瀏覽一邊),當(dāng)我可以進(jìn)入到MFC源碼調(diào)試的時(shí)候,我很高興,因?yàn)槲疫M(jìn)步了。當(dāng)我接觸到BCG庫和XTREME庫的時(shí)候,我都要進(jìn)入它的源代碼看,發(fā)現(xiàn)里面很多代碼和MFC源碼一樣,我想寫庫的那些人對(duì)MFC應(yīng)該算是很了解。而后我又花了半年時(shí)間學(xué)習(xí)COM,用ATL做項(xiàng)目。
現(xiàn)在每當(dāng)我拿到一款軟件的時(shí)候,我總要用Spy++去看看它的構(gòu)造,想一下如果是自己能否做出這樣的軟件。軟件設(shè)計(jì)不是靠蠻力,而是靠技巧,懂得借力省力。軟件好比一頭牛,模塊之間的松耦合就像牛骨頭依靠筋皮肉連接一樣,當(dāng)你了解其構(gòu)造后,你看到的是每一個(gè)模塊具有什么功能,可以拿來做什么,一個(gè)軟件可以有那些模塊拼湊,彼此間如何通信。恢恢乎其于游刃必有余地矣!
文檔視圖的剝離(App平臺(tái) 文檔視圖插件)
軟件開發(fā)講究分工合作,因?yàn)橐紤]開發(fā)周期及市場。一個(gè)企業(yè)軟件有幾種業(yè)務(wù):三維瀏覽、二維數(shù)據(jù)編輯、網(wǎng)絡(luò)控制。作為企業(yè)軟件應(yīng)該有統(tǒng)一的框架,像.NET集成VB.NET、C#、VC.NET一樣,這樣就需要把框架和文檔視圖分離,在一個(gè)框架上,不同開發(fā)部門開發(fā)不同應(yīng)用最后集成到統(tǒng)一應(yīng)用框架。
首先來看看用向?qū)傻囊粋€(gè)多文檔程序,主框架和文檔視圖子框架的聯(lián)系其實(shí)只有很少代碼:
CMultiDocTemplate
*
?pDocTemplate;
pDocTemplate?
=
?
new
?CMultiDocTemplate(
?IDR_TESTTYPE,
?RUNTIME_CLASS(CTestDoc),
?RUNTIME_CLASS(CChildFrame),?
//
?custom?MDI?child?frame
?RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
我們把這一段代碼刪除,并且刪除關(guān)聯(lián)的CTestDoc、CChildFrame、CTestView類。這時(shí)候我們得到一個(gè)空框架的應(yīng)用程序。
很明顯框架需要的只是文檔視圖子框架運(yùn)行時(shí)的類信息,我們的文檔視圖插件需要遵守這樣一個(gè)規(guī)則,于是我們定義一個(gè)插件接口:
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);
為了插件查找以及管理,需要一個(gè)類別,所有支持的插件都屬于這個(gè)類別:
BEGIN_CATEGORY_MAP(CManager)
?IMPLEMENTED_CATEGORY(CATID_DocViewCategory)
END_CATEGORY_MAP()
下面就可以實(shí)現(xiàn)文檔視圖插件了,生產(chǎn)一個(gè)ATL項(xiàng)目,添加文檔視圖子框架類,添加一個(gè)組件Manager,實(shí)現(xiàn)插件接口:
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);
加入到主框架。
這里有一個(gè)問題:一個(gè)應(yīng)用可能有20種文檔視圖,是不是應(yīng)用一起來就查找所有支持插件并加載呢?從內(nèi)存消耗和人的習(xí)慣思考來看都不應(yīng)該是這樣的??赡芤粋€(gè)用戶打開應(yīng)用只編輯一類文檔,這時(shí)候其他19種文檔就不需要加載。這就需要一點(diǎn)技巧來處理這類問題。
簡單做法可以這樣處理:應(yīng)用框架加載的時(shí)候便利所有插件,這時(shí)候不加載。當(dāng)新建文件的時(shí)候查找新建的插件是否已經(jīng)加載,如果沒有加載則先加載。打開文件的時(shí)候做類似操作。有如下三個(gè)函數(shù)完成操作:
//
?遍歷所有文檔插件,不加載
void
?EnumAllDocViews();
//
?根據(jù)插件CLSID加載文檔視圖插件
CMultiDocTemplate
*
?AddDocViewByCLSID(CLSID?clsid);
//
?根據(jù)CLSID查找插件是否加載
DocMgrData?FindDocByCLSID(CLSID?clsid);
插件管理之插件類別:
打開界面:
打開文檔:
代碼下載。里面有說明文件。
posted on 2006-07-06 10:06
萬連文 閱讀(2465)
評(píng)論(11) 編輯 收藏 引用 所屬分類:
MFC