资源描述
word单文档多视的实现多视图是VC开发中经常要用到的技术之一,一般地实现单文档多视图有两种方式1通过视图分割的技术使用CSplitterWnd实现,将窗口分割为多个局部,每个局部显示各自显示不同的视图,这种技术实现起来比拟简单,并且相关的资料也很多。2通过一个文档关联多个视图,窗口显示整个视图。第二种实现较第一种复杂,这里给出详细的实现方法。Step 1:使用VC 6.0新建一个Project,命名为:MultiView。除选择单文档属性外,一切使用“默认方式。于是你可以获得五个类:CMainFrame,CMultiViewApp,CMultiViewDoc,CMultiViewView,和CAboutDlg;Step 2:新建一个新的视图View,添加一个新的MFC ClassInsertNew Class,基类为CView或者CView的派生子类,如CEditView等。类的名字为CAnotherView,这就是新的视图;并为CAnotherView添加GetDocument的实现:CMultiViewDoc* CAnotherView:GetDocument()return (CMultiViewDoc*)m_pDocument;Step 3:在CMultiViewApp添加成员变量记录这两个视图:private:CView* m_pFirstView;CView* m_pAnotherView;给程序菜单IDR_MAINFRAME添加一个菜单项目“视图,该菜单项有两个子菜单“视图一和“视图二,添加相应函数:voidCMultiViewApp:OnShowFirstview和voidCMultiViewApp:OnShowSecondview;注意这里将菜单消息的响应都放在app对象里面,而不是放到MainFrame对象中进展。Step 4:创建新的视图:在BOOLCMultiViewApp:InitInstance()中添加代码:./创建一个新的视图CView* m_pActiveView = (CFrameWnd*)m_pMainWnd)-GetActiveView();m_pFirstView = m_pActiveView;m_pAnotherView = new CAnotherView();/文档和视图关联CDocument* m_pDoc = (CFrameWnd*)m_pMainWnd)-GetActiveDocument();CCreateContext context;context.m_pCurrentDoc = m_pDoc;/创建视图UINT m_IDFORANOTHERVIEW = AFX_IDW_PANE_FIRST + 1;CRect rect;m_pAnotherView-Create(NULL,NULL,WS_CHILD,rect,m_pMainWnd,m_IDFORANOTHERVIEW,&context);Step 5:现在已经创建了视图,并且都和文档关联起来了。现在要作的就是视图间的转换。在voidCMultiViewApp:OnShowFirstview中添加实现代码:void CMultiViewApp:OnShowFirstview()/ TODO: Add your mand handler code hereUINT temp = :GetWindowLong(m_pAnotherView-m_hWnd, GWL_ID);:SetWindowLong(m_pAnotherView-m_hWnd, GWL_ID, :GetWindowLong(m_pFirstView-m_hWnd, GWL_ID);:SetWindowLong(m_pFirstView-m_hWnd, GWL_ID, temp);m_pAnotherView-ShowWindow(SW_HIDE);m_pFirstView-ShowWindow(SW_SHOW);(CFrameWnd*)m_pMainWnd)-SetActiveView(m_pFirstView);(CFrameWnd*) m_pMainWnd)-RecalcLayout();m_pFirstView-Invalidate();在voidCMultiViewApp:OnShowSecondview中添加实现代码:void CMultiViewApp:OnShowSecondview()/ TODO: Add your mand handler code hereUINT temp = :GetWindowLong(m_pAnotherView-m_hWnd, GWL_ID);:SetWindowLong(m_pAnotherView-m_hWnd, GWL_ID, :GetWindowLong(m_pFirstView-m_hWnd, GWL_ID);:SetWindowLong(m_pFirstView-m_hWnd, GWL_ID, temp);m_pFirstView-ShowWindow(SW_HIDE);m_pAnotherView-ShowWindow(SW_SHOW);(CFrameWnd*)m_pMainWnd)-SetActiveView(m_pAnotherView);(CFrameWnd*) m_pMainWnd)-RecalcLayout();m_pAnotherView-Invalidate();Step 6:为了演示,这里将不同的视图给予一个标记,在CMultiViewView和CAnotherView的OnDraw方法中分别添加以下代码:pDC-TextOut(400,300,First View);pDC-TextOut(400,320,pDoc-GetTitle();和pDC-TextOut(400,300,Another View);pDC-TextOut(400,320,pDoc-GetTitle();至此就大功告成了,但是实现过程中有4点说明:1实现中由于使用到相关的类,因此在必要的地方要include相关的头文件,这里省略;CAnotherView的默认构造函数是Protected的,需要将其改为Public,或者提供一个产生CAnotherView对象的方法因要创建视图对象;2这里给出的是一个示例代码,实际开发中可以通过参考实现获得自己想要实现的具体应用情况例如视图类的不同、数量不同,更重要的还有业务逻辑的不同实现等;3本文的示例代码已上传到Blog,可以通过下面的地址获得代码。MFC程序总体的架构CWinApp取代WinMain的地位。CFrameWnd取代WndProc的地位在MFC里面,似乎应该是在CWnd类里有一个WindowProc这个类,应该就是这个函数来作为窗口回调函数,因为所有的窗口回调函数都继承自CWnd类,CFrame类也是的。在MFC中,为了让程序更加结构化,更容易理解和维护,利用了两个宏来处理消息映射:DECLARE_MESSAGE_MAP和BEGIN_MESSAGE_MAP,见前面第三章。程序运行第一步:引爆器Application Object从这一节以下,将以此图解释MFC程序的激活,运行与完毕,不同小节的图将标示出当时的程序进展状况程序运行第二步:隐晦不明的WinMain主要动作:tWinMain调用AfxWinMain,AfxWinMain调用AfxWinInit在这个过程中, tWinMain的参数hInstance经过AfxWinMain传给AfxWinInit,然后在AfxWinInit中利用传进来的参数对theApp对象进展初始化利用全局函数AfxGetApp在AfxWinInit中除了对theApp对象进展初始化之处,还调用AfxInitThread函数对线程进展初始化。程序运行第三步:AfxWinInitAFX内部初始化动作程序运行第四步:CWinApp:InitApplication程序运行第五步:CMyWinApp:InitInstance程序运行第六步:CFramWnd:Create产生主窗口并先注册窗口类别小结:在MFC中,应用程序窗口的产生,必须经历窗口类的注册创建显示。这一系列的动作是通过theApp-InitInstance函数来组织调用的。PS:即在InitInstance里做了好多的工作,调用了相关的函数,这些函数就有窗口类的注册,窗口的创建和窗口的显示工作。窗口类的注册动作,MFC给我们提供的接口是各个窗口类的PreCreateWindow函数,BOOL CStudentInfoView:PreCreateWindow(CREATESTRUCT& cs)利用这个结构体参数,对窗口的样式进展设定,这个结构体如下:typedef struct tagCREATESTRUCT LPVOID lpCreateParams; HANDLE hInstance; HMENU hMenu; HWND hwndParent; int cy;int cx; int y; int x;LONG style; LPCSTR lpszName; LPCSTR lpszClass; DWORD dwExStyle; CREATESTRUCT;怎么让一个窗口居中显示?利用cy,cx参数?程序运行第七步:窗口显示与更新程序运行第八步:CWinApp:Run程序生命的活水源头把消息与处理函数串接在一起:Message Map机制消息流动是个颇为复杂的机制,它和Document/View,动态生成Dynamic Creation,文件读写Serialization一样,都是需要特别留心的地方。来龙去脉总整理MFC骨干程序关于文档视图结构的创建这里的所谓的MFC应用程序骨干,批的是由AppWizard产生出来的MFC程序,也就是像第四章所产生的Scribble step0那样的程序。不二法门:熟记MFC类别阶层架构MFC程序的UI新风貌Document/View支撑你的应用程序一个问题:文档关于Document/View的配合:为什么要用Document/View机制?骨干程序使用哪些MFC类别?应用程序各显身手的地方只是各个可能改写的虚拟函数。Document Template的意义为什么要有Document Template?Document Template对象的构造函数:Document Template构造函数的参数说明:对于我们的Scrible应用程序,它的相关代码如下:Document TemplateCDocTemplate管理CDocument/CView/CFrameWnd关于File/New与File/Open按钮动作的响应过程重要,重要,很重要,台面下的File/New动作。关键的技术是产生Document/View/Frame三口组总体概览:上图的灰色局部,正是Document Template动态产生“三位一体的Document/View/Frame的行动。注意这里的CWinApp选择适当的Document Tempate:由于打开一个文件需要依赖于Document Tempate,而Document Tempate实例创建是在CMultiViewApp:InitInstance()里:CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMultiViewDoc),RUNTIME_CLASS(CMainFrame), / main SDI frame windowRUNTIME_CLASS(CMultiViewView);AddDocTemplate(pDocTemplate);需要提供四样东西:其中第一样就是打开文档所需要的资源IDR_MAINFRAME,资源中包含了文档的后缀,文档类型的信息,所以当我们选择打开一个文件的时候,CSingleDocTemplate会自动根据文档的后缀类型来选择不同的CSingleDocTemplate实例进展三口组的创建CDocTemplate类如何创建三口组:File/New与File/Open消息的总结关于重要,重要,很重要!MFC主框架的类图:主要的类:CWinApp,CMainFrame, DocTemplete, CDocument, CView框架类之间的关系:CWinApp与CMainFrame与DocTemplete之间的关系:CWinApp有两个成员变量:CMainFrame和DocTemplete,在CWinApp被CMyApp所继承的OnInitInstance方法中:创建一个CSingleDocTemplate,将文档,框架窗口和视图联系了在一起,所以我们点文件打开一个文件的时候,它会显示这三口组将Doc放在View中,然后View再嵌入到Frame中,由于程序运行的时候就应该显示一个文档,而文档需要通过三口组来显示,所以这里在CWinApp的派生对象中需要有这么一个三口组数组作为成员变量。CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,/这里是RUNTIME_CLASS(CMultiViewDoc),RUNTIME_CLASS(CMainFrame), / main SDI frame windowRUNTIME_CLASS(CMultiViewView);AddDocTemplate(pDocTemplate);/这里将这个新生成的DocTemplet放到自己的DocTemplete列表中。/按照深入浅出的理论是在这个OnInitInstance中对CMainFrame成员变量进展了初始化调用构造函数,然后就引出了CMainFrame的初始化过程。/ The one and only window has been initialized, so show and update it.m_pMainWnd-ShowWindow(SW_SHOW);/将三口组给显示出来,而不是只是只显示一个框架m_pMainWnd-UpdateWindow();return TRUE;CDocument与CView之间的关系:是组合关系!而且是一对多的组合关系,一个CDocument可以有多个CView,而一个CView只能对应一个CDocument参见前面的单文档多视的实现中的第四步:Step 4:创建新的视图:在BOOLCMultiViewApp:InitInstance()中添加代码:./创建一个新的视图CView* m_pActiveView = (CFrameWnd*)m_pMainWnd)-GetActiveView();m_pFirstView = m_pActiveView;m_pAnotherView = new CAnotherView();/文档和视图关联CDocument* m_pDoc = (CFrameWnd*)m_pMainWnd)-GetActiveDocument();CCreateContext context;context.m_pCurrentDoc = m_pDoc;/创建视图UINT m_IDFORANOTHERVIEW = AFX_IDW_PANE_FIRST + 1;CRect rect;m_pAnotherView-Create(NULL,NULL,WS_CHILD,rect,m_pMainWnd,m_IDFORANOTHERVIEW,&context);注意这里的m_pAnotherView-Create(NULL,NULL,WS_CHILD,rect,m_pMainWnd,m_IDFORANOTHERVIEW,&context);/一个View的创建必须与一个CMainFrame和一个context(里面有Doc)相关联可见一个CView与CMainFrame, CDoc都有联系!CMultiViewView中的成员方法:CMultiViewDoc* CMultiViewView:GetDocument() / non-debug version is inlineASSERT(m_pDocument-IsKindOf(RUNTIME_CLASS(CMultiViewDoc);return (CMultiViewDoc*)m_pDocument;由于前面CView在创建的时候将CMainFrame和CDoc作为它创建的参数,所以这里m_pDocument;代表的是前面传递进来的文档的指针。看到了吧,只返回一个指针,所以应该是一个CView只对应一个CDoc。CMultiViewDoc类中的成员方法:POSITION CMultiViewDoc:GetFirstViewPosition() const/ TODO: Add your specialized code here and/or call the base classreturn CDocument:GetFirstViewPosition();CView* CMultiViewDoc:GetNextView(POSITION& rPosition) const/ TODO: Add your specialized code here and/or call the base classreturn CDocument:GetNextView(rPosition);由此可见一个CDoc可以有多个CViewCFrameWnd与CDoc之间的关系:对于单文档程序来说一个CFrameWnd只有一个CDoc,而多文档应用程序如此可以有多个CDocCFrameWnd中的成员方法:CDocument* CMainFrame:GetActiveDocument() / TODO: Add your specialized code here and/or call the base classreturn CFrameWnd:GetActiveDocument();一个CFrameWnd可以返回一个CDocCFrameWnd与CView之间的关系好似没有什么关系,不过可能通过CFrameWnd获得CDoc之后,然后再由CDoc获得CView对象。关于源码CWinApp:Public:/ AttributesCWnd* m_pMainWnd; / main window (usually same AfxGetApp()-m_pMainWnd)CWnd* m_pActiveWnd; / active main window (may not be m_pMainWnd)。CDocManager* m_pDocManager;/这个就是用来管理CDocTemplet一个实例代表的是一个三口组实例的了。CWnd源码:/ for child windows, views, panes etcvirtual BOOL Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,const RECT& rect,CWnd* pParentWnd, UINT nID,CCreateContext* pContext = NULL);前面的CView在创建的时候就利用了这个方法:m_pAnotherView-Create(NULL,NULL,WS_CHILD,rect,m_pMainWnd,m_IDFORANOTHERVIEW,&context);CView源代码:成员变量:protected:CDocument* m_pDocument;CDocument源代码:成员变量:protected:/ default implementation。CDocTemplate* m_pDocTemplate;CPtrList m_viewList; / list of viewsBOOL m_bModified; / changed since last savedCWinFrame源码:Protected:CView* m_pViewActive; / current active view源代码图解
展开阅读全文