動態切分窗口:
動態切分窗口不能包含多於4個的窗格,要想得到4個以上的窗格,可以使用靜態切分窗口。動態切分窗口中的幾個窗格通常使用的是同一個視圖類。
創建動態切分窗口的方式共有3種:
1、在創建應用程序時,利用應用程序向導AppWizard的選項設置切分窗口
2、在創建了應用程序之后,通過添加組件對窗口進行切分
3、通過手動添加代碼編程的方式
使用AppWizard創建動態切分:
在MFC AppWizard Step 4 of 6對話框中,單擊Advanced按鈕,彈出Advanced Options對話框,選擇Window Styles選項卡,選中該頁的Use split window檢查框。
使用Splitter Bar組件:
選擇Project|Add To Project|Components and Controls...菜單,彈出Components and Controls Gallery對話框。雙擊Visual C++ Components目錄,選擇Splitter Bar項,插入Splitter Bar組件。
運行程序,打開【查看】菜單或者是【窗口】菜單,找不到前面使用AppWizard添加動態切分時的【分隔】菜單項。說明Splitter Bar組件的方式並沒有添加相應的菜單項,程序員可以自己添加上去。
注意:菜單的Caption(標題)可以隨便設定,但注意菜單項的ID必須是ID_WINDOW_SPLIT。ID_WINDOW_SPLIT是系統預先指定的,程序員一般不需要對此命令ID添加命令消息處理函數,就可以實現分隔,因為MFC應用程序框架中已經對該命令ID的消息進行了處理。
手動添加代碼:
1、在框架窗口類(對單文檔為主框架窗口,對多文檔為子框架窗口)中聲明一個CSplitterWnd類的成員變量m_wndSplitter
2、使用ClassWizard為框架窗口類添加OnCreateClient函數
注意:這里使用ClassWizard是重寫父類的虛函數,而不是添加消息處理
3、在OnCreateClient函數中調用CSplitterWnd類的成員函數Create產生動態切分
4、為應用程序添加一個ID為ID_WINDOW_SPLIT的菜單項
CSplitterWnd切分窗口類:
CSplitterWnd的成員函數中,有3個使用非常頻繁
1、Create函數
創建一個動態的切分窗口
2、CreateStatic函數
創建一個靜態的切分窗口
3、CreateView函數
為靜態切分窗口創建視圖窗口。在框架顯示一個靜態切分窗口之前,該窗口的所有視圖窗格都必須已被創建好。當動態切分窗口時,此函數被框架窗口自動調用。
靜態切分窗口:
靜態切分窗口是指在窗口創建時,切分窗口的窗格就已經創建好了,且窗格的數量和順序不會改變。最多可以支持16行×16列的窗口(即256個窗格),但是必須在應用程序中創建窗格,而且不同的窗口往往使用不同的視圖類。
創建靜態切分的步驟:
1、在框架窗口類中聲明一個CSplitterWnd類的成員變量m_wndSplitter
2、使用ClassWizard為框架窗口類添加OnCreateClient函數
3、在OnCreateClient函數中調用CSplitterWnd::CreateStatic產生靜態切分
4、在OnCreateClient函數中調用CSplitterWnd::CreateView產生每個切分的視圖窗口
為2DCAD應用程序添加靜態切分功能:
1、聲明切分窗口類對象m_wndSplitter
為主框架窗口類CMainFrame添加保護成員變量m_wndSplitter,代碼如下:
CSplitterWnd m_wndSplitter; //用於產生靜態切分
2、在框架窗口類中添加OnCreateClient函數
打開ClassWizard,選中MessageMaps選項卡,在Class name中選擇CMainFrame類,在ObjectIDs列表中選擇CMainFrame,在Message列表框中雙擊OnCreateClient項,為框架窗口添加OnCreateClient函數。
3、使用CreateStatic產生靜態切分
在OnCreateClient函數中添加如下代碼,就可以產生1×2的靜態切分窗口:
m_wndSplitter.CreateStatic(this, 1, 2);
4、使用CreateView產生每個視圖窗口
當用戶建立一靜態切分窗口時,應當同時建立窗口內的所以區域。可以在OnCreateClient函數中添加如下代碼來為第一行第一列的窗格產生視圖:
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CMy2DCADView),
CSize(450, 250), pContext);
其中,第3個參數,視圖類的執行期類CRuntimeClass指針,可以用宏RUNTIME_CLASS來獲得。
注意:(1)必須為每一個靜態切分窗口創建視圖窗口,不能漏掉一個
(2)由於使用到了視圖類,因此,必須包含相應的類的頭文件。在MainFrm.cpp文件的開始部分包括以下兩個頭文件:
#include "2DCADDoc.h"
#include "2DCADView.h"
(3)產生靜態切分后,就不能再調用默認情況下的基類的OnCreateClient函數。因此,應該將下面的代碼刪除或者注釋掉:
// return CFrameWnd::OnCreateClient(lpcs, pContext);
最后,完整的CMainFrame::OnCreateClient函數代碼如下:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
//使用CreateStatic函數產生靜態切分
m_wndSplitter.CreateStatic(this, //父窗口指針
1, //行數
2); //列數
//使用CreateView為第1個窗格產生視圖
m_wndSplitter.CreateView(0, 0, //窗格的行、列序數
RUNTIME_CLASS(CMy2DCADView), //視圖類
CSize(450, 250), //初始化大小
pContext); //父窗口的創建參數
//使用CreateView為第2個窗格產生視圖
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CMy2DCADView),
CSize(450, 250), pContext);
return 1; //不再調用基類的OnCreateClient函數
// return CFrameWnd::OnCreateClient(lpcs, pContext);
}
這時,用戶在一個視圖中所作的修改還不能立刻反映到其他視圖窗口中,還應添加相應的通知視圖重畫的代碼。
5、通知視圖重畫
文檔類有一個名為UpdateAllViews的函數,可以在文檔數據發生改變時通知所有的視圖窗口重畫。
在視圖類的OnLButtonDown函數中每個調用SetModifiedFlag函數的后面立即調用UpdateAllViews的函數,代碼如下:
...
pDoc->SetModifiedFlag(); //設置文檔“臟”標志
pDoc->UpdateAllViews(this); //重畫所有視圖
...
向當前工程中插入新類的兩種方式:
1、使用New Class對話框添加新類
(1)MFC ClassWizard中的New Class對話框
使用ClassWizard對話框中的Add Class...按鈕下的New...菜單項可以打開MFC ClassWizard對話框下的New Class對話框。這個對話框用來向當前工程中添加派生自MFC類庫的新類。
(2)通用的New Class對話框
單擊菜單命令Insert|New Class...可以打開一個通用的New Class對話框。實際上通用的New Class對話框相當於包含了3個對話框,用於產生幾乎所有不同的類。
2、使用插入包含類定義的文件到當前工程中
(1)將所需類的.h、.cpp文件復制到目標工程目錄下
(2)打開目標工程,單擊Project|Add To Project|Files...菜單項插入所需類的.h、cpp文件到目標工程中
(3)在所需類的.cpp文件中,去掉包含原來的工程的頭文件語句(比如2DCAD.h),添加包含目標工程的頭文件語句(比如Mut.h)
添加新的視圖類:
為2DCAD工程添加一個新的視圖類CTextView,單擊Insert|New Class...菜單項,在New Class對話框中確保Class type中選擇的是MFC Class,在Name編輯框中輸入CTextView,在Base class下拉列表中指定基類為CEditView。
添加了新的視圖類之后,就可以在框架窗口的OnCreateClient函數中調用CreateView函數時使用這個新視圖類了。接下來使用CTextView作為靜態切分窗口中第二個窗格的視圖類,在OnCreateClient函數中修改創建第二個窗口視圖的代碼如下:
//使用CreateView為第2個窗格產生視圖
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CTextView),//使用文本視圖類
CSize(250, 250), pContext);
由於使用到了新的視圖類,因此,應該在MainFrm.cpp文件的開始部分添加包含CTextView類頭文件的代碼:
#include "TextView.h"
注意:CEditView封裝了編輯框的功能,因此,如果要編寫文字處理軟件,可以使用CEditView作為視圖基類。CEditView最多支持64K的文本,如果需要支持更豐富的文本,可以使用CRichEditView作為視圖基類,但要包含頭文件afxrich.h。
表單視圖類CFormView:
CFormView就是可以在其中包含許多控件的視圖基類。這個類也像CDialog對話框類一樣,封裝了數據交換功能。甚至可以將CFormView理解為作為視圖的對話框。
1、創建對話框模板資源
(1)添加對話框模板,ID為IDD_FORMVIEW
(2)修改屬性
在Style選項卡中,設置Style選項為Child,Border選項為None,並去掉對Title Bar復選框的勾選。
注意:由於視圖類是一個子窗口,因此,必須設置Child選擇,否則程序不能正常運行。
(3)添加控件
刪掉默認的OK和Cancel按鈕,依次添加一個靜態文本、一個分組框和4個單選按鈕,同時為第一個單選按鈕添加上Group屬性。
注意:對一組單選按鈕來說,只需要對該組的第一個單選按鈕設置Group屬性就可以了。如果不設置Group屬性,就無法添加對應的成員變量。
2、產生CFormView的派生類
(1)使用ClassWizard為IDD_FORMVIEW對話框模板資源添加一個名為CColorForm的視圖類,注意選擇基類為CFormView
(2)在Member Variables選項卡中為IDC_RADIO1添加一個名為m_nColor的整型成員變量
注意:對一組單選按鈕只能對帶有Group屬性的按鈕增加一個成員變量,並且這個成員變量必須是int型。0表示選擇第一個,依次類推,-1表示未選擇。
在CColorForm構造函數中令成員變量m_nColor的初始值為0,表示選擇第一項。
3、修改切分窗口的視圖類
打開OnCreateClient函數,修改創建第二個窗口視圖的代碼如下:
//使用CreateView為第2個窗格產生視圖
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CColorForm),//使用表單視圖類
CSize(250, 250), pContext);
由於使用到了新的視圖類,因此,應該在MainFrm.cpp文件的開始部分添加包含CColorForm類頭文件的代碼:
#include "ColorForm.h"
可以像處理無模式對話框那樣,在表單視圖類中添加相應的消息處理函數。
一個多文檔多視圖的綜合示例:
下面綜合前面的知識,創建一個包含多文檔、多視圖、動態切分、三叉靜態切分以及可以支持多種文檔類型的綜合示例。
支持多種文檔類型:
在MFC應用程序中,要增加附加的文檔類型,大致可分為以下幾步:
1、使用ClassWizard添加新的文檔類、子框架窗口類以及視圖類(如果需要的話)
2、利用資源編輯器為新的文檔類型添加字符串資源
3、利用資源編輯器添加附加的圖標和菜單資源
4、創建新的文檔模板對象並加入到應用程序中
5、完成文檔的序列化以及視圖的顯示交互工作
下面為Mut添加支持多文檔類型的功能(此時Mut本身是一個空的具有動態切分窗口的多文檔程序):
1、添加新的文檔類、子框架窗口類和視圖類
一般來說,一個新的文檔類型所需要的文檔類、外邊框窗口類和視圖類有可能都不同於已經存在的類的,但也可能和現存的類相同(比如使用相同的子框架窗口)。因此,根據不同的需要會添加不同的類。這里,首先只添加一個新的視圖類和子框架窗口類,而文檔類則使用原來的類。
注意:多種文檔類型的各個文檔類一般是不同的
(1)添加新的視圖類的方法可以參加《向當先工程中插入新類的兩種方式》中的第二種方式,插入2DCAD程序中所用到的可以輸入簡單的文本的CTextView類。
(2)添加新的子框架窗口類CTextFrame,基類選擇為CMDIChildWnd。具體方法參加向當先工程中插入新類的兩種方式》中的第一種方式。
2、利用資源編輯器為新的文檔類型添加字符串資源
參照Mut應用程序已經存在的文檔模板字符串資源IDR_MUTTYPE,在字符串編輯器中增加一個新的字符串資源,指定ID為IDR_TEXTTYPE,內容為:
\nText\n文本視圖窗口\nText Files(*.txt)\n.txt\nTextDocument\nText Document
可見文檔模板字符串包含7個由“\n”結尾並分隔的子串,最后一個字符串后面不需要“\n”。這些子串描述了文檔的類型,它們的含義按照順序分別如下:
(1)窗口標題(2)文檔名(3)新建文檔類型名(4)過濾器名(5)文件擴展名(6)文檔類型ID(7)注冊文檔類型名
3、利用資源編輯器添加附加的圖標和菜單資源
(1)添加一個ID為IDR_TEXTTYPE的圖標,可以利用畫圖工具編輯之
(2)添加一個ID為IDR_TEXTTYPE的菜單,將IDR_MUTTYPE菜單資源下的【文件】、【編輯】、【查看】、【幫助】菜單復制到IDR_TEXTTYPE中。注意:可以使用右鍵菜單中的Insert Copy...,然后再修改。
注意:這些資源的ID必須同第二步中創建文檔模板字符串中所用的ID相同。
4、創建新的文檔模板對象並加入到應用程序中
在應用程序類CMutApp的InitInstance函數中,參照應用程序框架生成的代碼,創建另一個CMultiDocTemplate對象,並用CWinApp::AddDocTemplate成員函數注冊該模板對象。
//這是應用程序框架生成的代碼
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_MUTTYPE,
RUNTIME_CLASS(CMutDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMutView));
AddDocTemplate(pDocTemplate);
//創建新的文檔模板對象
pDocTemplate = new CMultiDocTemplate(
IDR_TEXTTYPE, //新創建的字符串資源、圖標資源和菜單資源
RUNTIME_CLASS(CMutDoc), //仍舊使用原來的文檔類
RUNTIME_CLASS(CTextFrame), //新添加的子框架窗口類
RUNTIME_CLASS(CTextView)); //新添加的文本視圖類
//將新的文檔模板對象加入到應用程序中。
AddDocTemplate(pDocTemplate);
由於使用到了新的視圖類CTextView和新的子框架窗口類CTextFrame,因此應該在Mut.cpp文件的開始部分包含它們的頭文件:
#include "TextView.h"
#include "TextFrame.h"
注意:此時Mut程序就包含有兩個文檔模板對象,可以支持兩種不同的文檔了,但是,它們使用了相同的文檔類,而實際應用中應該使用不同的文檔類。
5、完成文檔的序列化以及視圖的顯示交互工作
最后,根據用戶的需要,可以為文檔類添加上數據保存以及序列化功能,為視圖類添加相應的顯示數據和交換功能。
多文檔的三叉靜態切分:
這三個窗口的排列方式類似“|—”形狀,所使用的視圖類分別類似前面2DCAD工程中創建的CColorForm類以及Mut工程中創建的CTextView類和CMutView類。
1、添加新的文檔模板對象
(1)添加新的視圖類和子框架窗口類
在Mut工程中創建一個和2DCAD工程中相同的CFormView類的派生類CColorForm。
注意:可以使用文件復制再插入的方法,還需要將所需的對話框資源模板以及控件的ID值定義都復制過來。
再添加一個派生自CMDIChildWnd類的CThreeFrame類,作為文檔的外邊框窗口。
(2)編輯資源
添加ID都為IDR_THREETYPE的三個資源,字符串資源,圖標資源和菜單資源。其中,字符串資源如下:
\nThree\n三叉切分窗口\nThree Files(*.thr)\n.txt\nThreeDocument\nThree Document
(3)創建新的文檔模板對象
在應用程序類CMutApp的InitInstance函數中,參照應用程序框架生成的代碼,創建另一個CMultiDocTemplate對象,並用CWinApp::AddDocTemplate成員函數注冊該模板對象。
//創建三叉靜態切分的文檔模板對象
pDocTemplate = new CMultiDocTemplate(
IDR_THREETYPE, //用於三叉靜態切分窗口的字符串資源、圖標資源和菜單資源
RUNTIME_CLASS(CMutDoc), //仍舊使用原來的文檔類
RUNTIME_CLASS(CThreeFrame), //新添加的三叉切分子框架窗口類
RUNTIME_CLASS(CColorForm)); //新添加的表單視圖派生類
//將新的文檔模板對象加入到應用程序中。
AddDocTemplate(pDocTemplate);
由於使用到了新的視圖類CColorForm和新的子框架窗口類CThreeFrame,因此應該在Mut.cpp文件的開始部分包含它們的頭文件:
#include "ColorForm.h"
#include "ThreeFrame.h"
2、添加三叉靜態切分
(1)在子框架窗口類CThreeFrame中添加兩個CSplitterWnd類的保護成員變量m_wndSplitter1和m_wndSplitter2,分別用於產生第一次靜態切分和第二次靜態切分。
(2)使用ClassWizard為CThreeFrame添加OnCreateClient成員函數。
(3)改寫OnCreateClient成員函數代碼如下:
BOOL CThreeFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
//使用CreateStatic函數產生第一次靜態切分
m_wndSplitter1.CreateStatic(this, //父窗口指針
1, //行數
2); //列數,一行兩列
//使用CreateView為第1個窗格產生CColorForm視圖
m_wndSplitter1.CreateView(0, 0, //窗格的行、列序數
RUNTIME_CLASS(CColorForm), //表單視圖類
CSize(250, 250), //初始化大小
pContext); //父窗口的創建參數
//再次使用CreateStatic函數對第一次切分產生的第二個窗口靜態切分
m_wndSplitter2.CreateStatic(
&m_wndSplitter1, //父窗口是第一次的切分窗口
2, 1, //將這個窗口切分成兩行一列
WS_CHILD | WS_VISIBLE, //窗口風格
m_wndSplitter1.IdFromRowCol(0, 1)
//指出新切分的窗口是上一次分隔的第一行第二個窗口
);
//使用CreateView為第二次切分的第1個窗格產生視圖
m_wndSplitter2.CreateView(0, 0, RUNTIME_CLASS(CTextView),//使用文本視圖類
CSize(150, 120), pContext);
//使用CreateView為第二次切分的第2個窗格產生視圖
m_wndSplitter2.CreateView(1, 0, RUNTIME_CLASS(CMutView),//使用CMutView視圖類
CSize(250, 250), pContext);
return 1; //不再調用基類的OnCreateClient函數
// return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
可見產生第一次切分的方法是,先使用CreateStatic函數產生第一次切分,然后再使用CreateStatic函數對第一次切分所產生的窗口再次切分。
注意:第二次調用CreateStatic函數切分時,函數的參數有些變化:一是父窗口是第一次的切分窗口,二是ID必須是上次切分窗格的ID。
(4)在ThreeFrame.cpp文件的開始部分包含下列頭文件:
#include "MutDoc.h"
#include "MutView.h"
#include "TextView.h"
#include "ColorForm.h"