第7章 框架窗口、文檔和視圖
7.1框架窗口
7.2文檔及其序列化
7.3視圖及視圖類(lèi)
7.4文檔視圖結構
7.1框架窗口
7.1.1主窗口和文檔窗口
主窗口是應用程序直接放置在桌面上的那個(gè)窗口,每個(gè)應用程序只能有一個(gè)主窗口,主窗口的標題欄上往往顯示應用程序的名稱(chēng)。
文檔窗口對于SDI程序來(lái)說(shuō),它和主窗口是一致的,即主窗口就是文檔窗口;而對于MDI程序,文檔窗口是主窗口的子窗口,如圖。文檔窗口一般都有相應的可見(jiàn)邊框,它的客戶(hù)區是由相應的視圖來(lái)構成的,因此可以說(shuō)視圖是文檔窗口內的子窗口。文檔窗口時(shí)刻跟蹤當前處于活動(dòng)狀態(tài)的視圖的變化,并將用戶(hù)或系統產(chǎn)生的命令消息傳遞給當前活動(dòng)視圖。而主窗口負責管理各個(gè)用戶(hù)交互對象并根據用戶(hù)操作相應地創(chuàng )建或更新文檔窗口及其視圖。
7.1框架窗口
7.1.2窗口風(fēng)格的設置
Ø 窗口風(fēng)格
窗口風(fēng)格通常有一般和擴展兩種形式。這兩種形式的窗口風(fēng)格可在函數CWnd::Create或CWnd::CreateEx參數中指定,CreateEx函數可同時(shí)支持以上兩種風(fēng)格,而CWnd::Create只能指定窗口的一般風(fēng)格。需要說(shuō)明的是,對于控件和對話(huà)框這樣的窗口來(lái)說(shuō),它們的窗口風(fēng)格可直接通過(guò)其屬性對話(huà)框來(lái)設置。
需要說(shuō)明的是,除了上述風(fēng)格外,框架窗口還有以下三個(gè)自己的風(fēng)格。它們都可以在PreCreateWindow重載函數中指定。
u FWS_ADDTOTITLE
該風(fēng)格指定相關(guān)的信息如文檔名添加到框架窗口標題的后面。
u FWS_PREFIXTITLE
該風(fēng)格使得框架窗口標題中的文檔名顯示在應用程序名之前。
u FWS_SNAPTOBARS
該風(fēng)格用來(lái)調整窗口的大小,使它剛好包含了框架窗口中的控制欄。
7.1框架窗口
Ø 用MFC AppWizard設置
7.1框架窗口
Ø 修改CREATESTRUCT結構
窗口創(chuàng )建之前,系統自動(dòng)調用PreCreateWindow虛函數。在用MFC AppWizard創(chuàng )建SDI/MDI應用程序結構時(shí),MFC已為主窗口或文檔窗口類(lèi)自動(dòng)重載了該虛函數??梢栽诖撕瘮抵型ㄟ^(guò)修改CREATESTRUCT結構來(lái)設置窗口的絕大多數風(fēng)格。
例如,在SDI程序中,框架窗口默認的風(fēng)格是WS_OVERLAPPEDWINDOW和FWS_ADDTOTITLE的組合,更改風(fēng)格代碼:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{ // 新窗口不帶有[最大化]按鈕
cs.style &= ~WS_MAXIMIZEBOX;
// 將窗口的大小設為1/3屏幕并居中
cs.cy = ::GetSystemMetrics(SM_CYSCREEN) / 3;
cs.cx = ::GetSystemMetrics(SM_CXSCREEN) / 3;
cs.y = ((cs.cy * 3) - cs.cy) / 2;
cs.x = ((cs.cx * 3) - cs.cx) / 2;
// 調用基類(lèi)的PreCreateWindow函數
return CFrameWnd::PreCreateWindow(cs);
}
對于MDI程序,文檔窗口的風(fēng)格可用下列的代碼更改:
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{ // 創(chuàng )建不含有[最大化]按鈕的子窗口
cs.style &= ~WS_MAXIMIZEBOX;
// 調用基類(lèi)的PreCreateWindow函數
return CMDIChildWnd::PreCreateWindow(cs);
}
7.1框架窗口
Ø 使用ModifyStyle和ModifyStyleEx
CWnd類(lèi)中的成員函數ModifyStyle和ModifyStyleEx也可用來(lái)更改窗口的風(fēng)格,其中ModifyStyleEx還可更改窗口的擴展風(fēng)格。這兩個(gè)函數具有相同的參數,其含義如下。
BOOL ModifyXXXX( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0 );
參數dwRemove用來(lái) 指定需要刪除的風(fēng)格,dwAdd用來(lái)指定需要增加的風(fēng)格,nFlags表示SetWindowPos的標志,0(默認)表示更改風(fēng)格的同時(shí)不調用SetWindowPos函數。
由于框架窗口在創(chuàng )建時(shí)不能直接設定其擴展風(fēng)格,因此只能通過(guò)調用ModifyStyle函數來(lái)進(jìn)行。
7.1框架窗口
[例Ex_Vscroll] 多文檔(MDI)的子文檔窗口增加垂直滾動(dòng)條。
(1)用MFC AppWizard創(chuàng )建一個(gè)多文檔應用程序。
(2)用ClassWizard為子文檔窗口類(lèi)CChildFrame添加OnCreateClient消息處理,并增加下列代碼:
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{ ModifyStyle(0, WS_VSCROLL, 0);
return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
(3)編譯并運行,如圖。
7.1框架窗口
Ø 設置TopMost窗口風(fēng)格
最頂(TopMost)窗口是指那些始終出現在桌面的最前方且不會(huì )被其他窗口覆蓋的窗口。如果要創(chuàng )建這樣的一個(gè)最頂窗口,則在運行時(shí)必須指定WS_EX_TOPMOST擴展窗口風(fēng)格。此風(fēng)格的設置是在CWnd::SetWindowPos函數中進(jìn)行;作為技巧,我們可以先調用CWnd::GetExStyle函數來(lái)確定是否已經(jīng)設置了WS_EX_TOPMOST風(fēng)格。例如下面的代碼:
void SetWindowTopMost(CWnd* pWnd)
{ ASSERT_VALID(pWnd);
pWnd->SetWindowPos((pWnd->GetExStyle()&WS_EX_TOPMOST)?
&wndNoTopMost:&wndTopMost,0,0,0,0,
SWP_NOSIZE | SWP_NOMOVE);
}
函數SetWindowPos具體的使用方法將在下一節中介紹。
7.1框架窗口
7.1.3窗口狀態(tài)的改變
Ø 用ShowWindow改變窗口的顯示狀態(tài)
當一個(gè)新的應用程序創(chuàng )建之后,InitInstance函數總會(huì )調用ShowWindow函數來(lái)顯示窗口
想要改變改變窗口顯示狀態(tài),只需根據上表選擇所需的參數值,調用ShowWindow函數就可達到目的。當然,也可對CWinApp的公有型(public)成員變量m_nCmdShow進(jìn)行賦值,同樣能達到效果。下面的代碼是將窗口的初始狀態(tài)設置為“最小化”:
BOOL CEx_SdiApp::InitInstance()
{ ...
m_nCmdShow = SW_SHOWMINIMIZED;
// 由于CEx_SdiApp類(lèi)繼承了基類(lèi)CWinApp的特性,因此也可在派生類(lèi)
// CEx_SdiApp使用這個(gè)公有型成員變量m_nCmdShow。
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
7.1框架窗口
Ø 用SetWindowPos或MoveWindow改變窗口的大小和位置
u SetWindowPos不僅可以改變窗口的大小、位置,還可以改變所有窗口在堆棧排列的次序(Z次序),這個(gè)次序是根據它們在屏幕出現的先后來(lái)確定的。
u 函數CWnd::MoveWindow也可用來(lái)改變窗口的大小和位置,用戶(hù)必須在MoveWindow函數中指定窗口的大小。
void MoveWindow( int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE );
void MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );
參數x和y表示 窗口新的左上角坐標,nWidth和nHeight表示窗口新的寬度和高度,bRepaint用于指定窗口是否重繪,lpRect表示窗口新的大小和位置。
u 使用上述兩個(gè)函數把主窗口移動(dòng)到屏幕的(100,100)處(代碼添在CEx_SdiApp::InitInstance中[return TRUE]語(yǔ)句之前)。
// 使用SetWindowPos函數的示例
m_pMainWnd->SetWindowPos(NULL,100,100,0,0,SWP_NOSIZE|SWP_NOZORDER);
CRect rcWindow; // 使用MoveWindow函數的示例
m_pMainWnd->GetWindowRect(rcWindow);
m_pMainWnd->MoveWindow(100,100,rcWindow.Width(),rcWindow.Height(),TRUE);
u 改變窗口的大小和位置的CWnd成員函數還不止以上兩個(gè)。例如CenterWindow函數是使窗口居于父窗口中央,就像下面的代碼:
CenterWindow(CWnd::GetDesktopWindow()); // 將窗口置于屏幕中央
AfxGetMainWnd()->CenterWindow(); // 將主框架窗口居中
7.2文檔及其序列化
7.2.1單文檔和多文檔
一個(gè)多文檔應用程序的示例過(guò)程:
(1)用MFC AppWizard創(chuàng )建一個(gè)多文檔應用程序項目Ex_Mdi。由于向導默認創(chuàng )建的就是這種程序類(lèi)型,因此只要在向導的Step 1對話(huà)框中單擊[Finish]按鈕即可創(chuàng )建一個(gè)默認的多文檔應用程序。
(2)打開(kāi)CEx_MdiApp::InitInstance可看出與默認的單文檔應用程序不同的代碼是:
BOOL CEx_MdiApp::InitInstance()
{ …
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_EX_MDITYPE,
RUNTIME_CLASS(CEx_MdiDoc),
RUNTIME_CLASS(CChildFrame), // MDI文檔子窗口
RUNTIME_CLASS(CEx_MdiView));
AddDocTemplate(pDocTemplate);
// 創(chuàng )建MDI主框架窗口
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
…
}
7.2文檔及其序列化
(3)用ClassWizard為該項目添加一個(gè)CDocument派生類(lèi)CAnotherDoc,并在CEx_Mdi- App::InitInstance增加下列代碼:
BOOL CEx_MdiApp::InitInstance()
{ …
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_EX_MDITYPE,
RUNTIME_CLASS(CEx_MdiDoc),
RUNTIME_CLASS(CChildFrame), // MDI文檔子窗口
RUNTIME_CLASS(CEx_MdiView));
AddDocTemplate(pDocTemplate);
CMultiDocTemplate* pAnother;
pAnother = new CMultiDocTemplate(IDR_EX_MDITYPE,
RUNTIME_CLASS(CAnotherDoc),
RUNTIME_CLASS(CChildFrame), // MDI文檔子窗口
RUNTIME_CLASS(CEx_MdiView));
AddDocTemplate(pAnother);
// 創(chuàng )建MDI主框架窗口
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
…
}
7.2文檔及其序列化
(4)在Ex_Mdi.cpp文件的開(kāi)始處,添加包含CAnotherDoc類(lèi)的頭文件:
#include "AnotherDoc.h"
(5)編譯并運行后,如圖,從中選擇一個(gè)文檔類(lèi)型。任何時(shí)候,選擇Ex_Mdi程序的“文件”菜單à“新建”命令或單擊“新建”工具按鈕都將彈出此對話(huà)框。
選定第二個(gè)文檔類(lèi)型后,單擊[確定]。這時(shí)程序出現一個(gè)斷言錯誤,這是因為在視圖類(lèi)中有一個(gè)GetDocument函數,它的代碼如下:
CEx_MdiDoc* CEx_MdiView::GetDocument() // non-debug version is inline
{ ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEx_MdiDoc)));
return (CEx_MdiDoc*)m_pDocument;
}
創(chuàng )建的文檔類(lèi)型是CAnotherDoc時(shí),由于兩種類(lèi)型的文檔共用一個(gè)視圖,調用的GetDocument函數是同一個(gè)函數,因此斷言不成立,程序不能正常運行。
(6)用ClassWizard為該項目添加一個(gè)CView類(lèi)的派生類(lèi)CAnotherView,并將InitInstance第二模板pAnother中的CEx_MdiView改成CAnotherView,然后在該源文件前面添加CAnotherView的頭文件AnotherView.h。
(7)再次運行,上述問(wèn)題得到解決。但是,圖7.4的文檔“新建”對話(huà)框中顯示的文檔類(lèi)型名稱(chēng)應該怎么修改呢?這時(shí)就需要使用文檔的字串資源。
7.2文檔及其序列化
7.2.2文檔的字串資源
通過(guò)對文檔的字串資源修改可以改變文檔窗口標題、文檔類(lèi)型以及通用文件對話(huà)框中的某些內容。例如下面的示例過(guò)程:
(1)打開(kāi)前面的多文檔應用程序項目Ex_Mdi。
(2)打開(kāi)String Table,將IDR_EX_MDITYPE字串資源復制,復制后的ID為IDR_ANOTHERTYPE,修改這兩個(gè)字串資源,結果如下表所示。
如果IDR_ANOTHERTYPE的值不等于130,可在其屬性對話(huà)框中直接給標識賦值,如圖。
(3)將InitInstance第二模板pAnother中的資源號IDR_EX_MDITYPE改成IDR_ANOTHERTYPE。
7.2文檔及其序列化
(4)重新編譯并運行,如圖。測試后還發(fā)現“打開(kāi)”或“保存”對話(huà)框中的“文件類(lèi)型”框中顯示出文檔字串資源定義的類(lèi)型名,如圖。
文檔資源字串的內容也可在文檔應用程序創(chuàng )建時(shí)的Advanced Options對話(huà)框中指定,如圖,圖中數字表示該項的含義與表中對應串號的含義相同。
7.2文檔及其序列化
7.2.3 SDI序列化過(guò)程
Ø 創(chuàng )建空文檔
應用程序類(lèi)的InitInstance函數在調用了AddDocTemplate函數之后,會(huì )通過(guò)CWinApp:: ProcessShellCommand間接調用CWinApp的另一個(gè)非常有用的成員函數OnFileNew,并依次完成下列工作:
(1)構造文檔對象,但并不從磁盤(pán)中讀數據。
(2)構造主框架類(lèi)CMainFrame的對象,并創(chuàng )建該主框架窗口,但不顯示。
(3)構造視圖對象,并創(chuàng )建視圖窗口,也不顯示。
(4)通過(guò)內部機制,使文檔、主框架和視圖“對象”之間“真正”建立聯(lián)系。AddDocTemplate函數建立的是“類(lèi)”之間的聯(lián)系。
(5)調用文檔對象的CDocument::OnNewDocument虛成員函數,并調用CDocument:: DeleteContents(清除文檔對象的內容)虛函數。
(6)調用視圖對象的CView::OnInitialUpdate虛成員函數。
(7)調用框架對象的CFrameWnd::ActiveFrame虛成員函數,以便顯示出具有菜單、工具欄、狀態(tài)欄以及視圖窗口的主框架窗口。
MFC AppWizard為用戶(hù)在文檔類(lèi)中自動(dòng)產(chǎn)生OnNewDocument虛函數的重載,用戶(hù)利用此函數框架可以對文檔對象進(jìn)行初始化。
7.2文檔及其序列化
Ø 打開(kāi)文檔
MFC AppWizard創(chuàng )建應用程序時(shí),它會(huì )自動(dòng)將“文件(File)”菜單中的“打開(kāi)(Open)”命令(ID號為ID_FILE_OPEN)映射到CWinApp的OnFileOpen成員函數。這一結果可以從應用類(lèi)(.cpp)的消息入口處得到驗證:
BEGIN_MESSAGE_MAP(CEx_SdiApp, CWinApp)
……
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
OnFileOpen函數還會(huì )進(jìn)一步完成下列工作:
(1)彈出通用“打開(kāi)”對話(huà)框,供用戶(hù)選擇一個(gè)文件。
(2)調用文檔對象的CDocument:: OnOpenDocument虛成員函數。該函數將打開(kāi)文件,并調用DeleteContents清除文檔對象的內容,然后創(chuàng )建一個(gè)CArchive對象用于數據的讀取,接著(zhù)又自動(dòng)調用Serialize函數。
(3)調用視圖對象的CView::OnInitialUpdate虛成員函數。
7.2文檔及其序列化
MFC為我們重載了Serialize函數,使得我們不必使用CFile類(lèi)就可以完成相應的文檔操作。例如,在文檔類(lèi)中有這樣的代碼:
void CEx_SdiDoc::Serialize(CArchive& ar)
{ if (ar.IsStoring())
{ // TODO: add storing code here }
else
{ // TODO: add loading code here }
}
只需根據ar.IsStoring()結果的“真”與“假”就可決定向文檔寫(xiě)與讀數據。例如,在此判斷體中,可以增加一些代碼來(lái)讀取文檔中的數據:
void CEx_SdiDoc::Serialize(CArchive& ar)
{ if (ar.IsStoring()) {}
else
{ for (int i=0; i<sizeof(m_ch); i++) ar>>m_ch[i];
CString str;
str.Format(_T(“%s”), m_ch);
AfxMessageBox(str);
}
}
7.2文檔及其序列化
Ø 保存文檔
MFC AppWizard創(chuàng )建應用程序時(shí),會(huì )自動(dòng)將“文件(File)”菜單中的“保存(Save)”命令與文檔類(lèi)CDocument的OnFileSave函數在內部關(guān)聯(lián)起來(lái),在程序框架中看不到相應的代碼。OnFileSave函數還會(huì )進(jìn)一步完成下列工作:
(1)彈出通用“保存”對話(huà)框,讓用戶(hù)提供一個(gè)文件名。
(2)調用文檔對象的CDocument::OnSaveDocument虛成員函數,接著(zhù)又自動(dòng)調用Serialize函數,將CArchive對象的內容保存在文檔中。
需要說(shuō)明的是:
u 只有在保存文檔之前還沒(méi)有存過(guò)盤(pán)或讀取的文檔是“只讀”的,OnFileSave函數才會(huì )彈出通用“保存”對話(huà)框。否則,只執行第二步。
u “文件(File)”菜單中還有一個(gè)“另存為(Save As)”命令,它是與文檔類(lèi)CDocument的OnFileSaveAs函數相關(guān)聯(lián)。不管文檔有沒(méi)有保存過(guò),OnFileSaveAs都有會(huì )執行上述兩個(gè)步驟。
可以用ClassWizard來(lái)重載CDocument::OnSaveDocument函數,并可在Serialize函數體的ar.IsStorinr()為“真”的條件語(yǔ)句處添加代碼來(lái)在文檔中保存用戶(hù)自己的數據,
7.2文檔及其序列化
Ø 關(guān)閉文檔
試圖關(guān)閉文檔時(shí),應用程序會(huì )根據文檔是否修改進(jìn)一步完成下列任務(wù):
§ 若文檔內容已被修改,則彈出一個(gè)消息對話(huà)框,詢(xún)問(wèn)用戶(hù)是否需要將文檔保存。若用戶(hù)選擇“ 是” ,則應用程序執行OnFileSave 過(guò)程。
§ 調用CDocument::OnCloseDocument 虛成員函數,關(guān)閉所有與該文檔相關(guān)聯(lián)的文檔窗口及相應的視圖,調用文檔類(lèi)CDocument 的DeleteContents 清除文檔數據。
u MFC應用程序通過(guò)CDocument的數據成員m_bModified的邏輯值來(lái)判斷用戶(hù)是否對文檔進(jìn)行修改,如果m_bModified為“真”,則表示文檔被修改。
u protected類(lèi)型的m_bModified標記是通過(guò)CDocument的SetModifiedFlag和IsModified成員函數被訪(fǎng)問(wèn)的。當文檔創(chuàng )建、從磁盤(pán)中讀出以及文檔存盤(pán)時(shí),文檔的這個(gè)標記就被置為FALSE(假);而當文檔數據被修改時(shí),必須使用SetModifiedFlag函數將該標記置為TRUE。關(guān)閉文檔時(shí),應用程序才會(huì )顯示詢(xún)問(wèn)消息對話(huà)框。
7.2文檔及其序列化
7.2.4使用CFile和CArchive類(lèi)
Ø 使用CFile類(lèi)
(1)文件的打開(kāi)和關(guān)閉
在MFC中,使用CFile打開(kāi)一個(gè)文件通常使用下列兩個(gè)步驟:
u 構造一個(gè)不帶指定任何參數的CFile對象;
u 調用成員函數Open并指定文件路徑以及文件標志。
Open函數的原型如下:
BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );
7.2文檔及其序列化
(2)文件的讀寫(xiě)和定位
CFile類(lèi)支持文件的讀、寫(xiě)和定位操作。它們相關(guān)函數的原型如下:
u UINT Read( void* lpBuf, UINT nCount );
將文件中指定大小的數據讀入指定的緩沖區,返回向緩沖區傳輸的字節數。
u void Write( const void* lpBuf, UINT nCount );
此函數將緩沖區的數據寫(xiě)到文件中。
u LONG Seek( LONG lOff, UINT nFrom );
定位文件指針的位置,若要定位的位置是合法的,返回從文件開(kāi)始的偏移量。否則,返回值是不定的且激活一個(gè)CFileException對象。
u 函數void SeekToBegin( )和DWORD SeekToEnd( )分別將文件指針移動(dòng)到文件開(kāi)始和結尾位置,對于后者還將返回文件的大小。
7.2文檔及其序列化
(3)獲取文件的有關(guān)信息
BOOL GetStatus( CFileStatus& rStatus ) const;
static BOOL PASCAL GetStatus( LPCTSTR lpszFileName, CFileStatus& rStatus );
若指定文件的狀態(tài)信息成功獲得,該函數返回TRUE,否則返回FALSE。 rStatus用來(lái)存放文件狀態(tài)信息,是一個(gè)CFileStatus結構類(lèi)型,該結構具有下列成員:
CTime m_ctime 文件創(chuàng )建日期和時(shí)間
CTime m_mtime 文件最后一次修改日期和時(shí)間
CTime m_atime 文件最后一次訪(fǎng)問(wèn)日期和時(shí)間
LONG m_size 文件的邏輯大小字節數,就像DOS命令中DIR所顯示的大小
BYTE m_attribute 文件屬性
char m_szFullName[_MAX_PATH] 文件名
例如下面的代碼:
CFile theFile;
char* szFileName = "c:\\test\\myfile.dat";
BOOL bOpenOK;
CFileStatus status;
if( CFile::GetStatus( szFileName, status ) )
{ bOpenOK = theFile.Open( szFileName, CFile::modeWrite ); }
else
{ bOpenOK = theFile.Open( szFileName, CFile::modeCreate | CFile::modeWrite ); }
7.2文檔及其序列化
Ø 使用CArchive類(lèi)
(1)CArchive類(lèi)對象的創(chuàng )建和關(guān)閉
創(chuàng )建CArchive對象,有兩種方法:一是通過(guò)框架隱式創(chuàng )建CArchive對象,另一是顯式創(chuàng )建CArchive 對象。
(2)使用“<<”和“>>”運算符
(3)文件文本的讀寫(xiě)
CArchive提供成員函數ReadString和WriteString用來(lái)從一個(gè)文件對象中讀寫(xiě)一行文本,它們的原型如下:
Bool ReadString(CString& rString );
LPTSTR ReadString( LPTSTR lpsz, UINT nMax );
void WriteString( LPCTSTR lpsz );
7.2文檔及其序列化
7.2.5使用簡(jiǎn)單數組集合類(lèi)
Ø 簡(jiǎn)單數組集合類(lèi)的構造及元素的添加
對簡(jiǎn)單數組集合類(lèi)構造的方法都是一樣的,都是使用各自的構造函數,它們的原型如下:
CByteArray CByteArray( );
CDWordArray CDWordArray( );
CPtrArray CPtrArray( );
CStringArray CStringArray( );
CUIntArray CUIntArray( );
CWordArray CWordArray( );
為了有效使用內存,在使用簡(jiǎn)單數組集合類(lèi)之前最好調用成員函數SetSize 設置此數組的大小,與其對應的函數是GetSize,用來(lái)返回數組的大小。它們的原型如下:
void SetSize( int nNewSize, int nGrowBy = -1 );
int GetSize( ) const;
向簡(jiǎn)單數組集合類(lèi)添加一個(gè)元素,可使用成員函數Add和Append,它們的原型如下:
int Add( CObject* newElement );
int Append( const CObArray& src );
7.2文檔及其序列化
Ø 訪(fǎng)問(wèn)簡(jiǎn)單數組集合類(lèi)的元素
在MFC中,一個(gè)簡(jiǎn)單數組集合類(lèi)元素的訪(fǎng)問(wèn)既可以使用GetAt函數,也可使用“[]”操作符,例如:
// CObArray::operator []示例
CObArray array;
CAge* pa; // CAge是一個(gè)用戶(hù)類(lèi)
array.Add( new CAge( 21 ) ); // 添加一個(gè)元素
array.Add( new CAge( 40 ) ); // 再添加一個(gè)元素
pa = (CAge*)array[0]; // 獲取元素0
ASSERT( *pa == CAge( 21 ) );
array[0] = new CAge( 30 ); // 替換元素0;
ASSERT( *(CAge*) array[0] == CAge( 30 ) );
// CObArray::GetAt示例
CObArray array;
array.Add( new CAge( 21 ) ); // 元素 0
array.Add( new CAge( 40 ) ); // 元素 1
ASSERT( *(CAge*) array.GetAt( 0 ) == CAge( 21 ) );
7.2文檔及其序列化
Ø 刪除簡(jiǎn)單數組集合類(lèi)的元素
刪除簡(jiǎn)單數組集合類(lèi)中的元素一般需要進(jìn)行以下幾個(gè)步驟:
(1)使用函數GetSize()和整數下標值訪(fǎng)問(wèn)簡(jiǎn)單數組集合類(lèi)中的元素。
(2)若對象元素是在堆中創(chuàng )建的,則使用delete刪除每一個(gè)對象元素。
(3)調用函數RemoveAll()刪除簡(jiǎn)單數組集合類(lèi)中的所有元素。
例如,下面代碼過(guò)程是一個(gè)CObArray的刪除示例:
CObArray array;
CAge* pa1;
CAge* pa2;
array.Add( pa1 = new CAge( 21 ) );
array.Add( pa2 = new CAge( 40 ) );
ASSERT( array.GetSize() == 2 );
for (int i=0;i<array.GetSize();i++) delete array.GetAt(i);
array.RemoveAll();
函數RemoveAll是刪除數組中的所有元素,而函數RemoveAt( int nIndex, int nCount = 1)則表示要刪除數組中指定元素開(kāi)始的指定數目的元素。
7.3視圖及視圖類(lèi)
7.3.1一般視圖類(lèi)的使用
7.3視圖及視圖類(lèi)
Ø CFormView類(lèi)
u CFormView類(lèi)是一個(gè)非常有用的視圖類(lèi),它具有許多無(wú)模式對話(huà)框的特點(diǎn)。像CDiolog的派生類(lèi)一樣,CFormView的派生類(lèi)也和相應的對話(huà)框資源相聯(lián)系,它也支持對話(huà)框數據交換和對話(huà)框數據確認(DDX和DDV)。
u CFormView是所有表單視(如CRecordView、CDaoRecordView、CHtmlView等)的基類(lèi);一個(gè)基于表單的應用程序能讓用戶(hù)在程序中創(chuàng )建和使用一個(gè)或多個(gè)表單。
u 創(chuàng )建表單應用程序的基本方法除了在創(chuàng )建SDI/MDI的第六步中選擇CFormView作為應用程序視圖類(lèi)的基類(lèi)外。還可通過(guò)相關(guān)菜單命令來(lái)自動(dòng)插入一個(gè)表單,其步驟如下:
(1)切換到ClassView標簽項,在項目名稱(chēng)上右擊鼠標按鈕。從彈出的快捷菜單中選擇“New Form”命令,或者直接在主菜單中選擇“Insert”à“New Form...”菜單命令,彈出如圖7.10的“New Form”對話(huà)框。
(2)在“New Form”對話(huà)框中,鍵入表單名稱(chēng)。如果想要表單支持“自動(dòng)化”特性,則選擇“Automation”單選框。在“Document Template Information”欄中,指定和表單并聯(lián)的文檔內容。如果想要更改文件擴展名或文檔模板字串資源,則可按擊[Change]按鈕。
(3)單擊[OK]按鈕,這樣,一個(gè)表單視圖派生類(lèi)的程序框架就被添加到用戶(hù)程序中;此時(shí),我們就可用對話(huà)框編輯器為表單增加一些控件。
7.3視圖及視圖類(lèi)
Ø CEditView類(lèi)
CEditView類(lèi)對象是一種視圖,提供窗口編輯控制功能,可以執行簡(jiǎn)單文本操作。由于CEditView類(lèi)自動(dòng)封裝上述功能的映射函數,因此只要在文檔模板中使用CEditView類(lèi),那么應用程序的“編輯”菜單和“文件”菜單里的菜單項都可自動(dòng)激活。
但CEditView仍然擺脫不了所有編輯控件的限制,如:
u CEditView不具有所見(jiàn)即所得編輯功能。
u CEditView只能將文本作單一字體的顯示,不支持特殊格式的字符。
u CEditView可容納的文本總數有限,在32位Windows中最多不超過(guò)1M字節。
7.3視圖及視圖類(lèi)
Ø CRichEditView類(lèi)
CRichEditView類(lèi)使用了復文本編輯控件,因此它支持混合字體格式和更大數據量的文本。CRichEditView類(lèi)被設計成與CRichEditDoc和CRichEditCntrItem類(lèi)一起使用,它們可實(shí)現一個(gè)完整的ActiveX包容器應用程序。
Ø CHtmlView 類(lèi)
CHtmlView 類(lèi)是在文檔視圖結構中提供WebBrowser控件的功能。WebBrowser控件可以瀏覽網(wǎng)址,也可以作為本地文件和網(wǎng)絡(luò )文件系統的窗口,它支持超級鏈接、統一資源定位(URL)導航器并維護歷史列表等。
Ø CScrollView類(lèi)
CScrollView類(lèi)不僅能直接支持視圖的滾動(dòng)操作,而且還能管理視口的大小和映射模式,并能響應滾動(dòng)條消息、鍵盤(pán)消息以及鼠標滾輪消息。
滾動(dòng)視圖第一次被創(chuàng )建時(shí),往往在重載的CView::OnInitialUpdate或CView::OnUpdate中調用CScrollView成員函數SetScrollSizes來(lái)設置相關(guān)參數,如映射模式、滾動(dòng)邏輯窗口的大小、水平或垂直方向的滾動(dòng)量等。如果僅需要視圖具有自動(dòng)縮放功能(而不具有滾動(dòng)特性),則用CScrollView::SetScaleToFitSize函數代替SetScrollSizes即可。
7.3視圖及視圖類(lèi)
7.3.2列表控件和列表視圖
Ø 列表控件的風(fēng)格及其修改
7.3視圖及視圖類(lèi)
Ø 列表項的基本操作
u 函數SetImageList用來(lái)為列表控件設置一個(gè)關(guān)聯(lián)的圖像列表,其原型如下:
CImageList* SetImageList( CImageList* pImageList, int nImageList );
u 函數InsertItem用來(lái)向列表控件中插入一個(gè)列表項。該函數成功時(shí)返回新列表項的索引號,否則返回-1。函數原型如下:
int InsertItem( const LVITEM* pItem );
int InsertItem( int nItem, LPCTSTR lpszItem );
int InsertItem( int nItem, LPCTSTR lpszItem, int nImage );
pItem用來(lái)指定一個(gè)指向LVITEM結構的指針,其結構描述如下:
typedef struct _LVITEM
{ UINT mask; // 指明哪些參數有效
int iItem; // 列表項索引
int iSubItem; // 子項索引
UINT state; // 列表項狀態(tài)
UINT stateMask; // 指明state哪些位是有效的,-1全部有效
LPTSTR pszText; // 列表項文本標簽
int cchTextMax; // 文本大小
int iImage; // 在圖像列表中列表項圖標的索引號。
LPARAM lParam; // 32位值
int iIndent; // 項目縮進(jìn)數量,1個(gè)數量等于1個(gè)圖標的像素寬度
} LVITEM, FAR *LPLVITEM;
7.3視圖及視圖類(lèi)
u 函數DeleteItem和DeleteAllItems分別用來(lái)刪除指定的列表項和全部列表項,函數原型如下:
BOOL DeleteItem( int nItem );
BOOL DeleteAllItems( );
u 函數FindItem用來(lái)查尋列表項,函數成功查找時(shí)返回列表項的索引號,否則返回-1。其原型如下:
int FindItem( LVFINDINFO* pFindInfo, int nStart = -1 ) const;
pFindInfo表示要查找的信息,其結構描述如下:
typedef struct tagLVFINDINFO
{ UINT flags; // 查找方式
LPCTSTR psz; // 匹配的文本
LPARAM lParam; // 匹配的值
POINT pt; // 查找開(kāi)始的位置坐標。
UINT vkDirection;// 查找方向,用虛擬方向健值表示。
} LVFINDINFO, FAR* LPFINDINFO;
u 函數Arrange用來(lái)按指定方式重新排列列表項,其原型如下:
BOOL Arrange( UINT nCode );
7.3視圖及視圖類(lèi)
u 函數InsertColumn用來(lái)向列表控件插入新的一列,函數成功調用后返回新的列的索引,否則返回-1。其原型如下:
int InsertColumn( int nCol, const LVCOLUMN* pColumn );
int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT,
int nWidth = -1, int nSubItem = -1 );
pColumn表示包含新列信息的LVCOLUMN結構地址,其結構描述如下:
typedef struct _LVCOLUMN
{ UINT mask; // 指明哪些參數有效
int fmt; // 列的標題或子項文本格式
int cx; // 列的像素寬度
LPTSTR pszText; // 列的標題文本
int cchTextMax; // 列的標題文本大小
int iSubItem; // 和列相關(guān)的子項索引
int iImage; // 圖像列表中的圖像索引
int iOrder; // 列的序號,最左邊的列為0
} LVCOLUMN, FAR *LPLVCOLUMN;
u 函數DeleteColumn用來(lái)從列表控件中刪除一個(gè)指定的列,其原型如下:
BOOL DeleteColumn( int nCol );
u GetItemCount用來(lái)返回列表控件中的列表項個(gè)數等。它們的原型如下:
BOOL SetColumnWidth( int nCol, int cx );
int GetItemCount( );
7.3視圖及視圖類(lèi)
Ø 列表控件的消息
常用的列表控件消息有:
LVN_BEGINDRAG 用戶(hù)按左鼠拖動(dòng)列表列表項
LVN_BEGINLABELEDIT 用戶(hù)對某列表項標簽進(jìn)行編輯
LVN_COLUMNCLICK 某列被按擊
LVN_ENDLABELEDIT 用戶(hù)對某列表項標簽結束編輯
LVN_ITEMACTIVATE 用戶(hù)激活某列表項
LVN_ITEMCHANGED 當前列表項已被改變
LVN_ITEMCHANGING 當前列表項即將改變
LVN_KEYDOWN 某鍵被按下
在用ClassWizard處理上述這些消息時(shí),其消息處理函數參數中往往會(huì )出現NM_LISTVIEW結構,其定義如下:
typedef struct tagNMLISTVIEW
{ NMHDR hdr; // 包含通知消息的結構
int iItem; // 列表項索引,沒(méi)有為-1
int iSubItem; // 子項索引,沒(méi)有為0
UINT uNewState; // 新的項目狀態(tài)
UINT uOldState; // 原來(lái)的項目狀態(tài)
UINT uChanged; // 項目屬性更改標志
POINT ptAction; // 事件發(fā)生的地點(diǎn)
LPARAM lParam; // 用戶(hù)定義的32位值
} NMLISTVIEW, FAR *LPNMLISTVIEW;
7.3視圖及視圖類(lèi)
[例Ex_Lview] 將當前文件夾中的文件用“大圖標”、“小圖標”、“列表視圖”以及“報表視圖”四種不同方式在列表視圖中顯示出來(lái)。當雙擊某個(gè)列表項時(shí),還將該項的文本標簽內容用消息對話(huà)框的形式顯示出來(lái)。
(1)用MFC AppWziard創(chuàng )建一個(gè)單文檔應用程序Ex_List,在創(chuàng )建的第六步將視圖的基類(lèi)選擇為CListView。
(2)為CEx_ListView類(lèi)添加下列成員函數和成員函數:
public:
CImageList m_ImageList;
CImageList m_ImageListSmall;
CStringArray m_strArray;
void SetCtrlStyle(HWND hWnd, DWORD dwNewStyle);
(3)將項目工作區窗口切換到ResourceView頁(yè)面,打開(kāi)Accelerator節點(diǎn)下的IDR_MAINFRAME,為其添加一個(gè)鍵盤(pán)加速鍵Ctrl+Shift+X,其ID號為ID_VIEW_CHANGE。
(4)用ClassWizard為CEx_ListView類(lèi)添加ID_VIEW_CHANGE的COMMAND消息映射函數,并增加下列代碼:
void CEx_ListView::OnViewChange()
{ static int nStyleIndex = 1;
DWORD style[4] = {LVS_REPORT, LVS_ICON, LVS_SMALLICON, LVS_LIST };
CListCtrl& m_ListCtrl = GetListCtrl();
SetCtrlStyle(m_ListCtrl.GetSafeHwnd(), style[nStyleIndex]);
nStyleIndex++;
if (nStyleIndex>3) nStyleIndex = 0;
}
7.3視圖及視圖類(lèi)
(5)用ClassWizard為CEx_ListView類(lèi)添加NM_DBLCLK消息映射函數,增加代碼:
void CEx_ListView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{ LPNMITEMACTIVATE lpItem = (LPNMITEMACTIVATE)pNMHDR;
int nIndex = lpItem->iItem;
if (nIndex >= 0)
{ CListCtrl& m_ListCtrl = GetListCtrl();
CString str = m_ListCtrl.GetItemText(nIndex, 0);
MessageBox(str); }
*pResult = 0;
}
(6)在CEx_ListView::OnInitialUpdate中添加下列代碼:
void CEx_ListView::OnInitialUpdate()
{ CListView::OnInitialUpdate();
// 創(chuàng )建圖像列表
m_ImageList.Create(32,32,ILC_COLOR8|ILC_MASK,1,200);
m_ImageListSmall.Create(16,16,ILC_COLOR8|ILC_MASK,1,200);
CListCtrl& m_ListCtrl = GetListCtrl();
m_ListCtrl.SetImageList(&m_ImageList,LVSIL_NORMAL);
m_ListCtrl.SetImageList(&m_ImageListSmall,LVSIL_SMALL);
LV_COLUMN listCol;
char* arCols[4]={"文件名", "大小", "類(lèi)型", "修改日期"};
listCol.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
7.3視圖及視圖類(lèi)
for (int nCol=0; nCol<4; nCol++)
{ listCol.iSubItem = nCol;
listCol.pszText = arCols[nCol];
if (nCol == 1)
listCol.fmt = LVCFMT_RIGHT;
else
listCol.fmt = LVCFMT_LEFT;
m_ListCtrl.InsertColumn(nCol,&listCol); }
// 查找當前目錄下的文件
CFileFind finder;
BOOL bWorking = finder.FindFile("*.*");
int nItem = 0, nIndex, nImage;
CTime m_time;
CString str, strTypeName;
while (bWorking)
{ bWorking = finder.FindNextFile();
if (finder.IsArchived())
{ str = finder.GetFilePath();
SHFILEINFO fi;
SHGetFileInfo(str,0,&fi,sizeof(SHFILEINFO),
SHGFI_ICON|SHGFI_LARGEICON|SHGFI_TYPENAME);
strTypeName = fi.szTypeName;
nImage = -1;
7.3視圖及視圖類(lèi)
for (int i=0; i<m_strArray.GetSize(); i++)
{ if (m_strArray[i] == strTypeName)
{ nImage = i; break; }
}
if (nImage<0)
{ nImage = m_ImageList.Add(fi.hIcon);
SHGetFileInfo(str,0,&fi,sizeof(SHFILEINFO),
SHGFI_ICON|SHGFI_SMALLICON );
m_ImageListSmall.Add(fi.hIcon);
m_strArray.Add(strTypeName);
}
nIndex = m_ListCtrl.InsertItem(nItem,finder.GetFileName(),nImage);
DWORD dwSize = finder.GetLength();
if (dwSize> 1024) str.Format("%dK", dwSize/1024);
else str.Format("%d", dwSize);
m_ListCtrl.SetItemText(nIndex,1,str);
m_ListCtrl.SetItemText(nIndex,2,strTypeName);
finder.GetLastWriteTime(m_time) ;
m_ListCtrl.SetItemText(nIndex,3,m_time.Format("%Y-%m-%d"));
nItem++;
}
}
7.3視圖及視圖類(lèi)
SetCtrlStyle(m_ListCtrl.GetSafeHwnd(), LVS_REPORT);
m_ListCtrl.SetColumnWidth(0, LVSCW_AUTOSIZE);// 設置列寬
m_ListCtrl.SetColumnWidth(1, 100);
m_ListCtrl.SetColumnWidth(2, LVSCW_AUTOSIZE);
m_ListCtrl.SetColumnWidth(3, 200);
}
(7)編譯并運行,如圖。
7.3視圖及視圖類(lèi)
7.3.3樹(shù)控件和樹(shù)視圖
Ø 樹(shù)形視圖的風(fēng)格
7.3視圖及視圖類(lèi)
Ø 樹(shù)控件的常用操作
u 函數InsertItem用來(lái)向樹(shù)控件插入一個(gè)新項,操作成功后,函數返回新項的句柄,否則返回NULL。函數原型如下:
HTREEITEM InsertItem( UINT nMask, LPCTSTR lpszItem,int nImage, int nSelectedImage,
UINT nState, UINT nStateMask, LPARAM lParam,
HTREEITEM hParent, HTREEITEM hInsertAfter );
HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT,
HTREEITEM hInsertAfter = TVI_LAST );
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage,
HTREEITEM hParent = TVI_ROOT,
HTREEITEM hInsertAfter = TVI_LAST );
u 函數DeleteItem和DeleteAllItems分別用來(lái)刪除指定的項和全部的項。它們的原型如下:
BOOL DeleteAllItems( );
BOOL DeleteItem( HTREEITEM hItem );
u 函數Expand用來(lái)用來(lái)展開(kāi)或收縮指定父項的所有子項,其原型如下:
BOOL Expand( HTREEETEM hItem, UINT nCode );
7.3視圖及視圖類(lèi)
Ø 樹(shù)形視圖控件的通知消息
樹(shù)視圖可以用ClassWizard映射公共控件消息和樹(shù)控件消息。常用的樹(shù)控件消息有:
TVN_BEGINDRAG 開(kāi)始拖放操作
TVN_BEGINLABELEDIT 開(kāi)始編輯標簽
TVN_BEGINRDRAG 鼠標右按鈕開(kāi)始拖放操作
TVN_ENDLABELEDIT 標簽編輯結束
TVN_ITEMEXPANDED 含有子項的父項已展開(kāi)或收縮
TVN_ITEMEXPANDING 含有子項的父項將要展開(kāi)或收縮
TVN_SELCHANGED 當前選擇項發(fā)生改變
TVN_SELCHANGING 當前選擇項將要發(fā)生改變
用ClassWizard處理上述這些消息時(shí),其消息處理函數參數中往往會(huì )出現NM_TREEVIEW結構,定義如下:
typedef struct tagNMTREEVIEW
{ NMHDR hdr; // 含有通知代碼的信息結構
UINT action; // 通知方式標志
TVITEM itemOld; // 原有項的信息
TVITEM itemNew; // 現在項的信息
POINT ptDrag; // 事件產(chǎn)生時(shí),鼠標的位置
} NMTREEVIEW, FAR *LPNMTREEVIEW;
7.3視圖及視圖類(lèi)
[例Ex_Tree] 遍歷本地磁盤(pán)所有的目錄。
(1)用MFC AppWziard創(chuàng )建一個(gè)單文檔應用程序Ex_Tree,在創(chuàng )建的第六步將視圖的基類(lèi)選擇為CTreeView。
(2)按快捷鍵Ctrl+R,選定資源類(lèi)型Icon,單擊Import按鈕,從外部調入六個(gè)圖標,分別表示“我的電腦”、“軟驅”、“硬盤(pán)”、“光驅”、“文件夾”以及“打開(kāi)的文件夾”,相應的ID號設為IDI_MYCOMP、IDI_35FLOPPY、IDI_DRIVE、IDI_CDDRIVE、IDI_CLSDFOLD和IDI_OPENFOLD,如圖。
7.3視圖及視圖類(lèi)
(3)為CEx_TreeView類(lèi)添加下列成員函數和成員函數:
public:
CImageList m_ImageList;
CString m_strPath; // 文件夾路徑
void InsertFoldItem(HTREEITEM m_hItem);
void SetCtrlStyle(HWND hWnd, DWORD dwNewStyle);
InsertFoldItem函數的代碼如下:
void CEx_TreeView::InsertFoldItem(HTREEITEM m_hItem)
{ CFileFind finder;
BOOL bWorking = finder.FindFile(m_strPath);
CString m_str;
while (bWorking)
{ bWorking = finder.FindNextFile();
if (finder.IsDirectory() && !finder.IsHidden())
{ m_str = finder.GetFileName();
m_str.TrimRight();
m_str.TrimLeft();
if ((m_str!=_T("."))&&(m_str!=_T("..")))
{ CTreeCtrl& m_TreeCtrl = GetTreeCtrl();
m_TreeCtrl.InsertItem(m_str,4,5,m_hItem);}
}
}
}
7.3視圖及視圖類(lèi)
(4)為CEx_TreeView類(lèi)添加TVN_ITEMSELECTED消息處理,增加代碼:
void CEx_TreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{ NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
HTREEITEM m_hSelected = pNMTreeView->itemNew.hItem;
CTreeCtrl& m_TreeCtrl = GetTreeCtrl();
if ((!m_TreeCtrl.ItemHasChildren(m_hSelected))&&(m_hSelected)){
BOOL m_bIsTop = FALSE;
CString m_str;
m_strPath.Empty();
HTREEITEM m_hTemp = m_hSelected;
while (!m_bIsTop){
m_str = m_TreeCtrl.GetItemText(m_hTemp);
m_str.TrimLeft();
m_str.TrimRight();
m_strPath = m_str+"\\"+m_strPath;
m_TreeCtrl.GetParentItem(m_hTemp);
if(m_TreeCtrl.GetParentItem(m_TreeCtrl.GetParentItem(m_hTemp))){
m_hTemp = m_TreeCtrl.GetParentItem(m_hTemp);
}else m_bIsTop=TRUE; }
m_strPath += _T("*.");
InsertFoldItem(m_hSelected);
m_TreeCtrl.Expand(m_hSelected,TVE_EXPAND); }
*pResult = 0;
}
7.3視圖及視圖類(lèi)
(5)在CEx_TreeView::OnInitialUpdate函數中添加下列代碼:
void CEx_TreeView::OnInitialUpdate()
{ CTreeView::OnInitialUpdate();
CTreeCtrl& m_TreeCtrl = GetTreeCtrl();
m_ImageList.Create(16, 16, ILC_COLOR|ILC_MASK, 6, 0);
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_MYCOMP));
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_35FLOPPY));
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_DRIVE));
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_CDDRIVE));
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_CLSDFOLD));
m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_OPENFOLD));
m_TreeCtrl.SetImageList(&m_ImageList,TVSIL_NORMAL);
HTREEITEM m_hRootItem, m_hTempItem;
m_hRootItem = m_TreeCtrl.InsertItem("我的電腦",0,0);
// 查找并檢測已有的驅動(dòng)器
DWORD dwDrives = GetLogicalDrives();
UINT uType;
CString m_strDrive, m_strTemp;
7.3視圖及視圖類(lèi)
for( int i = 0; i < 32; i++ )
{ DWORD dwTemp = dwDrives;
if( dwTemp & 1 )
{ m_strTemp.Format(_T("%c:\\."),'A'+(char)i);
uType = GetDriveType(m_strTemp);
m_strTemp.Format(_T(" %c: "),'A'+(char)i);
switch(uType){
case DRIVE_REMOVABLE:
m_TreeCtrl.InsertItem(m_strTemp,1,1,m_hRootItem);
break;
case DRIVE_FIXED:m_hTempItem = m_TreeCtrl.InsertItem(m_strTemp,2,2,m_hRootItem);
break;
case DRIVE_CDROM:
m_TreeCtrl.InsertItem(m_strTemp,3,3,m_hRootItem);
break;
}
}
dwDrives = dwDrives >> 1;
}
SetCtrlStyle(m_TreeCtrl.GetSafeHwnd(),
TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS);
m_TreeCtrl.Expand(m_hRootItem,TVE_EXPAND);
}
7.3視圖及視圖類(lèi)
(6)編譯并運行,如圖。
7.4文檔視圖結構
7.4.1文檔與視圖的相互作用
Ø CView::GetDocument函數
MFC AppWizard產(chǎn)生CView的用戶(hù)派生類(lèi)時(shí),同時(shí)創(chuàng )建一個(gè)安全類(lèi)型的GetDocument函數,返回的是指向派生文檔類(lèi)的指針。該函數是一個(gè)內聯(lián)(inline)函數,類(lèi)似于下面的形式:
CMyDoc* CMyView::GetDocument() // non-debug version is inline
{ ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyDoc)));
// “斷言”m_pDocument指針可以指向的CMyDoc類(lèi)是一個(gè)RUNTIME_CLASS類(lèi)型
return (CMyDoc*)m_pDocument;
}
Ø CDocument::UpdateAllViews函數
UpdateAllViews函數的原型如下。
void UpdateAllViews( CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL );
Ø CView::OnUpdate函數
應用程序調用了CDocument::UpdateAllViews函數時(shí),應用程序框架就會(huì )相應地調用該函數。
virtual void OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint );
7.4文檔視圖結構
Ø CView::OnInitialUpdate函數
應用程序被啟動(dòng)時(shí),或從“文件”菜單中選擇了“新建”或“打開(kāi)”時(shí),CView虛函數都會(huì )被自動(dòng)調用。該函數除了調用無(wú)提示參數(lHint = 0, pHint = NULL)的OnUpdate函數之外,沒(méi)做其他任何事情。
可以重載此函數對文檔所需信息進(jìn)行初始化操作。如果應用程序中的文檔大小是動(dòng)態(tài)的,那么就可在文檔每次改變時(shí)調用OnUpdate來(lái)更新視圖的滾動(dòng)范圍。
Ø CDocument::OnNewDocument函數
在SDI應用程序中,從“文件”菜單中選擇“新建”命令時(shí),框架將先構造一個(gè)文檔對象,然后調用該虛函數。
MFC AppWizard為用戶(hù)的派生文檔類(lèi)自動(dòng)產(chǎn)生了重載的OnNewDocument函數,如下面的代碼:
BOOL CMyDoc::OnNewDocument()
{ if (!CDocument::OnNewDocument()) // 注意一定要保證對基類(lèi)函數的調用,
return FALSE;
// Do initialization of new document here.
return TRUE;
}
7.4文檔視圖結構
7.4.2應用程序對象指針的互調
Ø 從文檔類(lèi)中獲取視圖對象指針
在文檔類(lèi)中有一個(gè)與其關(guān)聯(lián)的各視圖對象的列表,并可通過(guò)CDocument類(lèi)的成員函數GetFirstViewPosition和GetNextView來(lái)定位相應的視圖對象。
GetFirstViewPosition函數用來(lái)獲得與文檔類(lèi)相關(guān)聯(lián)的視圖列表中第一個(gè)可見(jiàn)視圖的位置,GetNextView函數用來(lái)獲取指定視圖位置的視圖類(lèi)指針,并將此視圖位置移動(dòng)下一個(gè)位置,若沒(méi)有下一個(gè)視圖,則視圖位置為NULL。原型如下:
virtual POSITION GetFirstViewPosition( ) const;
virtual CView* GetNextView( POSITION& rPosition ) const;
例如,使用CDocument::GetFirstViewPosition和GetNextView重繪每個(gè)視圖
void CMyDoc::OnRepaintAllViews()
{ POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{ CView* pView = GetNextView(pos);
pView->UpdateWindow();
}
}
// 實(shí)現上述功能也可直接調用UpdateAllViews(NULL);
7.4文檔視圖結構
Ø 從視圖類(lèi)中獲取文檔對象和主框架對象指針
函數CWnd::GetParentFrame可實(shí)現從視圖類(lèi)中獲取主框架指針,原型:
CFrameWnd* GetParentFrame( ) const;
Ø 在主框架類(lèi)中獲取視圖對象指針
CFrameWnd類(lèi)的GetActiveView函數原型如下:
CView* GetActiveView( ) const;
在框架類(lèi)中可直接調用CFrameWnd::GetActiveDocument函數獲得當前活動(dòng)的文檔對象指針。
在同一個(gè)應用程序的任何對象中,可通過(guò)全局函數AfxGetApp()來(lái)獲得指向應用程序對象的指針。
7.4文檔視圖結構
7.4.3切分窗口
Ø 靜態(tài)切分和動(dòng)態(tài)切分
對“靜態(tài)切分”窗口,窗口第一次被創(chuàng )建時(shí),窗格就已經(jīng)被切分好了,窗格的次序和數目不能再被改變,但可以移動(dòng)切分條來(lái)調整窗格的大小。
對“動(dòng)態(tài)切分”窗口,允許在任何時(shí)候對窗口進(jìn)行切分,既可以通過(guò)選擇菜單項來(lái)對窗口進(jìn)行切分,也可以通過(guò)拖動(dòng)滾動(dòng)條中的切分框對窗口進(jìn)行切分。動(dòng)態(tài)切分窗口中的窗格通常使用的是同一個(gè)視圖類(lèi)。切分窗口被創(chuàng )建時(shí),左上窗格通常被初始化成一個(gè)特殊的視圖。當視圖沿著(zhù)某個(gè)方向被切分時(shí),另一個(gè)新增加的視圖對象被動(dòng)態(tài)創(chuàng )建;當視圖沿著(zhù)兩個(gè)方向被切分時(shí),新增加的三個(gè)視圖對象則被動(dòng)態(tài)創(chuàng )建。取消切分時(shí),所有新增加的視圖對象被刪除,但最先的視圖仍被保留,直到切分窗口本身消失。
Ø 切分窗口的CSplitterWnd類(lèi)
成員函數Create和CreateStatic用來(lái)創(chuàng )建“動(dòng)態(tài)切分”和“靜態(tài)切分”的文檔窗口,原型:
BOOL Create( CWnd* pParentWnd, int nMaxRows, int nMaxCols, SIZE sizeMin,
CCreateContext* pContext,
DWORD dwStyle = WS_CHILD | WS_VISIBLE |WS_HSCROLL | WS_VSCROLL | SPLS_DYNAMIC_SPLIT,
UINT nID = AFX_IDW_PANE_FIRST );
BOOL CreateStatic( CWnd* pParentWnd, int nRows, int nCols,
DWORD dwStyle = WS_CHILD | WS_VISIBLE,
UINT nID = AFX_IDW_PANE_FIRST );
7.4文檔視圖結構
[例Ex_ SplitSDI] 將SDI文檔窗口靜態(tài)分成3 x 2個(gè)窗格:
(1)用MFC AppWizard創(chuàng )建一個(gè)單文檔項目Ex_SplitSDI。
(2)打開(kāi)框架窗口類(lèi)MainFrm.h頭文件,為CMainFrame類(lèi)添加一個(gè)保護型的切分窗口的數據成員,如下面的定義:
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
CSplitterWnd m_wndSplitter;
(3)用ClassWizard創(chuàng )建一個(gè)新的視圖類(lèi)CDemoView(基類(lèi)為CView)用于與靜態(tài)切分的窗格相關(guān)聯(lián)。
7.4文檔視圖結構
(5) 為CMainFrame類(lèi)添加OnCreateClient消息函數,增加代碼:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{ CRect rc;
GetClientRect(rc);
CSize paneSize(rc.Width()/2-16,rc.Height()/3-16); m_wndSplitter.CreateStatic(this,3,2); m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CDemoView),
paneSize,pContext); m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CDemoView),
paneSize,pContext);
m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CDemoView),
paneSize,pContext);
m_wndSplitter.CreateView(1,1,RUNTIME_CLASS(CDemoView),
paneSize,pContext);
m_wndSplitter.CreateView(2,0,RUNTIME_CLASS(CDemoView),
paneSize,pContext);
m_wndSplitter.CreateView(2,1,RUNTIME_CLASS(CDemoView),
paneSize,pContext);
return TRUE;
}
7.4文檔視圖結構
(5)在MainFrm.cpp源文件的開(kāi)始處,添加視圖類(lèi)CDemoView的包含文件:
#include "DemoView.h"
(6)編譯并運行,如圖。
7.4文檔視圖結構
值得注意的是:
u 調用CreateStatic函數創(chuàng )建靜態(tài)切分窗口后,必須將每個(gè)窗格用CreateView函數指定相關(guān)聯(lián)的視圖類(lèi)。
u 由于切分功能只應用于文檔窗口,因而在MDI添加該功能時(shí)應在文檔子窗口類(lèi)CChildFrame中進(jìn)行操作。
u 動(dòng)態(tài)切分功能的添加過(guò)程比靜態(tài)切分要簡(jiǎn)單得多,它不需要重新為窗格指定其它視圖類(lèi),因為動(dòng)態(tài)切分窗口的所有窗格共享同一個(gè)視圖。若在文檔窗口添加動(dòng)態(tài)切分的功能可在MFC AppWizard創(chuàng )建SDI(或MDI)應用程序過(guò)程中進(jìn)行設置。在“Step 4”對話(huà)框中單擊[Advanced]按鈕,再單擊Window Styles標簽,然后選擇Use Split Window即可。應用程序編譯運行之后,還在“查看”菜單中包括一個(gè)“分隔”菜單命令,如圖。
7.4文檔視圖結構
7.4.4一檔多視
MFC對于“一檔多視”提供下列三個(gè)模式:
(1)在各自MDI文檔窗口中包含同一個(gè)視圖類(lèi)的多個(gè)視圖對象。
有時(shí),想要應用程序能為同一個(gè)文檔打開(kāi)另一個(gè)文檔窗口,以便同時(shí)使用兩個(gè)文檔窗口來(lái)查看文檔的不同部分內容。用MFC AppWizard創(chuàng )建的MDI應用程序支持這種模式,選擇“窗口”菜單的“新建窗口”命令時(shí),系統就會(huì )為第一個(gè)文檔窗口創(chuàng )建一個(gè)副本。
(2)在同一個(gè)文檔窗口中包含同一個(gè)視圖類(lèi)的多個(gè)視圖對象。
這種模式實(shí)際上是使用“切分窗口”機制使SDI應用程序具有多視的特征。
(3)在單獨一個(gè)文檔窗口中包含不同視圖類(lèi)的多個(gè)視圖對象。
實(shí)驗13的示例(項目名為Ex_Rect)就是在MDI中為同一個(gè)文檔數據提供兩種不同的顯示和編輯方式,如圖。
7.4文檔視圖結構
u 幾個(gè)視圖之間的數據傳輸是通過(guò)CDocument::UpdateAllViews和CView::OnUpdate的相互作用來(lái)實(shí)現的。例如:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{ ...
GetDocument()->UpdateAllViews(NULL, 1, (CObject*)&m_ptDraw);
...
}
和
void CEx_RectView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{ if (lHint == 1)
{ ...
}
}
是一一對應的。
u 在為文檔創(chuàng )建另一個(gè)視圖時(shí),該視圖的CView::OnInitialUpdate將被調用,因此該函數是放置初始化的最好地方。
u 為了能及時(shí)更新并保存文檔數據,相應的數據成員應在用戶(hù)文檔類(lèi)中定義。這樣,由于所有的視圖類(lèi)都可與文檔類(lèi)進(jìn)行交互,因而可以共享這些數據。