标题：CFrameWnd
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帮助 
站长统计