版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
文檔/視圖和單文檔界面
12.1文檔/視圖體系結(jié)構(gòu)基礎(chǔ)
12.2文檔對(duì)象和視圖對(duì)象
12.3文檔的序列化
12.4文檔模板資源
12.5滾動(dòng)視圖習(xí)題
12.1文檔/視圖體系結(jié)構(gòu)基礎(chǔ)12.1.1對(duì)象之間的關(guān)系在由AppWizard生成的SDI文檔/視圖體系結(jié)構(gòu)應(yīng)用程序中,主要涉及到4個(gè)類及其對(duì)象,它們之間的關(guān)系如圖12-1所示。圖12-1SDI文檔/視圖體系結(jié)構(gòu)主框架窗口是應(yīng)用程序的頂層窗口,通常是具有WS_OVERLAPPEDWINDOW樣式的窗口,帶有可縮放邊框、標(biāo)題欄、菜單欄、系統(tǒng)菜單以及最大化、最小化和關(guān)閉按鈕。各種子窗口(包括工具欄、視圖窗口和狀態(tài)欄窗口)占據(jù)主框架窗口的客戶區(qū)域。視圖窗口是主框架窗口的子窗口,其大小根據(jù)框架窗口的大小而變化。應(yīng)用程序的數(shù)據(jù)保存在文檔對(duì)象中,數(shù)據(jù)的可視表示保存在視圖對(duì)象中。用戶通過(guò)視圖對(duì)象窗口操作文檔對(duì)象,改變文檔中的數(shù)據(jù),當(dāng)文檔中的數(shù)據(jù)發(fā)生變化時(shí),要更新視圖,使文檔對(duì)象和視圖對(duì)象保持同步。對(duì)于SDI應(yīng)用程序,主框架窗口類是從CFrameWnd派生來(lái)的,文檔類是從CDoucument派生來(lái)的,而視圖類是從CView或相關(guān)類如CScrollView派生來(lái)的。箭頭表示了數(shù)據(jù)流動(dòng)的方向。應(yīng)用程序?qū)ο筇峁┫⒀h(huán)給框架窗口和視圖窗口,用來(lái)提取消息。視圖對(duì)象將鼠標(biāo)和鍵盤輸入轉(zhuǎn)換為處理保存在文檔中的數(shù)據(jù)的命令,文檔對(duì)象提供了視圖所需要的用來(lái)輸出的數(shù)據(jù)。文檔/視圖體系結(jié)構(gòu)通過(guò)將數(shù)據(jù)封裝在獨(dú)立的文檔對(duì)象中并為程序的輸出提供視圖對(duì)象,從而增強(qiáng)了模塊化程序設(shè)計(jì)方法。因?yàn)樵赟DI文檔/視圖體系應(yīng)用程序中,主框架窗口的客戶區(qū)完全被視圖遮蓋,因此,一般不獲取框架窗口客戶區(qū)的設(shè)備環(huán)境并在其中繪制輸出,而是繪制輸出到視圖中。文檔/視圖體系結(jié)構(gòu)并沒(méi)有完全要求所有數(shù)據(jù)都屬于文檔對(duì)象,視圖對(duì)象中也可以有自己的數(shù)據(jù)。按照文檔/視圖體系結(jié)構(gòu)的一般處理方法,視圖對(duì)象中不定義數(shù)據(jù),在需要時(shí)從文檔對(duì)象中獲取。但這種方法并不總是方便和高效的,例如,在文本編輯程序中,往往在視圖對(duì)象中緩存部分?jǐn)?shù)據(jù),以避免對(duì)文檔對(duì)象的頻繁訪問(wèn),提高程序運(yùn)行效率。12.1.2對(duì)象的創(chuàng)建由7.3.2小節(jié)已經(jīng)知道,MFC應(yīng)用程序中有且只有一個(gè)代表應(yīng)用程序自身的應(yīng)用程序?qū)ο?,它被說(shuō)明為全局的對(duì)象,在進(jìn)入主函數(shù)之前就已經(jīng)構(gòu)造完成。因此,程序運(yùn)行進(jìn)入主函數(shù)后,就可以在由MFC定義的主函數(shù)AfxWinMain中獲取該對(duì)象的指針,通過(guò)應(yīng)用程序?qū)ο笾羔樥{(diào)用應(yīng)用程序?qū)ο蟮某蓡T函數(shù)。由AppWizard生成的SDI應(yīng)用程序框架(假定設(shè)置的項(xiàng)目名為MySDI)的應(yīng)用程序類中只是重載了基類CWinApp的InitInstance函數(shù),它被主函數(shù)AfxWinMain調(diào)用,用來(lái)對(duì)應(yīng)用程序進(jìn)行初始化(見(jiàn)7.3.2小節(jié)中AfxWinMain的定義)。在InitInstance函數(shù)中,語(yǔ)句
CSingleDocTemplate*pDocTemplate; pDocTemplate=newCSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CMySDIDoc), RUNTIME_CLASS(CMainFrame),
//SDI的主框架窗口
RUNTIME_CLASS(CMySDIView));由MFC的CSingleDocTemplate類創(chuàng)建了一個(gè)SDI文檔模板。SDI文檔模板是SDI文檔/視圖應(yīng)用程序的最主要部分。它表示了用來(lái)管理應(yīng)用程序數(shù)據(jù)的文檔類、包含數(shù)據(jù)輸出視圖的框架窗口類以及用來(lái)繪制可視數(shù)據(jù)表示的視圖類。文檔模板還保存了資源ID,應(yīng)用程序框架用它來(lái)加載菜單、加速鍵以及其它形成應(yīng)用程序用戶界面的資源。AppWizard在它生成的應(yīng)用程序代碼中使用了IDR_MAINFRAME的資源ID。類名括號(hào)外的RUNTIME_CLASS宏對(duì)于所指定的類返回指向CRuntimeClass結(jié)構(gòu)的指針,這就使得應(yīng)用程序框架可以在運(yùn)行時(shí)創(chuàng)建這些類的對(duì)象。這種動(dòng)態(tài)創(chuàng)建機(jī)制是文檔/視圖體系結(jié)構(gòu)的另一個(gè)重要的成分。在文檔模板創(chuàng)建以后,語(yǔ)句
AddDocTemplate(pDocTemplate);將它添加到由應(yīng)用程序?qū)ο蟊4娴奈臋n模板列表中。用此方法注冊(cè)的每個(gè)文檔模板都定義了一個(gè)應(yīng)用程序支持的文檔類型。SDI應(yīng)用程序只能注冊(cè)一個(gè)文檔類型,而MDI應(yīng)用程序可以注冊(cè)多個(gè)文檔模板。調(diào)用AddDocTemplate和文檔模板的構(gòu)造函數(shù)后,就建立了類之間的相互關(guān)系,這些類包括應(yīng)用程序類、文檔類、視圖窗口類和主框架窗口類。當(dāng)然,在構(gòu)造模板之前,應(yīng)用程序?qū)ο笫谴嬖诘模?,此時(shí)卻沒(méi)有構(gòu)造文檔、視圖和框架對(duì)象。在以后需要的時(shí)候,應(yīng)用程序框架會(huì)動(dòng)態(tài)創(chuàng)建這些對(duì)象。SDI應(yīng)用程序框架中,5個(gè)類之間的關(guān)系如圖12-2所示。圖12-2SDI應(yīng)用程序的5個(gè)類之間的關(guān)系
Windows應(yīng)用程序可以在命令行啟動(dòng),啟動(dòng)應(yīng)用程序時(shí),除了程序名,還可以附加一個(gè)或幾個(gè)參數(shù),如運(yùn)行Word時(shí)可以指定要打開(kāi)文件的文件名。語(yǔ)句
CCommandLineInfocmdInfo; ParseCommandLine(cmdInfo);調(diào)用CWinApp::ParseCommandLine函數(shù)解析命令行,并用命令行中的參數(shù)來(lái)初始化CCommandLineInfo對(duì)象,其中通常包含應(yīng)用程序操作的文檔文件名。語(yǔ)句
if(!ProcessShellCommand(cmdInfo))returnFALSE;對(duì)命令行參數(shù)進(jìn)行處理。函數(shù)ProcessShellCommand首先調(diào)用CWinApp::OnFileNew,在調(diào)用OnFileNew時(shí)應(yīng)用程序框架創(chuàng)建文檔對(duì)象、主框架窗口對(duì)象和視圖對(duì)象,并建立這些對(duì)象之間的連接(不要將這些對(duì)象連接與通過(guò)調(diào)用AddDocTemplate建立的類關(guān)系混淆)。如果命令行參數(shù)中指定了文件名,則調(diào)用CWinApp::OpenDocument來(lái)加載此文檔。如果初始化成功,則ProcessShellCommand返回TRUE,否則返回FALSE。如果初始化是成功的,則語(yǔ)句
m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow();將在屏幕上顯示應(yīng)用程序的主框架窗口(通過(guò)擴(kuò)展顯示視圖窗口)。在應(yīng)用程序被啟動(dòng),文檔、主框架窗口和視圖被創(chuàng)建后,消息循環(huán)就開(kāi)始工作了,應(yīng)用程序也開(kāi)始檢索和處理消息。12.2文檔對(duì)象和視圖對(duì)象12.2.1文檔對(duì)象在文檔/視圖體系結(jié)構(gòu)中,數(shù)據(jù)被保存在文檔對(duì)象中。文檔對(duì)象是在應(yīng)用程序初始化時(shí)從CDocument的派生類創(chuàng)建的。文檔數(shù)據(jù)通常保存在文檔類的數(shù)據(jù)成員中,可以將數(shù)據(jù)成員聲明為公有成員,使得其它對(duì)象可以直接使用它們,而更嚴(yán)格的封裝應(yīng)該是將文檔數(shù)據(jù)聲明為私有的,并提供可以用來(lái)訪問(wèn)它們的公有成員函數(shù)。基類CDocument提供了對(duì)文檔對(duì)象和與之關(guān)聯(lián)的視圖對(duì)象進(jìn)行操作的成員函數(shù),比較重要和常用的有以下幾種。1.?SetModifiedFlag和IsModified
每次修改文檔數(shù)據(jù)之后都應(yīng)調(diào)用SetModifiedFlag。此函數(shù)在文檔對(duì)象內(nèi)設(shè)置一個(gè)修改標(biāo)志,告訴MFC應(yīng)用程序框架文檔包含未保存的數(shù)據(jù),以確保在關(guān)閉文檔之前框架提示用戶保存文檔。當(dāng)以默認(rèn)參數(shù)TRUE調(diào)用此函數(shù)時(shí)設(shè)置修改標(biāo)志,以FALSE為參數(shù)調(diào)用時(shí)則清除修改標(biāo)志。在應(yīng)用程序中也可以使用IsModified自己來(lái)確定文檔是否被修改過(guò)。如果在最近一次保存后文檔被修改過(guò)(設(shè)置了修改標(biāo)志),則函數(shù)返回非0值。
2.?UpdateAllViews
在MFC應(yīng)用程序中,一個(gè)文檔對(duì)象可以有多個(gè)與之關(guān)聯(lián)的視圖對(duì)象。如果由于某種原因而使文檔數(shù)據(jù)發(fā)生了改變,則必須通知所有與之關(guān)聯(lián)的視圖,以便它們更新顯示的數(shù)據(jù)。函數(shù)UpdateAllViews命令所有與之關(guān)聯(lián)的視圖進(jìn)行更新。實(shí)際上,UpdateAllViews調(diào)用了每個(gè)視圖的OnUpdate函數(shù),其默認(rèn)操作是使視圖無(wú)效而實(shí)現(xiàn)重繪。在支持文檔具有多個(gè)視圖的應(yīng)用程序中,當(dāng)文檔數(shù)據(jù)改變后,可以通過(guò)調(diào)用UpdateAllViews來(lái)使各視圖保持同步。即使是單視圖應(yīng)用程序也可以調(diào)用UpdateAllViews來(lái)刷新基于當(dāng)前保存在文檔中數(shù)據(jù)的視圖。
3.?OnNewDocument
在首次構(gòu)造文檔對(duì)象(在應(yīng)用程序啟動(dòng))之后,或當(dāng)用戶從SDI應(yīng)用程序的“文件”菜單中執(zhí)行了“新建”命令時(shí),應(yīng)用程序框架會(huì)調(diào)用此函數(shù)??梢栽诖撕瘮?shù)中對(duì)文檔數(shù)據(jù)成員進(jìn)行初始化。AppWizard在派生的文檔類中生成一個(gè)重載的OnNewDocument函數(shù)。在重載此函數(shù)時(shí),應(yīng)確保保留對(duì)基類函數(shù)的調(diào)用。在SDI應(yīng)用程序中,文檔、主框架窗口和視圖對(duì)象都只創(chuàng)建一次,并在程序的整個(gè)運(yùn)行過(guò)程中都存在。因此,文檔類的構(gòu)造函數(shù)也只執(zhí)行一次初始化。如果在新建文檔時(shí)想要對(duì)派生文檔類包含的數(shù)據(jù)成員進(jìn)行重新初始化,則應(yīng)在OnNewDocument中進(jìn)行。因此,OnNewDocument是進(jìn)行文檔數(shù)據(jù)成員初始化最好的地方。
4.?OnOpenDocument當(dāng)執(zhí)行“文件”菜單下的“打開(kāi)”命令時(shí),應(yīng)用程序框架調(diào)用此函數(shù)。其缺省實(shí)現(xiàn)是打開(kāi)指定的文件,調(diào)用DeleteContents函數(shù)清空文檔對(duì)象,然后調(diào)用CObject::Serialize讀取文件內(nèi)容并將文檔標(biāo)明為未修改。當(dāng)打開(kāi)一個(gè)文檔時(shí)使用DeleteContents刪除文檔對(duì)象的內(nèi)容比實(shí)際關(guān)閉并銷毀文檔對(duì)象,再重新創(chuàng)建一個(gè)文檔對(duì)象更有效。
5.?DeleteContents
在SDI應(yīng)用程序中,文檔對(duì)象只創(chuàng)建一次,因此,在新文檔被創(chuàng)建或從磁盤文件打開(kāi)現(xiàn)有文檔時(shí),必須以某種方式刪除現(xiàn)存文檔對(duì)象中的內(nèi)容,完成此工作的最好方法是調(diào)用DeleteContents函數(shù)。此函數(shù)由應(yīng)用程序框架調(diào)用來(lái)刪除文檔數(shù)據(jù)而不刪除文檔對(duì)象本身。在對(duì)“文件”→“新建”和“文件”→“打開(kāi)”菜單命令的響應(yīng)中,CDocument的OnNewDocument和OnOpenDocument函數(shù)都調(diào)用DeleteContents函數(shù)。這意味著,在第一次構(gòu)造文檔對(duì)象之后,會(huì)立即調(diào)用該函數(shù)。此函數(shù)的缺省實(shí)現(xiàn)是什么都不做,因此,應(yīng)在派生的文檔類中重載此函數(shù),以采取必要的步驟來(lái)清空文檔類的數(shù)據(jù)成員。在關(guān)閉文檔時(shí),會(huì)再次調(diào)用DeleteContents,因此,可以利用重載的DeleteContents函數(shù)來(lái)釋放分配給文檔的任何資源,還可以執(zhí)行其它必要的清理工作,為重新使用文檔對(duì)象作準(zhǔn)備。除了被應(yīng)用程序框架自動(dòng)調(diào)用外,DeleteContents函數(shù)也可以由程序員調(diào)用來(lái)完成清除文檔的內(nèi)容。例如,可以調(diào)用此函數(shù)來(lái)實(shí)現(xiàn)“Edit”→“ClearAll”或類似命令:
voidCMySDIDoc::OnEditClearAll()
{ DeleteContents(); UpdateAllViews(NULL);
}
voidCMySDIDoc::DeleteContents()
{ //在這里重新初始化文檔對(duì)象的數(shù)據(jù)
}
上述3.4.5中介紹的函數(shù)是CDocument提供的可重載的虛函數(shù),可以在應(yīng)用程序中進(jìn)行重載來(lái)自定義文檔的功能。12.2.2視圖對(duì)象文檔對(duì)象的任務(wù)是管理應(yīng)用程序的數(shù)據(jù),視圖對(duì)象的作用是提供文檔數(shù)據(jù)的可視化表示,以及將用戶的輸入(特別是鼠標(biāo)和鍵盤消息)轉(zhuǎn)換為操作文檔數(shù)據(jù)的命令。這樣,文檔和視圖就被緊緊地聯(lián)系在了一起,信息在它們之間雙向傳遞。
MFC的CView類定義了視圖的基本屬性。MFC還包含一組從CView派生來(lái)的視圖類,用來(lái)給視圖添加功能。例如,CScrollView給視圖窗口添加滾動(dòng)功能。視圖類CView提供了一些文檔—視圖相互作用的成員函數(shù)和用于輸出數(shù)據(jù)的函數(shù)。
1.?GetDocument
一個(gè)文檔可以具有與它關(guān)聯(lián)的多個(gè)視圖,但一個(gè)視圖對(duì)象只有一個(gè)與之關(guān)聯(lián)的文檔對(duì)象。應(yīng)用程序框架在視圖的m_pDocument數(shù)據(jù)成員中保存了指向與之關(guān)聯(lián)的文檔對(duì)象的指針,并將該指針提供給視圖對(duì)象的GetDocument成員函數(shù)使用。視圖對(duì)象通過(guò)GetDocument函數(shù)可以得到與之關(guān)聯(lián)的文檔對(duì)象的指針,利用該指針就可以訪問(wèn)文檔對(duì)象的公有數(shù)據(jù)成員和成員函數(shù)。
當(dāng)利用AppWizard創(chuàng)建SDI應(yīng)用程序MySDI時(shí),生成了視圖類的一個(gè)派生類,并重載了基類的GetDocument函數(shù):
CMySDIDoc*CMySDIView::GetDocument()//non-debugversionisinline
{ ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS
(CMySDIDoc))); return(CMySDIDoc*)m_pDocument;
}該函數(shù)將m_pDocument強(qiáng)制轉(zhuǎn)換為與派生視圖類關(guān)聯(lián)的文檔類型并返回結(jié)果。這種重載就確保了對(duì)文檔對(duì)象訪問(wèn)的安全性,并消除了每次調(diào)用GetDocument時(shí)進(jìn)行外部強(qiáng)制類型轉(zhuǎn)換的必要。
2.虛函數(shù)OnDraw
OnDraw是CView類的一個(gè)純虛函數(shù),被應(yīng)用程序框架調(diào)用,用于繪制文檔的數(shù)據(jù)視圖。在派生視圖類中必須重載此函數(shù)。應(yīng)用程序框架通過(guò)傳遞給此函數(shù)不同的設(shè)備環(huán)境來(lái)完成屏幕顯示、打印和打印預(yù)覽。每次在視圖接收到WM_
PAINT消息時(shí)便調(diào)用此函數(shù)。在文檔/視圖應(yīng)用程序中,WM_PAINT消息由OnPaint處理函數(shù)處理。在CView類中,OnPaint的定義如下:
voidCView::OnPaint()
{ //standardpaintroutine CPaintDCdc(this); OnPrepareDC(&dc); OnDraw(&dc);
}該函數(shù)首先創(chuàng)建CPaintDC對(duì)象,并用指向CPaintDC對(duì)象的指針來(lái)調(diào)用視圖的OnDraw。CPaintDC是指向屏幕設(shè)備環(huán)境的對(duì)象,因此,此時(shí)的輸出是輸出到屏幕的窗口中。在打印文檔時(shí),應(yīng)用程序框架會(huì)通過(guò)傳遞一個(gè)指向打印機(jī)設(shè)備環(huán)境的指針來(lái)調(diào)用相同的OnDraw。由于WindowsGDI是與設(shè)備無(wú)關(guān)的圖形系統(tǒng),如果用戶使用了兩種不同的設(shè)備環(huán)境,那么相同的程序可以在不同的設(shè)備上產(chǎn)生相同的輸出。MFC利用這個(gè)特點(diǎn)使得對(duì)文檔的打印方便了許多。
3.虛函數(shù)OnUpdate
當(dāng)應(yīng)用程序調(diào)用CDocument::UpdateAllViews函數(shù)時(shí)將調(diào)用OnUpdate。當(dāng)然,也可以在派生的視圖類中直接調(diào)用該函數(shù)。該函數(shù)的缺省實(shí)現(xiàn)是調(diào)用Invalidate函數(shù)而使整個(gè)客戶區(qū)無(wú)效,從而導(dǎo)致重繪。在派生視圖類中重載該函數(shù)可以只使視圖的一部分無(wú)效,導(dǎo)致只需重繪視圖中需要更新的部分而不是重繪整個(gè)視圖。
4.虛函數(shù)OnInitialUpdate
在SDI應(yīng)用程序中,視圖與文檔對(duì)象一樣只構(gòu)造一次,然后可以重復(fù)使用。在應(yīng)用程序啟動(dòng),或者創(chuàng)建新文檔(執(zhí)行“文件”→“新建”菜單命令),或者打開(kāi)文檔(執(zhí)行“文件”→“打開(kāi)”命令)時(shí),應(yīng)用程序框架都要調(diào)用此函數(shù)。OnInitialUpdate的缺省實(shí)現(xiàn)只是調(diào)用了OnUpdate函數(shù)。當(dāng)應(yīng)用程序啟動(dòng)時(shí),應(yīng)用程序框架在調(diào)用OnCreate之后立即調(diào)用OnInitialUpdate。OnCreate函數(shù)在整個(gè)程序運(yùn)行中只調(diào)用一次,而OnInitialUpdate可以調(diào)用很多次。因此,可以在派生視圖類中重載此函數(shù)來(lái)初始化視圖對(duì)象。例如,在CScrollView的派生類中,通常在OnInitialUpdate中設(shè)置映射模式和調(diào)用視圖的SetScrollsizes以初始化滾動(dòng)視圖。
5.虛函數(shù)OnPrepareDC
在應(yīng)用程序的視圖類中可以重載此函數(shù)來(lái)設(shè)置設(shè)備環(huán)境的屬性。在屏幕顯示調(diào)用OnDraw之前或?yàn)榇蛴』虼蛴☆A(yù)覽而調(diào)用OnPrint成員函數(shù)之前,應(yīng)用程序框架調(diào)用該函數(shù)(參見(jiàn)本節(jié)中OnPaint的定義)。如果是為屏幕顯示輸出調(diào)用該函數(shù),則這個(gè)函數(shù)的缺省實(shí)現(xiàn)不做任何操作。但是,這個(gè)函數(shù)在派生類中(例如在CScrollView類中)將被重載,以調(diào)整設(shè)備環(huán)境的屬性。在重載此函數(shù)時(shí),應(yīng)該在重載代碼的開(kāi)始調(diào)用基類的實(shí)現(xiàn)。如果在視圖的OnDraw函數(shù)之外繪制輸出,則要自己調(diào)用OnPrepareDC,讓MFC在輸出中考慮映射模式和滾動(dòng)位置的影響。即在屏幕輸出時(shí),只有執(zhí)行OnDraw時(shí),才會(huì)自動(dòng)調(diào)用OnPrepareDC。【例12.1】編寫一個(gè)單文檔界面應(yīng)用程序,它顯示一個(gè)具有4行4列的正方形網(wǎng)格。開(kāi)始時(shí)每個(gè)正方形都是白色,可以用鼠標(biāo)單擊來(lái)改變其顏色。默認(rèn)時(shí),單擊會(huì)將顏色改為紅色??梢詮摹邦伾辈藛沃羞x擇單擊時(shí)設(shè)置的顏色。程序運(yùn)行結(jié)果如圖12-3所示。圖12-3例12.1運(yùn)行結(jié)果程序創(chuàng)建和編程過(guò)程如下:
(1)利用AppWizard創(chuàng)建一個(gè)單文檔界面應(yīng)用程序,項(xiàng)目名設(shè)置為SDISquares。
(2)在文檔類CSDISquaresDoc中添加數(shù)據(jù)成員用于保存應(yīng)用程序的數(shù)據(jù):
protected: COLORREFm_clrCurrentColor; COLORREFm_clrGrid[4][4];
二維數(shù)組m_clrGrid用于保存每個(gè)方格的顏色,數(shù)據(jù)成員m_clrCurrentColor用于保存方格被單擊時(shí)賦給方格的顏色。
(3)在文檔類CSDISquaresDoc的OnNewDocument函數(shù)中對(duì)數(shù)據(jù)成員進(jìn)行初始化:
BOOLCSDISquaresDoc::OnNewDocument()
{ if(!CDocument::OnNewDocument()) returnFALSE; for(inti=0;i<4;i++) for(intj=0;j<4;j++) m_clrGrid[i][j]=RGB(255,255,255); m_clrCurrentColor=RGB(255,0,0); returnTRUE;
}網(wǎng)格中的16個(gè)方格被初始化為白色,當(dāng)前顏色被初始化為紅色。由于SDI應(yīng)用程序中的文檔對(duì)象只構(gòu)造一次并在新建文檔和打開(kāi)文檔時(shí)被反復(fù)使用,因此,對(duì)數(shù)據(jù)成員的初始化應(yīng)在OnNewDocument中而不是在構(gòu)造函數(shù)中進(jìn)行,以確保在新建文檔時(shí)可以被重新初始化。如果在文檔的構(gòu)造函數(shù)中對(duì)它們進(jìn)行初始化,則它們只能在應(yīng)用程序啟動(dòng)時(shí)初始化一次,并且會(huì)在新建文檔時(shí)保留當(dāng)前值。
(4)由于文檔類中的數(shù)據(jù)成員被指定為protected訪問(wèn)權(quán)限,為了將數(shù)據(jù)提供給視圖,必須在文檔類中定義相應(yīng)的公有成員函數(shù)用于獲取數(shù)據(jù)和對(duì)其進(jìn)行設(shè)置,因此,在文檔類中定義了三個(gè)公有成員函數(shù)。可以手工在文檔類的頭文件中定義函數(shù)原型,并在實(shí)現(xiàn)文件中編寫函數(shù)體。也可以用如下方法添加函數(shù)原型和函數(shù)框架,然后在函數(shù)框架內(nèi)添加代碼。單擊項(xiàng)目工作區(qū)窗口的“ClassView”標(biāo)簽,在窗口中右擊CSDISquaresDoc類名,選擇“AddMemberFunction”菜單,在彈出的菜單中填入函數(shù)類型、函數(shù)聲明(包括參數(shù)及其類型)并選擇訪問(wèn)權(quán)限為Public。在CSDISquares類中添加如下三個(gè)函數(shù):
COLORREFCSDISquaresDoc::GetCurrentColor()
{ returnm_clrCurrentColor;
}
COLORREFCSDISquaresDoc::GetSquare(inti,intj)
{ ASSERT(i>=0&&i<=3&&j>=0&&j<=3); returnm_clrGrid[i][j];
}
voidCSDISquaresDoc::SetSquare(inti,intj,COLORREFcolor)
{ ASSERT(i>=0&&i<=3&&j>=0&&j<=3); m_clrGrid[i][j]=color; //設(shè)置方格的顏色
SetModifiedFlag(TRUE); //設(shè)置修改標(biāo)志
UpdateAllViews(NULL); //更新與文檔關(guān)聯(lián)的所有視圖
}
GetCurrentColor、GetSquare和SetSquare作為文檔和視圖之間的橋梁,視圖對(duì)象通過(guò)它們可以訪問(wèn)文檔的保護(hù)成員。
(5)實(shí)現(xiàn)視圖類CSDISquaresView的OnDraw函數(shù),在視圖窗口中繪制網(wǎng)格,并根據(jù)文檔類中保存的方格顏色來(lái)設(shè)置方格的顏色。
voidCSDISquaresView::OnDraw(CDC*pDC)
{ CSDISquaresDoc*pDoc=GetDocument(); ASSERT_VALID(pDoc); pDC->SetMapMode(MM_LOENGLISH); //設(shè)置映射模式
//繪制16個(gè)方格 for(inti=0;i<4;i++){ for(intj=0;j<4;j++){ COLORREFcolor=pDoc->GetSquare(i,j); //獲取方格的顏色
CBrushbrush(color); intx1=(j*100)+50; inty1=(i*-100)-50; intx2=x1+100; inty2=y1-100; CRectrect(x1,y1,x2,y2); pDC->FillRect(rect,&brush); //用指定顏色填充方格
} } //繪制方格周圍的網(wǎng)格線
for(intx=50;x<=450;x+=100){ pDC->MoveTo(x,-50); pDC->LineTo(x,-450); } for(inty=-50;y>=-450;y-=100){ pDC->MoveTo(50,y); pDC->LineTo(450,y); }
}
(6)利用ClassWizard在視圖類CSDISquaresView中添加消息WM_LBUTTONDOWN的處理函數(shù)。
voidCSDISquaresView::OnLButtonDown(UINTnFlags,CPointpoint)
{ CView::OnLButtonDown(nFlags,point); CClientDCdc(this); dc.SetMapMode(MM_LOENGLISH); CPointpos=point; dc.DPtoLP(&pos); //將鼠標(biāo)單擊時(shí)的坐標(biāo)轉(zhuǎn)換為邏輯坐標(biāo)值
//檢查鼠標(biāo)單擊的方格,并將此方格的顏色設(shè)置為文檔中保存的當(dāng)前顏色 if(pos.x>=50&&pos.x<=450&&pos.y<=-50&&pos.y>=-450){ inti=(-pos.y-50)/100; intj=(pos.x-50)/100; CSDISquaresDoc*pDoc=GetDocument(); COLORREFclrCurrentColor=pDoc->GetCurrentColor(); pDoc->SetSquare(i,j,clrCurrentColor); }
}
(7)利用菜單編輯器編輯和添加菜單。在“編輯”菜單下添加“清除所有方格”菜單項(xiàng),用于清除所有方格內(nèi)的顏色,重新設(shè)置為白色。添加“顏色”菜單,用于設(shè)置單擊方格時(shí)的顏色。編輯的菜單如圖12-4所示。各菜單項(xiàng)的屬性設(shè)置如表12-1所示?!熬庉嫛辈藛巍邦伾藛巍眻D12-4添加的菜單和菜單項(xiàng)表12-1菜單項(xiàng)的屬性設(shè)置ID標(biāo)題(Caption)ID_EDIT_CLEARALL清除所有方格ID_COLOR_RED紅色(&R)ID_COLOR_GREEN綠色(&G)ID_COLOR_BLUE藍(lán)色(&B)ID_COLOR_CUSTOM自定義…
(8)利用ClassWizard在派生視圖類中重載DeleteContents函數(shù),用于清除所有方格內(nèi)的顏色。
voidCSDISquaresDoc::DeleteContents()
{ for(inti=0;i<4;i++) for(intj=0;j<4;j++)
m_clrGrid[i][j]=RGB(255,255,255); CDocument::DeleteContents();
}
(9)利用ClassWizard為添加的菜單項(xiàng)添加命令消息處理函數(shù)和用戶界面更新命令處理函數(shù)。由于這些函數(shù)都是針對(duì)文檔數(shù)據(jù)的操作,因此將這些菜單命令的消息處理函數(shù)添加在文檔類中。
voidCSDISquaresDoc::OnEditClearall()
{ DeleteContents(); UpdateAllViews(NULL);
}
voidCSDISquaresDoc::OnUpdateEditClearall
(CCmdUI*pCmdUI)
{ BOOLColorFlag=FALSE; for(inti=0;i<4;i++) for(intj=0;j<4;j++) if(m_clrGrid[i][j]!=RGB
(255,255,255)) ColorFlag=ColorFlag||TRUE; pCmdUI->Enable(ColorFlag==TRUE);
}
voidCSDISquaresDoc::OnColorRed()
{ m_clrCurrentColor=RGB(255,0,0);
}
voidCSDISquaresDoc::OnUpdateColorRed(CCmdUI*pCmdUI)
{ pCmdUI->SetRadio(m_clrCurrentColor==RGB(255,0,0));
}
voidCSDISquaresDoc::OnColorGreen()
{ m_clrCurrentColor=RGB(0,255,0);
}
voidCSDISquaresDoc::OnUpdateColorGreen(CCmdUI*pCmdUI)
{ pCmdUI->SetRadio(m_clrCurrentColor==RGB(0,255,0));
}
voidCSDISquaresDoc::OnColorBlue()
{ m_clrCurrentColor=RGB(0,0,255);
}
voidCSDISquaresDoc::OnUpdateColorBlue(CCmdUI*pCmdUI)
{ pCmdUI->SetRadio(m_clrCurrentColor==RGB(0,0,255));
}
voidCSDISquaresDoc::OnColorCustom()
{ CColorDialogclrDlg; clrDlg.DoModal(); m_clrCurrentColor=clrDlg.GetColor();
}
(10)編譯、鏈接和運(yùn)行應(yīng)用程序。測(cè)試各菜單的功能和鼠標(biāo)單擊,同時(shí)測(cè)試新建文檔和關(guān)閉應(yīng)用程序的功能。程序運(yùn)行結(jié)果如圖12-3所示。12.3文檔的序列化一般的應(yīng)用程序都需要將程序中處理的數(shù)據(jù)保存到文件中或從文件中讀取需要處理的數(shù)據(jù)。文件的輸入和輸出服務(wù)是所有操作系統(tǒng)的主要工作。Windows系統(tǒng)提供了各種API函數(shù)用于文件的讀/寫和操作。在MFC中,對(duì)文件的讀/寫可以利用CFile類以及其派生類來(lái)完成。雖然可以直接使用CFile類來(lái)實(shí)現(xiàn)文件的讀/寫,但在大部分的MFC應(yīng)用程序中并不直接使用CFile對(duì)象,而是使用序列化的方法將應(yīng)用程序的數(shù)據(jù)保存在磁盤上或從磁盤上讀取需要的數(shù)據(jù)。
12.3.1序列化序列化(Serialization)也稱為串行化,是向永久的存儲(chǔ)介質(zhì)(比如磁盤)中寫入對(duì)象或從存儲(chǔ)介質(zhì)中載入對(duì)象的過(guò)程。其基本思想是:對(duì)象是連續(xù)的,一個(gè)對(duì)象應(yīng)該能夠?qū)⒆陨淼臓顟B(tài)(通常由數(shù)據(jù)成員的值代表)保存到磁盤上,然后對(duì)象隨時(shí)可以通過(guò)從磁盤中讀取對(duì)象的狀態(tài)數(shù)據(jù)來(lái)再次創(chuàng)建并恢復(fù)該對(duì)象。序列化是MFC編程中的一個(gè)重要概念,因?yàn)樵谖臋n/視圖體系結(jié)構(gòu)應(yīng)用程序中,打開(kāi)并保存文檔是MFC的基本功能。MFC在CObject類中提供了對(duì)序列化的支持,所有從CObject類派生或間接派生而來(lái)的類都帶有序列化的功能,如CDocument類。這些類都從CObject類繼承了一個(gè)Serialize函數(shù),對(duì)象的序列化就通過(guò)這個(gè)函數(shù)進(jìn)行。由于不同對(duì)象的狀態(tài)不同,為了將自身的狀態(tài)寫入磁盤或從磁盤讀取狀態(tài)值,這些派生類應(yīng)該重載Serialize函數(shù),使對(duì)象支持對(duì)自身特定數(shù)據(jù)的序列化。對(duì)于MFC類庫(kù)來(lái)說(shuō),磁盤文件是通過(guò)CFile類的對(duì)象來(lái)表示的,CFile對(duì)象封裝了二進(jìn)制文件句柄。如果應(yīng)用程序沒(méi)有進(jìn)行直接的磁盤文件輸入/輸出,而是通過(guò)了序列化過(guò)程,就可以不直接使用CFile對(duì)象。在Serialize函數(shù)和CFile對(duì)象之間,有一個(gè)CArchive類的對(duì)象,如圖12-5所示。圖12-5序列化過(guò)程
CArchive對(duì)象為CFile對(duì)象緩沖數(shù)據(jù)并保持一個(gè)內(nèi)部標(biāo)志,該標(biāo)志用來(lái)指明CArchive對(duì)象是要存儲(chǔ)(寫到磁盤)還是要加載(從磁盤讀取)。在任何時(shí)刻,只能有一個(gè)活動(dòng)的CArchive對(duì)象與文件相連。應(yīng)用程序框架負(fù)責(zé)CFile和CArchive對(duì)象的構(gòu)造,為CFile對(duì)象打開(kāi)相應(yīng)的磁盤文件,并將CArchive對(duì)象與CFile對(duì)象關(guān)聯(lián)。我們要做的是在Serialize函數(shù)中將數(shù)據(jù)存到CArchive對(duì)象中,或從CArchive對(duì)象中取出。在執(zhí)行“文件”→“保存”和“文件”→“打開(kāi)”菜單命令時(shí),應(yīng)用程序框架調(diào)用文檔的Serialize函數(shù)。
Serialize函數(shù)使用CArchive的引用作為參數(shù)。CArchive::IsStoring成員函數(shù)用來(lái)檢索CArchive對(duì)象的內(nèi)部標(biāo)志,如果CArchive對(duì)象被用于存儲(chǔ)數(shù)據(jù),則返回非0值,如果返回0,則CArchive對(duì)象是用于讀取數(shù)據(jù)。在Serialize函數(shù)中,可以使用CArchive類中重載的插入運(yùn)算符(<<)和提取運(yùn)算符(>>)執(zhí)行數(shù)據(jù)的讀/寫操作。插入和提取運(yùn)算符支持對(duì)基本數(shù)據(jù)類型直接進(jìn)行讀/寫操作,基本數(shù)據(jù)類型包括BYTE、WORD、DWORD、long、float、double、int、unsignedint、short和char。除了可以直接支持這些基本數(shù)據(jù)類型外,MFC還重載了<<和>>運(yùn)算符,以便直接序列化像CString等非基本數(shù)據(jù)類型,這些非基本數(shù)據(jù)類型包括CString、CPoint、CSize、CTime、CTimeSpan、CRect、COleCurrency、COleVariant、COleDateTime和COleDateTimeSpan。另外,類型為SIZE、POINT和RECT的結(jié)構(gòu)也可以直接序列化?!纠?2.2】為例12.1的應(yīng)用程序SDISquares添加序列化功能,將各方格的顏色以及當(dāng)前顏色存入文件,并允許從文件中讀取。打開(kāi)CSDISquaresDoc類的Serialize函數(shù),并添加如下黑體的代碼:
voidCSDISquaresDoc::Serialize(CArchive&ar)
{ if(ar.IsStoring()) { for(inti=0;i<4;i++) for(intj=0;j<4;j++) ar<<m_clrGrid[i][j]; ar<<m_clrCurrentColor; } else { for(inti=0;i<4;i++) for(intj=0;j<4;j++) ar>>m_clrGrid[i][j]; ar>>m_clrCurrentColor; }
}12.3.3編寫可序列化類在MFC中,可以創(chuàng)建自己的可序列化的類,使其對(duì)象具有將自身進(jìn)行序列化的能力。編寫一個(gè)具有序列化功能的類,可以按以下5個(gè)步驟進(jìn)行。
1.直接或間接從CObject類派生出新的類
CObject類是MFC類庫(kù)中大多數(shù)類的基類,它定義了序列化的基本協(xié)議和功能。因此如果新類是從CObject類或其派生類派生出來(lái)的,則新類就自動(dòng)獲得了序列化的功能。
2.重載Serialize成員函數(shù)
Serialize成員函數(shù)在CObject類中定義,它負(fù)責(zé)對(duì)對(duì)象的當(dāng)前狀態(tài)(即數(shù)據(jù)成員的值)進(jìn)行實(shí)際的序列化。該函數(shù)有一個(gè)CArchive對(duì)象的引用作為參數(shù),通過(guò)此參數(shù)可進(jìn)行對(duì)象數(shù)據(jù)的讀/寫。CArchive對(duì)象的成員函數(shù)IsStoring用來(lái)指示序列化過(guò)程是存儲(chǔ)(寫數(shù)據(jù))還是加載(讀取數(shù)據(jù)),根據(jù)IsStoring函數(shù)的返回值,可以利用插入運(yùn)算符(<<)將對(duì)象數(shù)據(jù)輸出到CArchive對(duì)象中,或者用提取運(yùn)算符(>>)從CArchive對(duì)象中提取數(shù)據(jù)。例如,如下的類從CObject類派生,它有兩個(gè)數(shù)據(jù)成員,重載Serialize函數(shù)可實(shí)現(xiàn)對(duì)數(shù)據(jù)成員的序列化。
classCPerson:publicCObject
{
public:
DECLARE_SERIAL(CPerson)
CPerson(){};
//缺省構(gòu)造函數(shù),是必需的
CStringm_name;
WORDm_number;
voidSerialize(CArchive&archive); //重載的Serialize函數(shù)
//類的其它聲明
};
voidCPerson::Serialize(CArchive&archive)
{
CObject::Serialize(archive);
//先調(diào)用基類的成員函數(shù)
//對(duì)派生類中新增數(shù)據(jù)成員的序列化
if(archive.IsStoring())
archive<<m_name<<m_number;
else
archive>>m_name>>m_number;
}在重載的Serialize函數(shù)中,應(yīng)先調(diào)用基類的Serialize成員函數(shù)以確保對(duì)從基類繼承來(lái)的成員的序列化。如果要進(jìn)行大量無(wú)類型數(shù)據(jù)的讀/寫,還可以使用CArchive::Read和CArchive::Write成員函數(shù)。在Serialize函數(shù)中,對(duì)于基本數(shù)據(jù)類型的數(shù)據(jù)成員以及CArchive的插入運(yùn)算符和提取運(yùn)算符支持的MFC類的對(duì)象成員的序列化,直接使用<<和>>運(yùn)算符。如果類中包含有從CObject類直接或間接派生出來(lái)的派生類的對(duì)象成員,并且該對(duì)象具有自己的Serialize成員函數(shù),則對(duì)這種對(duì)象成員的序列化應(yīng)直接調(diào)用其Serialize函數(shù)。假設(shè)CPerson類中包含有自定義類(從CObject類派生)CTranscript的對(duì)象成員:
public:
CTranscriptm_transcript;則CPerson類的Serialize函數(shù)應(yīng)為如下形式:
voidCPerson::Serialize(CArchive&archive)
{
CObject::Serialize(archive);
if(archive.IsStoring())
archive<<m_name<<m_number;
else
archive>>m_name>>m_number;
m_transcript.Serialize(archive);
}如果類CPerson中包含有自定義類CTranscript的指針數(shù)據(jù)成員,并且在CPerson類的構(gòu)造函數(shù)或其它地方已經(jīng)構(gòu)造了一個(gè)此指針指向的對(duì)象,則可以通過(guò)指針調(diào)用其Serialize函數(shù)進(jìn)行序列化。但如果沒(méi)有為此指針構(gòu)造指向的對(duì)象,則應(yīng)直接使用<<和>>進(jìn)行序列化。例如,假設(shè)類CPerson中有如下的指針數(shù)據(jù)成員,并且沒(méi)有為此指針構(gòu)造指向的對(duì)象:
public:
CTranscript*m_pTranscript;則CPerson類的Serialize函數(shù)應(yīng)為如下形式:
voidCPerson::Serialize(CArchive&archive)
{
CObject::Serialize(archive);
if(archive.IsStoring())
archive<<m_name<<m_number<<m_pTranscript;
else
archive>>m_name>>m_number>>m_pTranscript;
m_transcript.Serialize(archive);
}
3.在類定義中加入DECLARE_SERIAL宏
DECLARE_SERIAL宏是支持序列化功能的類的定義所必需的,其使用格式為:DECLARE_SERIAL(class_name)它需要實(shí)際類的類名作為參數(shù)。
4.為類定義一個(gè)缺省的構(gòu)造函數(shù)(該函數(shù)沒(méi)有參數(shù))
在序列化載入(從磁盤讀取數(shù)據(jù))過(guò)程中,MFC需要一個(gè)缺省的構(gòu)造函數(shù)重建對(duì)象,并向?qū)ο笾刑钊胨械某蓡T變量的值。缺省構(gòu)造函數(shù)可以聲明為public、protected或private的訪問(wèn)權(quán)限。如果聲明為protected或private的,則此構(gòu)造函數(shù)只能被序列化過(guò)程使用。如果在使用了DECLARE_SERIAL和IMPLEMENT_
SERIAL宏的類中沒(méi)有定義缺省構(gòu)造函數(shù),那么編譯時(shí)在使用IMPLEMENT_SERIAL宏的代碼行上會(huì)出現(xiàn)警告信息“nodefaultconstructoravailable”。
5.在類的實(shí)現(xiàn)中加入IMPLEMENT_SERIAL宏
IMPLEMENT_SERIAL宏的使用格式如下:
IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)其中,參數(shù)“class_name”為類名;“base_class_name”為基類名;參數(shù)“wSchema”是給序列化文檔指定的“版本號(hào)”,用于序列化加載時(shí)標(biāo)識(shí)和處理早期程序版本創(chuàng)建的數(shù)據(jù)。MFC的序列化代碼在從文件中讀入對(duì)象數(shù)據(jù)時(shí)檢測(cè)“版本號(hào)”,如果與當(dāng)前對(duì)象的“版本號(hào)”不符,則產(chǎn)生出錯(cuò)信息,以防止讀入版本號(hào)不正確的數(shù)據(jù)。如果想讓Serialize函數(shù)能夠讀取多個(gè)版本號(hào)的對(duì)象,可以使用VERSIONABLE_SCHEMA作為IMPLEMENT_SERIAL宏的第三個(gè)參數(shù)。在類中使用了DECLARE_SERIAL和IMPLEMENT_SERIAL宏后,不用也不能再使用DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏或DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,因?yàn)榍罢甙撕髢蓪?duì)宏的全部功能?!纠?2.3】編寫一個(gè)SDI應(yīng)用程序,可以用鼠標(biāo)在視圖窗口中畫(huà)線,并能將所畫(huà)的線條保存。程序創(chuàng)建和編程過(guò)程如下:
(1)利用AppWizard創(chuàng)建一個(gè)SDI應(yīng)用程序框架,項(xiàng)目名設(shè)置為DrawLines。
(2)為線段定義一個(gè)新類CLine,在其中完成畫(huà)線并保存線段起點(diǎn)和終點(diǎn)。執(zhí)行“Insert”→“NewClass”菜單命令,彈出“NewClass”對(duì)話框。在“Classtype”欄中選擇“GenericClass”,在“Name”框中輸入類名CLine,單擊“DerivedFrom”下的空白行,輸入類CLine的基類CObject,單擊“OK”按鈕,則自動(dòng)生成類CLine的頭文件Line.h和實(shí)現(xiàn)文件Line.cpp。
(3)編輯類CLine,在其中添加相應(yīng)的數(shù)據(jù)成員和成員函數(shù)。完成的類如下所示:
//Line.h
classCLine:publicCObject
{
private:
CPointm_ptFrom;
CPointm_ptTo;
public: CLine(){} //缺省構(gòu)造函數(shù),序列化時(shí)必需的
CLine(CPointptFrom,CPointptTo); virtual~CLine(){} voidDrawLine(CDC*pDC); //畫(huà)線
voidSerialize(CArchive&ar); //重載的Serialize函數(shù),保存或讀取線段端點(diǎn)
DECLARE_SERIAL(CLine)//類定義中添加的宏
};
//Line.cpp
//類的實(shí)現(xiàn)中添加的宏
IMPLEMENT_SERIAL(CLine,CObject,
VERSIONABLE_SCHEMA)
CLine::CLine(CPointptFrom,CPointptTo)
{ m_ptFrom=ptFrom; m_ptTo=ptTo;
}
voidCLine::DrawLine(CDC*pDC)
{ pDC->MoveTo(m_ptFrom); pDC->LineTo(m_ptTo);
}
voidCLine::Serialize(CArchive&ar)
{ CObject::Serialize(ar);//調(diào)用基類的Serialize函數(shù)
if(ar.IsStoring()) ar<<m_ptFrom<<m_ptTo; else ar>>m_ptFrom>>m_ptTo;
}
(4)在文檔類中添加保存線段的數(shù)組??梢宰约憾xCLine類的數(shù)組用于保存線段。MFC提供了實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的類模板。類CObArray從CObject類派生,支持CObject指針數(shù)組,這些對(duì)象數(shù)組類似C語(yǔ)言的數(shù)組,但在需要時(shí)可以動(dòng)態(tài)增大和縮小。CObArray引入了IMPLEMENT_SERIAL宏,以支持其元素的序列化。對(duì)CObject指針數(shù)組的序列化可以使用運(yùn)算符<<和>>,也可以使用Serialize成員函數(shù)。在文檔類CDrawLinesDoc的頭文件DrawLinesDoc.h中添加如下的數(shù)據(jù)成員和成員函數(shù),并包含定義類CLine的頭文件和使用MFC模板類時(shí)需要的頭文件:
#include"line.h"
#include"afxtempl.h" //使用MFC類模板
classCDrawLinesDoc:publicCDocument
{
public: CLine*GetLine(intnIndex);
//獲取指定序號(hào)CLine對(duì)象的指針
voidAddLine(CPointptFrom,CPointptTo);
//向動(dòng)態(tài)數(shù)組添加CLine對(duì)象指針
intGetLinesNum(); //獲取線段的數(shù)量
protected: //定義存放線段的動(dòng)態(tài)數(shù)組
CTypedPtrArray<CObArray,CLine*>m_LineArray; };數(shù)組類模板的定義如下:
template<classBASE_CLASS,classTYPE>classCTypedPtrArray:publicBASE_CLASS其中,參數(shù)“BASE_CLASS”指定基類,必須是CObArray或CptrArray;參數(shù)“TYPE”指定存儲(chǔ)在基類數(shù)組中元素的類型。本例中,這兩個(gè)參數(shù)分別指定為CObArray和CLine*,表示m_LineArray是CObArray的派生類的數(shù)組對(duì)象,用來(lái)存放CLine對(duì)象的指針。在使用MFC類模板時(shí)必須包含MFC頭文件afxtempl.h。在文檔類的實(shí)現(xiàn)文件中編寫添加的成員函數(shù):
CLine*CDrawLinesDoc::GetLine(intnIndex)
{ if(nIndex<0||nIndex>m_LineArray.GetUpperBound()) //判斷是否越界
returnNULL; returnm_LineArray.GetAt(nIndex); //返回指定序號(hào)的線段
}
voidCDrawLinesDoc::AddLine(CPointptFrom,
CPointptTo)
{ CLine*pLine=newCLine(ptFrom,ptTo); //創(chuàng)建CLine對(duì)象
m_LineArray.Add(pLine); //將該線段添加到動(dòng)態(tài)數(shù)組
SetModifiedFlag(); //設(shè)置文檔修改標(biāo)志
}
intCDrawLinesDoc::intGetLinesNum();
{ returnm_LineArray.GetSize(); //返回線段的數(shù)量
}
(5)當(dāng)在視圖窗口中按下鼠標(biāo)左鍵時(shí)開(kāi)始畫(huà)線,鼠標(biāo)左鍵抬起時(shí)完成線段的繪制,在鼠標(biāo)移動(dòng)的過(guò)程中畫(huà)橡皮筋線。因此需要記錄畫(huà)線的起點(diǎn)和終點(diǎn)并設(shè)置畫(huà)橡皮筋線的跟蹤標(biāo)志。畫(huà)橡皮筋線時(shí),需要將原來(lái)的線條刪除,重新畫(huà)一條從起點(diǎn)到當(dāng)前鼠標(biāo)指針坐標(biāo)的線條,最簡(jiǎn)單的辦法是使用R2_NOT繪圖模式反轉(zhuǎn)線條。在視圖類CDrawLinesView中添加如下的數(shù)據(jù)成員和成員函數(shù):protected:
BOOLm_bTracking;
//跟蹤標(biāo)志,畫(huà)橡皮筋線時(shí)為TRUE
CPointm_ptFrom; //畫(huà)橡皮筋線的起點(diǎn)
CPointm_ptTo; //畫(huà)橡皮筋線的終點(diǎn)
voidInvertLine(CDC*pDC,CPointptFrom,CPointptTo);在視圖類CDrawLinesView的實(shí)現(xiàn)文件中編寫函數(shù)InvertLine:
voidCDrawLinesView::InvertLine(CDC*pDC,CPointptFrom,CPointptTo)
{ //在繪圖模式R2_NOT下畫(huà)橡皮筋線時(shí)反轉(zhuǎn)畫(huà)線的像素
intnOldMode=pDC->SetROP2(R2_NOT);pDC->MoveTo(ptFrom);pDC->LineTo(ptTo);pDC->SetROP2(nOldMode);
}利用ClassWizard在視圖類中添加鼠標(biāo)消息WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP的處理函數(shù)并添加如下黑體代碼:
voidCDrawLinesView::OnLButtonDown(UINTnFlags,CPointpoint)
{
m_ptFrom=point; //畫(huà)線的起點(diǎn)
m_ptTo=point;
//畫(huà)線的終點(diǎn),開(kāi)始時(shí)起點(diǎn)和終點(diǎn)相同
m_bTracking=TRUE; //設(shè)置跟蹤標(biāo)志
}
voidCDrawLinesView::OnMouseMove(UINTnFlags,CPointpoint)
{ //如果跟蹤標(biāo)志為TRUE(即線條為橡皮筋線),則當(dāng)鼠標(biāo)移動(dòng)時(shí)
//擦除原來(lái)的橡皮筋線條,重畫(huà)新的
if(m_bTracking){CClientDCdc(this);InvertLine(&dc,m_ptFrom,m_ptTo);
//刪除原來(lái)的橡皮筋線
InvertLine(&dc,m_ptFrom,point);
//重新繪制到新終點(diǎn)的橡皮筋線
m_ptTo=point; //更新終點(diǎn)坐標(biāo)
}
}
voidCDrawLinesView::OnLButtonUp(UINTnFlags,CPointpoint)
{ //當(dāng)畫(huà)橡皮筋線時(shí)若釋放鼠標(biāo)左鍵,則擦除最后的橡皮筋線而畫(huà)一條最終的線段
if(m_bTracking){
m_bTracking=FALSE;CClientDCdc(this);InvertLine(&dc,m_ptFrom,m_ptTo);
//擦除橡皮筋線
dc.MoveTo(m_ptFrom);dc.LineTo(point); //繪制最終的線段
} CDrawLinesDoc*pDoc=GetDocument(); ASSERT_VALID(pDoc);
//測(cè)試文檔對(duì)象是否運(yùn)行有效
pDoc->AddLine(m_ptFrom,point);
//將線段添加到文檔的線段數(shù)組中
}
(6)為了在改變窗口大小或最小化后重新打開(kāi)窗口,或在被覆蓋后重新顯示時(shí)保留原有的圖形,必須在OnDraw函數(shù)中重新繪制文檔中線段數(shù)組中的線段。
voidCDrawLinesView::OnDraw(CDC*pDC)
{ CDrawLinesDoc*pDoc=GetDocument(); ASSERT_VALID(pDoc); intnIndex=pDoc->GetLinesNum(); //獲取線段數(shù)目
while(nIndex--) pDoc->GetLine(nIndex)->DrawLine(pDC); //畫(huà)線
}
(7)在定義類CLine時(shí)實(shí)現(xiàn)了類的序列化,但只是一條線段的序列化,還必須保存文檔類的數(shù)據(jù)。編寫文檔類CDrawLinesDoc的Serialize函數(shù),完成對(duì)線段數(shù)組的序列化。
voidCDrawLinesDoc::Serialize(CArchive&ar)
{ if(ar.IsStoring()) { m_LineArray.Serialize(ar); } else { m_LineArray.Serialize(ar); }
}
(8)當(dāng)執(zhí)行“文件”→“新建”命令或“文件”→“打開(kāi)”命令時(shí),需要以某種方式清空文檔對(duì)象,因此在文檔類中重載DeleteContents函數(shù)。
voidCDrawLinesDoc::DeleteContents()
{ intnIndex=GetLinesNum(); //獲取數(shù)組中線段的數(shù)目
while(nIndex--) deletem_LineArray.GetAt(nIndex); //刪除線段
m_LineArray.RemoveAll(); //釋放指針數(shù)組
CDocument::DeleteContents();
}
(9)編譯、鏈接和運(yùn)行程序,用鼠標(biāo)在視圖窗口中畫(huà)線并測(cè)試保存、新建、打開(kāi)等命令。程序運(yùn)行結(jié)果如圖12-6所示。圖12-6程序DrawLines的運(yùn)行結(jié)果12.4文檔模板資源在12.1.2小節(jié)已經(jīng)看到SDI應(yīng)用程序初始化時(shí)首先創(chuàng)建了CSingleDocTemplate的文檔模板對(duì)象,并建立了各個(gè)類之間的聯(lián)系。CSingleDocTemplate構(gòu)造函數(shù)有4個(gè)參數(shù),其中第一個(gè)參數(shù)是一個(gè)整型的資源ID值IDR_MAINFRAME,用來(lái)標(biāo)識(shí)應(yīng)用程序中下列4種資源:應(yīng)用程序的圖標(biāo)、應(yīng)用程序的菜單、伴隨菜單的加速鍵表以及文檔字符串。如果AppWizard生成應(yīng)用程序框架時(shí)指定包括工具欄(缺省生成工具欄),則IDR_MAINFRAME還用來(lái)標(biāo)識(shí)此工具欄。在SDI文檔/視圖體系應(yīng)用程序中,應(yīng)用程序框架創(chuàng)建主框架窗口時(shí)是這樣的:先用保存在文檔模板中的運(yùn)行時(shí)創(chuàng)建的類的信息來(lái)創(chuàng)建一個(gè)主框架窗口對(duì)象,然后調(diào)用該對(duì)象的LoadFrame函數(shù)。LoadFrame接收的第一個(gè)參數(shù)是資源ID,用來(lái)標(biāo)識(shí)上面列出的4種資源。應(yīng)用程序框架提供給LoadFrame的資源ID與文檔模板提供的完全相同。LoadFrame創(chuàng)建一個(gè)框架窗口并一次裝載相關(guān)的菜單、加速鍵和圖標(biāo),因此如果想讓操作順利進(jìn)行,就必須給所有的資源分配相同的ID。這就是在AppWizard為文檔/視圖體系應(yīng)用程序生成的資源文件中對(duì)多種不同的資源使用相同的ID的原因。文檔字符串是一個(gè)字符串資源,它由7個(gè)用字符“\n”分隔的子字符串組成。每個(gè)子字符串描述一種框架窗口或文檔類型的特性。在SDI應(yīng)用程序中,按從左到右的順序,各子字符串的含義如下:
■出現(xiàn)在框架窗口標(biāo)題欄中的標(biāo)題。通常是應(yīng)用程序的名稱,例如“DrawLines”。
■分配給新文檔的缺省文檔名,例如“Sheet”。如果這個(gè)字符串被省略,則默認(rèn)值為“Untitled”(無(wú)標(biāo)題)。
■文檔類型名。如果應(yīng)用程序支持兩種及兩種以上的文檔類型,則當(dāng)用戶執(zhí)行“文件”→“新建”菜單命令時(shí),它將與其它文檔類型一起出現(xiàn)在對(duì)話框中。
■文檔類型描述和過(guò)濾器。包含文檔類型的描述以及與該類型文檔匹配的帶通配符的過(guò)濾器,例如“Worksheets(*.xls)”。這個(gè)子字符串顯示在“打開(kāi)”和“另存為”對(duì)話框的“文件類型”列表框中。
■某類型文檔的默認(rèn)文件擴(kuò)展名,例如“.doc”。
■存儲(chǔ)在Windows注冊(cè)表中的文檔類型標(biāo)識(shí),例如“Exdel.Worksheet”。如果應(yīng)用程序調(diào)用CWinApp::RegisterShellFileTypes來(lái)注冊(cè)此文檔類型,則此子字符串就成了以文檔的文件擴(kuò)展名命名的HKEY_CLASSES_ROOT子鍵的默認(rèn)值。
■存儲(chǔ)在注冊(cè)表中的文檔類型名。與前一個(gè)子字符串不同,此子字符串可以包含空格,例如“MicrosoftExcelWorksheet”。對(duì)于上述的7個(gè)子字符串,可以省略其中的個(gè)別子字符串。但在省略子字符串時(shí),分隔符“\n”不能省略,在前一個(gè)子字符串的分隔符“\n”后緊跟一個(gè)“\n”即可。在使用AppWizard創(chuàng)建應(yīng)用程序框架時(shí),在“MFCAppWizard–Step4of6”對(duì)話框(見(jiàn)圖8-5)中單擊“Advanced”按鈕,打開(kāi)“AdvancedOptions”對(duì)話框,如圖12-7所示。在此對(duì)話框中可以指定文檔模板字符串資源中的6個(gè)子字符串。圖12-7設(shè)置文檔字符串的對(duì)話框
AppWizard根據(jù)“AdvancedOptions”對(duì)話框中的設(shè)置在字符串表中生成了ID值為IDR_MAINFRAME的字符串資源。例如,在DrawLines應(yīng)用程序中,文檔字符串為:
DrawLines\n\nDrawLi\n\n\nDrawLines.Document
\nDrawLiDocument當(dāng)應(yīng)用程序開(kāi)始運(yùn)行或新建一個(gè)空文檔時(shí),其框架窗口的標(biāo)題就是“無(wú)標(biāo)題-DrawLines”,由于沒(méi)有指定文檔類型描述和過(guò)濾器子字符串(第4個(gè)子字符串),因此當(dāng)執(zhí)行“文件”→“打開(kāi)”命令時(shí),在“打開(kāi)”對(duì)話框的“文件類型”列表框中顯示“所有文件(*.*)”。在應(yīng)用程序中,如果需要使用文檔字符串中的子字符串,可以使用MFC的CDocTemplate::GetDocString函數(shù)從文檔字符串中檢索各個(gè)子字符串。例如,語(yǔ)句:
CStringstrDefExt;
pDocTemplate->GetDocString(strDefExt,
CDocTemplate::filterExt);將文檔的默認(rèn)文件擴(kuò)展名復(fù)制到名為strDefExt的CString變量中。12.5滾動(dòng)視圖在前面的應(yīng)用程序中,如果繪制的圖形或輸出的文本超出視圖窗口的大小范圍,則Windows會(huì)將超出視圖窗口的那部分裁減掉,因而用戶只能看見(jiàn)視圖窗口內(nèi)的部分。在Windows應(yīng)用程序中,可以在視圖窗口中添加滾動(dòng)條,允許用戶通過(guò)滾動(dòng)視圖窗口來(lái)瀏覽和編輯程序的所有輸出。
MFC類庫(kù)中提供了CScrollView類來(lái)為視圖窗口提供滾動(dòng)的功能。12.5.1建立滾動(dòng)視圖
CScrollView是從CView類派生出來(lái)的,它給CView類添加了基本的滾動(dòng)功能。該類包含WM_VSCROLL和WM_HSCROLL消息的處理程序,使MFC完成響應(yīng)滾動(dòng)條消息所要做的涉及滾動(dòng)窗口的大量工作。它還包含一些成員函數(shù),可以用來(lái)執(zhí)行一些基本任務(wù),如滾動(dòng)到指定位置和檢索當(dāng)前滾動(dòng)到的位置。CScrollView類完全靠自己處理滾動(dòng)任務(wù)。使用CScrollView創(chuàng)建滾動(dòng)視圖比較簡(jiǎn)單,只需要如下幾個(gè)步驟就可以創(chuàng)建一個(gè)具有滾動(dòng)功能的視圖:
(1)使用CScrollView作為應(yīng)用程序視圖類的基類。在使用AppWizard創(chuàng)建應(yīng)用程序框架時(shí),從“MFCAppWizard-Step6of6”對(duì)話框(如圖8-7所示)的“Baseclass”基類列表框中選擇視圖類的基類為CScrollView。
(2)編輯應(yīng)用程序視圖類中重載的OnInitialUpdate,通過(guò)調(diào)用SetScrollSizes來(lái)指定滾動(dòng)視圖的邏輯大小。在AppWizard生成的應(yīng)用程序框架中調(diào)用SetScrollSizes將視圖的邏輯寬度和高度設(shè)置為100個(gè)像素。通??梢詫L動(dòng)視圖的大小設(shè)置成一個(gè)比較大的固定尺寸。如果需要根據(jù)文檔的大小來(lái)設(shè)置滾動(dòng)視圖的大小,則可以調(diào)用應(yīng)用程序中文檔類的一個(gè)成員函數(shù)來(lái)獲取文檔的大小,例如通過(guò)GetMyDocSize(自定義成員函數(shù),用于返回文檔的CSize大小),可以用以下方式根據(jù)文檔大小設(shè)置滾動(dòng)視圖的大?。?/p>
SetScrollSizes(nMapMode,GetDocument->GetMyDocSize());函數(shù)SetScrollSize的原型為:
voidSetScrollSizes(intnMapMode,SIZEsizeTotal,constSIZE&sizePage=sizeDefault,constSIZE&sizeLine=sizeDefault);其中,參數(shù)“nMapMode”用于指定視圖的映射模式,它可以是除MM_ISOTROPIC和MM_ANISOTROPIC外的所有其它映射模式;參數(shù)“sizeTotal”用于指定滾動(dòng)視圖的邏輯尺寸;參數(shù)“sizePage”用于指定單擊滾動(dòng)條的空白區(qū)域時(shí)水平方向和垂直方向上滾動(dòng)的量;參數(shù)“sizeLine”用于指定單擊滾動(dòng)條箭頭
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 反并購(gòu)條款的案例分析-廣發(fā)收購(gòu)中信
- 國(guó)防支出變動(dòng)趨勢(shì)分析及熱點(diǎn)問(wèn)題1
- nste-acs多支血管病變靶血管的判定
- 債務(wù)服務(wù)合同(2篇)
- 公共事業(yè)資產(chǎn)管理合同(2篇)
- 2025年濾波型無(wú)功補(bǔ)償裝置項(xiàng)目合作計(jì)劃書(shū)
- 《職場(chǎng)溝通》電子教案 項(xiàng)目二職場(chǎng)溝通情商培養(yǎng)教案
- 2025年脫硝催化劑項(xiàng)目合作計(jì)劃書(shū)
- 工商局租賃合同
- 深圳廠房租賃合同書(shū)
- 年勞保用品采購(gòu) 投標(biāo)方案(技術(shù)標(biāo) )
- 閱讀042023年中考英語(yǔ)之考前五十天押題五十篇(閱讀寫作)(原卷版)
- 山東各市2022年中考物理試題及答案
- 華為認(rèn)證智能協(xié)作中級(jí)HCIP-CollaborationH11-861考試題及答案
- 2024年中國(guó)紅菜薹市場(chǎng)調(diào)查研究報(bào)告
- 2024年威海市120急救指揮中心招考調(diào)度員高頻500題難、易錯(cuò)點(diǎn)模擬試題附帶答案詳解
- 報(bào)建協(xié)議書(shū)模板
- 山東虛擬電廠商業(yè)模式介紹
- 2024至2030年中國(guó)鈦行業(yè)“十四五”分析及發(fā)展前景預(yù)測(cè)研究分析報(bào)告
- 2024至2030年中國(guó)步進(jìn)式光刻機(jī)市場(chǎng)現(xiàn)狀研究分析與發(fā)展前景預(yù)測(cè)報(bào)告
- 30 《岳陽(yáng)樓記》對(duì)比閱讀-2024-2025中考語(yǔ)文文言文閱讀專項(xiàng)訓(xùn)練(含答案)
評(píng)論
0/150
提交評(píng)論