複製鏈接
請複製以下鏈接發送給好友

CFrameWnd

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

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中。

CFrameWnd應用

CFrameWnd視圖

視圖是主框架窗口的一個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,因為有些操作是隻針對第一視圖的。例如,只有第一視圖才能與控制條爭奪主窗口客户區的空間,所以其他視圖無法在主框架窗口中正常顯示(如果不使用拆分)。

CFrameWnd控制條

主框架窗口的直觀特點是被豐富的控制條裝飾的,如工具條、狀態條等,它們都派生於基類CControlBar。當鼠標移到工具條或某菜單項區域時,相應的提示信息會在狀態欄顯示或以Tip形式彈出;沒有建立消息映射的命令會自動禁止;客户區發生變化時視圖和控制條會自動排列。這一切都是CFrameWnd封裝的功能。下面列舉幾個重要的控制條操作函數。
EnableDocking(): 允許控制條在自己的客户區依靠。
DockControlBar(): 將控制條依靠在客户區周邊。
FloatControlBar(): 將控制條浮動在屏幕上,而不是依靠在客户區。
ShowControlBar(): 顯示或隱藏控制條。
SaveBarState(): 將所有控制條的狀態存入初始化文件或註冊表。
LoadBarState(): 從初始化文件或註冊表中恢復所有控制條狀態。
GetDockState(): 將控制條狀態信息存入一個CDockState對象。
SetDockState(): 從一個CDockState對象中恢復控制條狀態。
SetMessageText(): 在狀態欄的第一個面板區域顯示一個信息串。
ReclcLayout():虛函數,當控制條位置變化或客户區尺寸變化時被調用,重新設置視圖及控制條在客户區的位置。可根據需要重載它或主動調用它。

CFrameWnd命令消息

命令消息是指菜單、工具欄加速鍵命令按鈕向其所在窗口發送的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消息處理

為了管理控制條和視圖,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框架窗口
調用以從資源信息中動態構造一個框架窗口
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幫助