簡介
該類定義了兩個成員函式用於創建主視窗,即Create()和LoadFrame()。前者主要通過CWnd::CreateEx()創建視窗;而後者首先組織參數,再調用前者。它們的定義如下:
BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext)
{
/*可見,參數列表與CWnd::Create()稍有不同。因為目的是創建主視窗,所以第6個參數要求選單資源名*/
HMENU hMenu = NULL;
if (lpszMenuName != NULL)
{
//搜尋包含該選單資源的實例(當前進程或者按進行裝入的DLL)
HINSTANCE hInst = AfxFindResourceHandl(lpszMenuName, RT_MENU);
//裝入選單資源
if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
{
TRACE0(“Warning: failed to load menu for CFrameWnd.\n”);
PostNcDestroy(); //perhaps delete to C++ object
return FALSE;
}
}
m_strTitle = lpszWindowName; //存儲視窗標題,以備後用(如刷新顯示)
// 調用CWnd::CreateEx()
if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right – rect.left, rect,bottom – rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
{
if (hMenu != NULL)
DestroyMenu(hMenu); //如果創建失敗,釋放選單資源
return FALSE;
}
return TRUE;
}
BOOL CFrameWnd::LoadFrame(UNIT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)
{
/*主視窗的選單、圖示、
加速鍵、及標題都以nIDResource標識。除創建視窗外,還要做許多工作,如設定幫助上下文ID、裝入加速鍵、初始化子視窗。所以在文檔/視圖框架程式中,總是使用LoadFrame()創建主視窗。*/
ASSERT_VALID_IDR(nIDResource);
ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE)
CString strFullString;
if (strFullString.LoadString(nIDResource))
AfxEXtractSubString(m_strTitle, strFullString, 0); //取得視窗標題
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
LPCTSTR lpszTitle = m_strTitle;
//調用CFrameWnd::Create()
if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
return FALSE;
//存儲選單句柄
ASSERT(m_hWnd != NULL);
m_hMenuDefault = ::GetMenu(m_hWnd);
LoadAccelTable(MAKEINTRESOURCE(nIDResource));
if (pContext == NULL) //初始化子視窗
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
return TRUE;
}
由於LoadFrame()的形能簡潔,在創建視窗的同時,完成許多主
窗體的初始化工作。所以,如果以CFrameWnd為程式主窗體,一般通過LoadFrame()創建。如果要使用CFrameWnd 創建簡單化的主窗體或子窗體,可調用Create()。
在文檔視圖支持的SDI程式中,主框架窗是在文檔模板中套用CDocTemplate::CreateNewFrame()創建的。在該函式中,首先動態創建CFrameWnd對象,再調用對象的LoadFrame()成員。由於在CFrameWnd::PostNcDestroy()中清除了當前對象,所以儘管CFrameWnd對象慣於在堆中構造,卻不必關心它的釋放。例如:
void CFrameWnd::PostNcDestroy()
{
delete this;
}
另外,因為CFrameWnd創建了主視窗,所以在視窗銷毀時,要向
訊息循環傳送WM_QUIT訊息,這個處理已封裝在
基類CWnd中。
套用
視圖
視圖是主框架視窗的一個ID為AFX_IDW_PANE_FIRST,帶有框線的子視窗,這個主框架視窗是由CFrameWnd類封裝並創建的。顯然,視圖作為其子視窗,也是由CFrameWnd創建的。成員函式CFrameWnd::OnCreateClient()用於創建視圖視窗,它是該類的
WM_CREATE訊息處理函式中被調用的。代碼如下:
BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
{
/*pContext->m_pNewViewClass存儲視圖的運動時類信息的指針(CRuntimeClass*),可用於動態創建視圖*/
if (pContext != NULL && pContext->m_pNewViewClass != NULL)
{
//調用CFrameWnd::CreateView()創建視圖
if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
return FALSE;
}
return TRUE;
}
CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
{
ASSERT(m_hWnd != NULL);
ASSERT(::IsWindow(m_hWnd));
ASSERT(pContext != NULL);
ASSERT(pContext->m_pNewViewClass != NULL);
//套用運行類信息,動態創建視圖對象
CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
if (pView == NULL)
{
TRACE1(“Warning: Dynamiccreateof view type %hs failed.\n”, pContext->m_pNewViewClass->m_lpszClassName);
return NULL;
}
ASSERT_KINDOF(CWnd, pView);
//使用已經創建的視圖對象創建視圖視窗
if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, nID, pContext))
{
TRACE0(“Warning: could notcreateview for frame.\n”);
return NULL; //can’t continue without a view
}
//根據視圖視窗的邊界風格調整框架視窗風格
if (afxData.bWin4 && (pView->GetExStyle() & WS_EX_CLIENTEDGE))
{
//如果視圖已經設定了凹陷框線,去除主視窗的凹陷框線
ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
}
return pView;
}
一個主視窗可能包含多個視圖,它們或者是通過CSpliterWnd在客戶區拆分創建的,或者是直接在客戶區以子視窗形式創建。框架規定只能有一個活動視圖,如果不使用拆分,同時只能顯示一個視圖。在主框架視窗創建後(視圖也已創建),一般要調用CFrameWnd::InitialUpdateFrame()進行初始化,該函式首先設定第一視圖(ID為AFX_IDW_PANE_FIRST)為活動視圖,然後向所有視圖傳送初始化訊息,確保每個視圖CView::
OnInitialUpdate()被調用。
可以調用CFrameWnd::SetActiveView()及CFrameWnd::GetActiveView()設定或取得活動視圖。在設定活動視圖後,應該將活動視的ID切換為AFX_IDW_PANE_FIRST,因為有些操作是只針對第一視圖的。例如,只有第一視圖才能與
控制條爭奪主視窗客戶區的空間,所以其他視圖無法在主框架視窗中正常顯示(如果不使用拆分)。
控制條
主框架視窗的直觀特點是被豐富的
控制條裝飾的,如工具條、狀態條等,它們都派生於
基類CControlBar。當滑鼠移到工具條或某選單項區域時,相應的提示信息會在狀態欄顯示或以Tip形式彈出;沒有建立訊息映射的命令會自動禁止;客戶區發生變化時視圖和控制條會自動排列。這一切都是CFrameWnd封裝的功能。下面列舉幾個重要的控制條操作函式。
EnableDocking(): 允許控制條在自己的客戶區依靠。
DockControlBar(): 將控制條依靠在客戶區周邊。
FloatControlBar(): 將控制條浮動在螢幕上,而不是依靠在客戶區。
ShowControlBar(): 顯示或隱藏控制條。
SaveBarState(): 將所有控制條的狀態存入初始化檔案或註冊表。
LoadBarState(): 從初始化檔案或註冊表中恢復所有
控制條狀態。
SetDockState(): 從一個CDockState對象中恢復控制條狀態。
SetMessageText(): 在狀態欄的第一個面板區域顯示一個信息串。
ReclcLayout():
虛函式,當
控制條位置變化或客戶區尺寸變化時被調用,重新設定視圖及控制條在客戶區的位置。可根據需要
重載它或主動調用它。
命令訊息
命令訊息是指選單、
工具列、
加速鍵及
命令按鈕向其所在視窗傳送的
WM_COMMAND訊息。主框架視窗通常包含應用程式的系統主選單和工具列,而加速鍵往往在LoadFrame()中裝入主視窗,它們都要向主視窗傳送命令訊息。
命令訊息與視窗訊息(除WM_COMMAND之外,前綴是WM_的訊息)不同,視窗訊息與某一視窗(句柄)緊密相關,應該由接收訊息的視窗來處理;而命令訊息往往與具體的視窗無關,只是為本程式完成一個功能操作。主框架視窗的系統選單(工具按鈕)尤其如此,某一個主選單命令在其他視窗中(如視圖)或在其他模組中(如文檔)處理也許更合理。為了解決這個矛盾,CFrameWnd實現了分發命令訊息的機制,它能夠將本視窗收到的命令訊息分發給視圖、文檔和套用類。
CCmdTarget類定義了一個OnCmdMsg()
虛擬函式,用於處理命令訊息,
派生類可以
重載它,實現自己的命令訊息處理方式。是的,CFrameWnd的命令訊息分發機制就是通過重載這個函式實現的。該函式的代碼如下:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
CPushRoutingFrame push(this);
//首先將命令訊息傳給活動視圖
CView* pView = GetActiveView();
//如果袖圖沒有處理,它將傳送命令訊息給關聯的文檔對象
if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE; //視圖或文檔已經處理該命令,返回
//視圖或文檔沒有處理該訊息,即沒有建立該命令的訊息映射,下面由主視窗本身處理
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE; //主視窗已處理
//主視窗沒有處理,最後嘗試套用類
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE; //最終沒有處理該命令訊息,返回false,該訊息由默認過程處理
}
最後,讀者要注意這樣一個事實:主視窗直接調用視圖(間接調用文檔)、套用類的OnCmdMsg()
虛函式處理命令訊息,並沒有通過SendMessage()或PostMessage()將命令訊息轉發。而OnCmdMsg()僅在類中搜尋訊息映射表,查找該命令的處理函式,找不到則返回false。所以視圖類只有通過訊息映射,才能處理主視窗轉發的命令訊息,如果使用CView::WindowProc()捕捉該類訊息,會一無所獲。
訊息處理
為了管理
控制條和視圖,CFrameWnd為幾個視窗訊息建立了訊息映射,專門進行處理。
OnInitMenuPopup(): 處理WM_INITMENUPOPUP訊息,設定彈出選單的各項目的啟用/禁止狀態。
OnEnterIdle(): 處理WM_ENTERIDLE訊息,設定狀態條的空閒時提示信息。
OnMenuSelect(): 處理WM_MENUSELECT訊息,當某選單項被選擇時更新狀態條提示。
OnToolTipText(): 處理TTN_NEEDTEXT通知訊息,顯示工具條的工具提示。
OnUpdateKeyIndicator(): 更新狀態條的鍵盤狀態指示器信息。
OnUpdateControlBarMenu(): 更新控制條的啟用/禁止狀態,如工具條按鈕。
OnSize(): 處理
WM_SIZE訊息,調用RecalcLayout()排列客戶區控制項及視圖。
OnHScroll(): 處理WM_HSCROLL訊息,滾動視圖。
OnVScroll(): 處理WM_VSCROLL訊息,滾動視圖。
OnClose(): 處理WM_CLOSE訊息,存儲並關閉文檔。
成員
m_bAutoMenuEnable | 自動控制使選單項目可用或無效 |
rectDefault | 當構造一個CFrameWnd對象時傳遞此靜態CRect作為參數,使Windows選擇窗體的初始大小和位置 |
初始化
Create | 調用以構造和初始化一個與CFrameWnd對象有關的Windows框架視窗 |
| 調用以從資源信息中動態構造一個框架視窗 |
LoadAccelTable | 裝入一個加速器表格 |
| 復位控制項條設定 |
| 存儲控制項條設定 |
| 顯示控制項條 |
| 在主視窗中停靠框架視窗 |
GetDockState | 獲取框架視窗的停靠狀態 |
操作
ActivateFrame | 使框架對用戶可視並可用 |
InitialUpdateFrame在 | 調用的框架窗中使OnInitialUpdate成員函式屬於所有視圖 |
| 返回活動CFrameWnd對象 |
SetActiveView | 設定活動CView對象 |
| 返回活動CView對象 |
CreateView | 在框架中構造一個非CView派生的視圖 |
| 返回活動CDowment對象 |
GetControlBar | 返回控制項條 |
GetMessageString | 獲得與命令ID相符的訊息 |
IsTracking | 確定分隔條是否正在移動 |
SetMessageText | 設定標準狀態條的文本 |
| 允許一個控制項條停靠 |
DockControlBar | 停靠一個控制項條 |
FloatControlBar | 浮動一個控制項條 |
| 將框架視窗設定為模態 |
EndModalState | 結束框架視窗的模態狀態,用BeginModalState使無效的視窗可用 |
| 返回一個表明框架視窗是否處於模態狀態 |
ShowOwnedWindows | 顯示所有CFrameWnd對象的後代視窗 |
| 重新設定CFrameWnd對象的控制項條的位置 |
可重載函式
OnCreateClient | 為框架構造一個用戶視窗 |
OnSetPreviewMode | 設定套用的主框架成為或退出預列印模式 |
GetMessageBar | 返回一個屬於框架視窗的狀態條指針 |
NegotiateBorderSpace | 調整框架視窗中的框線空白 |
命令處理
OnContextHelp | 處理相應項的SHIFT+F1幫助 |