在用 Microsoft 基础类库 (MFC) 创建的单文档接口 (SDI) 应用程序中,每个文档类型与单个视图类型关联。在一些情况中,最好具有在当前文档视图与新视图之间切换的能力。
提示 有关实现单文档的多个视图的其他过程,请参见 tabindex="0" keywords="_mfc_CDocument.3a3a.AddView"> CDocument::AddView 和下列 MFC 示例: tabindex="0" keywords="_sample_mfc_ENROLL">ENROLL 和 tabindex="0" keywords="_sample_mfc_COLLECT">COLLECT。
通过添加新的 CView 派生类和用于将视图动态切换到现有 MFC 应用程序的附加代码,可以实现此功能。
步骤如下:
- 修改现有应用程序类
- 创建和修改新视图类
- 创建并附加新视图
- 实现切换函数
- 添加视图切换支持
本主题的其余部分假定下列情况:
- CWinApp 派生的对象名是
CMyWinApp
,并且CMyWinApp
在 MYWINAPP.H 和 MYWINAPP.CPP 中声明和定义。 CNewView
是新的 CView 派生对象名,并且CNewView
在 NEWVIEW.H 和 NEWVIEW.CPP 中声明和定义。
修改现有应用程序类
为使应用程序能在视图间切换,需要修改应用程序类,方法是添加存储视图的成员变量和添加切换视图的方法。
将下列代码添加到 MYWINAPP.H 中的 CMyWinApp
声明:
CView* m_pOldView; CView* m_pNewView; CView* SwitchView( );
新成员变量 m_pOldView
和 m_pNewView
指向当前视图和新创建的视图。新方法 (SwitchView
) 在用户请求时切换视图。
对应用程序类的最后修改要求包括一个新的头文件来定义用在切换函数中的 Windows 消息 (WM_INITIALUPDATE)。
将下行插到 MYWINAPP.CPP 的 include 部分:
#include <AFXPRIV.H>
保存所做更改后继续进行下一步操作。
创建和修改新视图类
通过使用类视图中可用的新建类命令使创建新视图类变得容易。对此类的唯一要求是它从 CView 派生。将此新类添加到应用程序。
将类添加到项目后,需要更改一些视图类成员的可访问性。
修改 NEWVIEW.H,将构造函数和析构函数的访问说明符从 protected 更改为 public。这使得可以动态创建和销毁类,并且允许类在视图可见前修改视图外观。
保存所做更改后继续进行下一步操作。
创建并附加新视图
若要创建和附加新视图,需要修改应用程序类的 InitInstance 函数。该修改添加创建新视图对象的新代码,然后用现有的两个视图对象初始化 m_pOldView
和 m_pNewView
。
因为新视图是在 InitInstance 函数内创建的,因此新视图和现有视图在应用程序的生存期内一直保持。但是,应用程序可以很容易地动态创建新视图。
在调用 ProcessShellCommand
后插入此代码:
... CView* pActiveView = ((CFrameWnd*) m_pMainWnd)->GetActiveView(); m_pOldView = pActiveView; m_pNewView = (CView*) new CNewView; CDocument* pCurrentDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument(); // Initialize a CCreateContext to point to the active document. // With this context, the new view is added to the document // when the view is created in CView::OnCreate(). CCreateContext newContext; newContext.m_pNewViewClass = NULL; newContext.m_pNewDocTemplate = NULL; newContext.m_pLastView = NULL; newContext.m_pCurrentFrame = NULL; newContext.m_pCurrentDoc = pCurrentDoc; // The ID of the initial active view is AFX_IDW_PANE_FIRST. // Incrementing this value by one for additional views works // in the standard document/view case but the technique cannot // be extended for the CSplitterWnd case. UINT viewID = AFX_IDW_PANE_FIRST + 1; CRect rect(0, 0, 0, 0); // Gets resized later. // Create the new view. In this example, the view persists for // the life of the application. The application automatically // deletes the view when the application is closed. m_pNewView->Create(NULL, "AnyWindowName", WS_CHILD, rect, m_pMainWnd, viewID, &newContext); // When a document template creates a view, the WM_INITIALUPDATE // message is sent automatically. However, this code must // explicitly send the message, as follows. m_pNewView->SendMessage(WM_INITIALUPDATE, 0, 0); ...
保存所做更改后继续进行下一步操作。
实现切换函数
在上一步中,您添加了创建和初始化新视图对象的代码。最后一步的主要工作是实现切换方法 SwitchView
。
在应用程序类 (MYWINAPP.CPP) 的实现文件的末尾,添加下列方法定义:
CView* CMyWinApp::SwitchView( ) { CView* pActiveView = ((CFrameWnd*) m_pMainWnd)->GetActiveView(); CView* pNewView= NULL; if(pActiveView == m_pOldView) pNewView= m_pNewView; else pNewView= m_pOldView; // Exchange view window IDs so RecalcLayout() works. #ifndef _WIN32 UINT temp = ::GetWindowWord(pActiveView->m_hWnd, GWW_ID); ::SetWindowWord(pActiveView->m_hWnd, GWW_ID, ::GetWindowWord(pNewView->m_hWnd, GWW_ID)); ::SetWindowWord(pNewView->m_hWnd, GWW_ID, temp); #else UINT temp = ::GetWindowLong(pActiveView->m_hWnd, GWL_ID); ::SetWindowLong(pActiveView->m_hWnd, GWL_ID, ::GetWindowLong(pNewView->m_hWnd, GWL_ID)); ::SetWindowLong(pNewView->m_hWnd, GWL_ID, temp); #endif pActiveView->ShowWindow(SW_HIDE); pNewView->ShowWindow(SW_SHOW); ((CFrameWnd*) m_pMainWnd)->SetActiveView(pNewView); ((CFrameWnd*) m_pMainWnd)->RecalcLayout(); pNewView->Invalidate(); return pActiveView; }
保存所做更改后继续进行下一步操作。
添加视图切换支持
最后一步是添加当应用程序需要在视图间切换时调用 SwitchView
方法的代码。该操作可以通过几种方式来完成:添加供用户选择的新菜单项,或在符合某些条件时从内部切换视图。