CWnd的派生类-1、CFrameWnd类

以上对CWnd作了深入的探讨,但是还不够全面,相信在以后章节的学习中,读者会逐渐地把握这个重要的基类。承接以上内容,下面学习CWnd几个实用的派生类:CFrameWnd、CView、CDialog。

7.1  CFrameWnd类
CFrameWnd类往往用于创建应用程序的主窗口,因为它能很好地支持系统菜单和控制条(工具条、状态条等),为此定义了大量的成员函数和变量。在编写文档/视图结构的应用程序时,CFrameWnd作为主窗口管理视图和文档对象。视图对象和控制条都成为CFrameWnd的子窗口,它们分享客户区,其位置被CFrameWnd有效地排列。

CFrameWnd直接支持单文档界面(SDI),对于多文档界面(MDI),使用其派生类CMDIFrameWnd和CMDIChildWnd。

7.1.1  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 = AfxFindResourceHandle(lpszMenuName, RT_MENU);

                  //装入菜单资源

                  if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)

                  {        TRACE0("Warning: failed to load menu for CFrameWnd.\n");

                          PostNcDestroy();            // perhaps delete the 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(UINT 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::Create NewFrame()创建的。在该函数中,首先动态创建CFrameWnd对象,再调用对象的LoadFrame()成员。由于在CFrameWnd::PostNcDestroy()中清除了当前对象,所以尽管CFrameWnd对象惯于在堆中构造,却不必关心它的释放。例如:

void CFrameWnd::PostNcDestroy()

{        delete this; }

另外,因为CFrameWnd创建了主窗口,所以在窗口销毁时,要向消息循环发送WM_QUIT消息,这个处理已封装在基类CWnd中。

7.1.2  管理视图对象
其实,视图无非是主框架窗口的一个ID为AFX_IDW_PANE_FIRST,带有边框的子窗口,这个主框架窗口是由CFrameWnd类封装并创建的。显然,视图作为其子窗口,也是由CFrameWnd创建的。成员函数CFrameWnd::OnCreateClient()用于创建视图窗口,它是在该类的WM_CREATE消息处理函数中被调用的。代码如下:

BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)

{       

/* pContext->m_pNewViewClass存储视图的运行时类信息的指针(CRuntimeClass*),可用于动态创建视图(参见5.1.2节)*/

         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;

}

一个主窗口可能包含多个视图,它们或者是通过CSplitterWnd在客户区拆分创建的,或者是直接在客户区以子窗口形式创建。框架规定只能有一个活动视图,如果不使用拆分,同时只能显示一个视图。在主框架窗口创建后(视图也已创建),一般要调用CFrame Wnd::InitialUpdateFrame()进行初始化,该函数首先设置第一视图(ID为AFX_IDW_PANE_ FIRST)为活动视图,然后向所有视图发送初始化消息,确保每个视图的CView:: OnInitialUpdate()被调用。

可以调用CFrameWnd::SetActiveView()及CFrameWnd::GetActiveView()设置或取得活动视图。在设置活动视图后,应该将活动视图的ID切换为AFX_IDW_PANE_FIRST,因为有些操作是只针对第一视图的。例如,只有第一视图才能与控制条争夺主窗口客户区的空间,所以其他视图无法在主框架窗口中正常显示(如果不使用拆分)。

7.1.3  管理控制条
主框架窗口的直观特点是被丰富的控制条装饰的,如工具条、状态条等,它们都派生于基类CControlBar 。当鼠标移到工具条按钮或某菜单项区域时,相应的提示信息会在状态栏显示或以Tip形式弹出;没有建立消息映射的命令会自动禁止;客户区发生变化时视图和控制条会自动排列。这一切都是CFrameWnd封装的功能。下面列举几个重要的控制条操作函数。

q     EnableDocking():允许控制条在自己的客户区停靠。

q     DockControlBar():将控制条停靠在客户区周边。

q     FloatControlBar():将控制条浮动在屏幕上,而不是停靠在客户区。

q     ShowControlBar():显示或隐藏控制条。

q     SaveBarState()       :将所有控制条的状态存入初始化文件或注册表。

q     LoadBarState()       :从初始化文件或注册表中恢复所有控制条状态。

q     GetDockState()      :将控制条状态信息存入一个CDockState对象。

q     SetDockState()       :从一个CDockState对象中恢复控制条状态。

q     SetMessageText():在状态栏的第一个面板区域显示一个信息串。

q     RecalcLayout()       :虚函数,当控制条位置变化或客户区尺寸变化时被调用,重新设置视图及控制条在客户区的位置。可根据需要重载它或主动调用它。

7.1.4  分发命令消息
命令消息是指菜单、工具栏、加速键及命令按钮向其所在窗口发送的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()捕捉该类消息,会一无所获。

7.1.5  必要的消息处理
为了管理控制条和视图,CFrameWnd为几个窗口消息建立了消息映射,专门进行处理。下面列举几个消息处理函数。

q     OnInitMenuPopup()       :处理WM_INITMENUPOPUP消息,设置弹出菜单的各项目的启用/禁止状态。

q     OnEnterIdle():处理WM_ENTERIDLE消息,设置状态条的空闲时提示信息。

q     OnMenuSelect():处理WM_MENUSELECT消息,当某菜单项被选择时更新状态条提示。

q     OnToolTipText():处理TTN_NEEDTEXT通知消息,显示工具条的工具提示。

q     OnUpdateKeyIndicator()       :更新状态条的键盘状态指示器信息。

q     OnUpdateControlBarMenu():更新控制条的启用/禁止状态,如工具条按钮。

q     OnSize():处理WM_SIZE消息,调用RecalcLayout()排列客户区控件及视图。

q     OnHScroll():处理WM_HSCROLL消息,滚动视图。

q     OnVScroll():处理WM_VSCROLL消息,滚动视图。

OnClose()      :处理WM_CLOSE消息,存储并关闭文档。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值