CFrameWnd

CFrameWnd類往往用於創建應用程式的主視窗,因為它能很好地支持系統選單和控制條(工具條、狀態條等),為此定義了大量的成員函式和變數。在編寫文檔/視圖結構的應用程式時,CFrameWnd作為主視窗管理視圖和文檔對象。視圖對象和控制條都成為CFrameWnd的子視窗,它們分享客戶區,其位置被CFrameWnd有效地排列。 CFrameWnd直接支持單文檔界面(SDI),對於多文檔界面(MDI),使用其派生類CMDIFrameWnd和CMDIChildWnd。

CFrameWnd的創建

該類定義了兩個成員函式用於創建主視窗,即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: Dynamic create of 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 not create view 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(): 從初始化檔案或註冊表中恢復所有控制條狀態。
GetDockState(): 將控制條狀態信息存入一個CDockState對象。
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訊息,存儲並關閉文檔。

CFrameWnd類成員

數據成員

m_bAutoMenuEnable 自動控制使選單項目可用或無效
rectDefault 當構造一個CFrameWnd對象時傳遞此靜態CRect作為參數,使Windows選擇窗體的初始大小和位置

構造函式
CFrameWnd 構造一個CFrameWnd對象

初始化
Create 調用以構造和初始化一個與CFrameWnd對象有關的Windows框架視窗
LoadFrame 調用以從資源信息中動態構造一個框架視窗
LoadAccelTable 裝入一個加速器表格
LoadBarState 復位控制項條設定
SaveBarState 存儲控制項條設定
ShowControlBar 顯示控制項條
SetDockState 在主視窗中停靠框架視窗
GetDockState 獲取框架視窗的停靠狀態

操作
ActivateFrame 使框架對用戶可視並可用
InitialUpdateFrame在 調用的框架窗中使OnInitialUpdate成員函式屬於所有視圖
GetActiveFrame 返回活動CFrameWnd對象
SetActiveView 設定活動CView對象
GetActiveView 返回活動CView對象
CreateView 在框架中構造一個非CView派生的視圖
GetActiveDocument 返回活動CDowment對象
GetControlBar 返回控制項條
GetMessageString 獲得與命令ID相符的訊息
IsTracking 確定分隔條是否正在移動
SetMessageText 設定標準狀態條的文本
EnableDocking 允許一個控制項條停靠
DockControlBar 停靠一個控制項條
FloatControlBar 浮動一個控制項條
BeginModalState 將框架視窗設定為模態
EndModalState 結束框架視窗的模態狀態,用BeginModalState使無效的視窗可用
InModalState 返回一個表明框架視窗是否處於模態狀態
ShowOwnedWindows 顯示所有CFrameWnd對象的後代視窗
RecalcLayout 重新設定CFrameWnd對象的控制項條的位置

可重載函式
OnCreateClient 為框架構造一個用戶視窗
OnSetPreviewMode 設定套用的主框架成為或退出預列印模式
GetMessageBar 返回一個屬於框架視窗的狀態條指針
NegotiateBorderSpace 調整框架視窗中的框線空白

命令處理
OnContextHelp 處理相應項的SHIFT+F1幫助
站長統計

相關詞條

相關搜尋

熱門詞條

聯絡我們