该类定义了两个成员函数用于创建主窗口,即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 createof 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 createview 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消息,存储并关闭文档。
数据成员
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帮助 |