MFC"一檔多視"模式
我們知道,MFC應用程序用一種編程模式使程序中數據與它的顯示形式和用戶交互分離開來,這種模式就是"文檔/視圖結構"。在單文檔應用程序結構中,一個文檔對應於一個視圖。但有時一個文檔可能需要多個視圖以改變文檔數據的顯示方式,稱為"一檔多視",MFC對於這種"一檔多視"提供下列三個模式:
第一種模式是用在多文檔應用程序中,用同一個視圖類創建多個視圖對象,並在各自的窗口中顯示。例如,當我們選擇"窗口"菜單的"新建窗口"命令,程序就會打開一個新的窗口顯示出相同的文檔內容。在界面上表現為,一個框架窗口中有多個文檔窗口,如圖1(a)所示。
第二種模式是動態切分窗口方式,即在同一個文檔窗口中創建多個視圖,用同一方式來顯示相同的文檔內容。如圖1(b)所示。
第三種模式是靜態切分窗口方式,即在同一個文檔窗口中創建多個視圖,但每個視圖可用不同的方式來顯示文檔內容。如圖1(c)所示。
(a)
(b)
(c)
圖1 "一檔多視"的三種模式
但在本講中所討論的"一檔多視"是指在單文檔應用程序中具體多個視圖的特性。文檔窗口每次只有一個視圖顯示,但可以通過菜單等命令在多個視圖中進行切換。我們的目是將文檔內容在"普通文本"和"HTML浏覽"視圖之間進行切換,如圖2(a)和2(b)所示。
(a)
(b)
圖2 視圖切換的結果
添加"HTML浏覽"視圖
為了使我們的這個文本浏覽器功能更加強大,我們添加一個"HTML浏覽"視圖用來正確顯示出擴展名為.htm或.html等的Web頁效果。
1. 添加視圖類CWebView
我們先來添加一個視圖類CWebView,具體步驟如下:
(1) 啟動Visual Studio .NET,打開上一講的單文檔應用程序項目Viewer。
(2) 打開"項目"菜單,單擊"添加類",彈出"添加資源"對話框,展開左邊的所有"類別",單擊"MFC",在右側模板中選中"MFC類",如圖3所示。
圖3 "添加類"對話框
(3) 單擊"打開"按鈕,彈出"MFC類向導"對話框。在對話框中輸入"類名"CWebView,然後將基類選擇為CHtmlView,CHtmlView類封裝了URL資源的浏覽和鏈接等功能。其它為默認值,結果如圖4所示。
圖4 使用"MFC類向導"
(4) 單擊"完成"按鈕。
2. 添加代碼並測試CWebView
(1) 打開CWebView類的接口文件WebView.h,在最前面加上CHtmlView類的包含文件" "。
(2) 為CWebView類添加OnInitialUpdate函數的重載,並添加如圖5加框部分的代碼。
圖5 在OnInitialUpdate中添加的代碼
(3) 打開CViewerApp::InitInstance函數,將CSingleDocTemplate中的第4個參數的視圖類由原來的CViewerView改為CWebView。
(4) 在CViewerApp類的實現文件前面加上" "。
(5) 運行程序。圖6是顯示某個文件的結果。
圖6 CWebView類的顯示效果
(6) 再把CViewerApp::InitInstance函數中的CSingleDocTemplate參數CWebView改回到CViewerView。
添加"HTML浏覽"視圖
為了使我們的這個文本浏覽器功能更加強大,我們添加一個"HTML浏覽"視圖用來正確顯示出擴展名為.htm或.html等的Web頁效果。
1. 添加視圖類CWebView
我們先來添加一個視圖類CWebView,具體步驟如下:
(1) 啟動Visual Studio .NET,打開上一講的單文檔應用程序項目Viewer。
(2) 打開"項目"菜單,單擊"添加類",彈出"添加資源"對話框,展開左邊的所有"類別",單擊"MFC",在右側模板中選中"MFC類",如圖3所示。
圖3 "添加類"對話框
(3) 單擊"打開"按鈕,彈出"MFC類向導"對話框。在對話框中輸入"類名"CWebView,然後將基類選擇為CHtmlView,CHtmlView類封裝了URL資源的浏覽和鏈接等功能。其它為默認值,結果如圖4所示。
圖4 使用"MFC類向導"
(4) 單擊"完成"按鈕。
2. 添加代碼並測試CWebView
(1) 打開CWebView類的接口文件WebView.h,在最前面加上CHtmlView類的包含文件" "。
(2) 為CWebView類添加OnInitialUpdate函數的重載,並添加如圖5加框部分的代碼。
圖5 在OnInitialUpdate中添加的代碼
(3) 打開CViewerApp::InitInstance函數,將CSingleDocTemplate中的第4個參數的視圖類由原來的CViewerView改為CWebView。
(4) 在CViewerApp類的實現文件前面加上" "。
(5) 運行程序。圖6是顯示某個文件的結果。
圖6 CWebView類的顯示效果
(6) 再把CViewerApp::InitInstance函數中的CSingleDocTemplate參數CWebView改回到CViewerView。
(4) 在MainFrm.h文件的前面添加下列代碼,如圖10所示的加框部分。
圖10 在MainFrm.h中添加的語句
不少人對上述語句不理解:既然使用了包含文件,為什麼還要在class CMainFrame前添加"class CViewerView;"等代碼?如果用包含文件代替它,行不行?
很多Visual C++書籍對這些問題避而不談,但實際上這是一個重要的問題。如果不能理解上述代碼,我們很可能為無法通過編譯而大傷腦筋。這些問題的出現是基於這樣的一些事實:在我們用標准C/C++設計程序時,有一個原則即兩個代碼文件不能相互包含,而且多次包含還會造成重復定義的錯誤。為了解決這個難題,Visual C++使用#pragma once來通知編譯器在生成時只包含(打開)一次,也就是說,在第一次#include之後,編譯器重新生成時不會再對這些包含文件進行包含(打開)和讀取,因此我們看到在用向導創建的所有類的頭文件中有#pragma once語句就不會覺得奇怪了。然而正是由於這個語句而造成了在第二次#include後編譯器無法正確識別所引用的類。因此,我們在相互包含時還需要加入類似class CViewerView這樣的語句來通知編譯器這個類是一個實際的調用。
(5) 重新生成解決方案後運行程序,看看有沒有出現編譯錯誤,此時提示出SwitchToView1函數中,CViewerView和CWebView類無法構造,因為它們的構造函數是protected。
(6) 分別在ViewerView.h和WebView.h文件中,將構造函數CViewerView()和CWebView()前面的訪問方式改成public。再運行程序。
(7) 為CMainFrame類添加一個int類型的成員變量m_nViewID,並將其初值設為1。
(8) 為CMainFrame類添加菜單項ID_VIEW_TEXT的COMMAND和UPDATE_COMMAND_UI的事件映射,並在映射函數添加如圖11所示的代碼。
圖11 ID_VIEW_TEXT的映射函數代碼
(9) 為CMainFrame類添加菜單項ID_VIEW_HTML的COMMAND和UPDATE_COMMAND_UI的事件映射,並在映射函數添加如圖12所示的代碼。
圖12 ID_VIEW_HTML的映射函數代碼
(10) 運行程序,結果如前面圖2所示。
使用CDocument類的AddView和RemoveView來切換
在CDocument類中,AddView和RemoveView可以說是專門用於單文檔視圖的切換的,具體實現過程如下:
(1) 在CMainFrame類添加兩個變量,一個是CViewerView類指針對象m_pMainView,另一個是CWebView類指針對象m_pWebView。需要說明的是,用"添加成員變量向導"添加指針對象m_pMainView時,指定的類型名應是CViewerView*,注意後面的星號。m_pWebView添加時也類似。
(2) 為CWebView類添加一個CString類成員變量strFileName。
(3) 將CWebView::OnInitialUpdate中的語句" "刪除。
(4) 在CMainFrame類添加成員函數SwitchToView2,用於切換視圖,其代碼如圖13所示。
圖13 SwitchToView2函數代碼
(5) 將CMainFrame::OnViewText和OnViewHtml中的SwitchToView1函數調用改為SwitchToView2,其余不變。
(6) 運行程序,結果如前圖2所示。
兩種切換方法的比較
SwitchToView1切換方法實質上是創建兩個具有與文檔相關聯的視圖,即視圖創建時指定CCreateContext結構,具有很直接的"一檔多視"的關系。而SwitchToView2方法是通過CDocument類的AddView和RemoveView來改變一個文檔與多個視圖的關聯。
由於SwitchToView1中創建的視圖帶有文檔關聯,因此可以直接在視圖類中通過GetDocument()函數來獲取相關聯的文檔指針,從而可以訪問文檔中的數據。而SwitchToView2中創建的視圖本身不帶文檔關聯,因此無法直接訪問文檔中的數據。
由於SwitchToView2中需要在函數外指定視圖類指針變量,因此在各個視圖中可以通過AfxGeMainWnd()獲取CMainFrame類指針,從而可以直接該問到CMainFrame類中定義的public視圖類指針變量,這樣便可在視圖類之間直接訪問。而SwitchToView1中的視圖類指針變量是在函數內定義的,因此無法在各個視圖類中進行視圖之間的訪問,並且在本例中CViewerView類對象被創建了兩次。SwitchToView2中,CViewerView類對象只創建一次。
除了上述兩種方法外,還有一種方法,那就是使用靜態切分機制來進行。由於它涉及過多的底層方法,對初學者而言,相對較難,因此這裡不再給出。
結束語
在本講中,我們重點討論了視圖類的添加、單文檔的視圖切換方法和技巧。對於初學者來說,學習並掌握這些技巧能對MFC的文檔/視圖機制有一個較為深入的了解。當然,MFC文檔/視圖機制本身是非常復雜的,在以後的學習中應慢慢地體會和理解。在最後一講中,我們將重點應用程序的安裝和部署。