1. The system used to create multiple document templates. When different views are displayed, the system calls the Response Document Template pointer to generate a view.
When switching, first obtain the current view and determine whether the view is to be switched. If not, destroy the view and create a new view.
There is a problem: the system is actually just a document, and the menu of the system framework is actually the same. Therefore, although the document template is generated using the same menu, the view is switched, causes the flashing of the menu. Therefore, you can create only one document template. When switching, You can dynamically modify the view corresponding to the document.
References
(1) http://www.codeproject.com/docview/inplacereplacingview.asp switching View
<P nD = "1"> to fully understand what follows, some previous knowledge is required,
Namely how MFC creates documents, frame windows and views using document
Templates and how their relationship is maintained in the program. A <
Href = "http://www.codeproject.com/docview/ReplacingView.asp"> previous article </a>
Showing how to replace views in non-ole applications is a recommended
Reading. </P>
<P nD = "2"> first of all, let's understand how the framework creates Ole frames.
When a <a class = IAS
Style = "font-weight: normal; font-size: 100%; padding-bottom: 1px; color: darkgreen; border-bottom: darkgreen 0.07em solid; Background-color: transparent; text-Decoration: underline"
Href = "#" target = _ blank itxtdid = "3675733"> server </a> object is inserted in
Container, a document, a frame window and a view contained within it (or
Multiple views, if it is a splitter) are first constructed, following common
Document-template procedures. After that, in response to the <code
ND = "3"> oleiverb_show </code> OLE verb, the <code
ND = "4"> coleserverdoc: activateinplace </code> function is called. If there is no
Previous Ole frame (I. e. It is the first time it will be activated), the Ole
Frame is created, calling the <code
ND = "5"> coleserverdoc: createinplaceframe </code> function. Here, not only the Ole
Frame is created (using the <code nD = "6"> cdoctemplate: createoleframe </code>
Function), but also the view in the previusly constructed frame window is
"Connected" to the newly created Ole frame. If the original frame was
Splitter, then the view in the first pane is used. The "connection" Process
Consists on changing the view's parent window to the new frame. It is saved med
Calling the <code nD = "7"> coleserverdoc: connectview </code> function. The active
View on the original frame window is kept (using the <code
ND = "8"> m_pactiveview </code> member), because some controls continue to send
Notification messages to their original parent window. These Notification
Messages shoshould be handled in the original frame window. Once the OLE frame is
Created and a view is "connected" to it, then it is activated. </P>
<P nD = "9"> now, if we want to replace a view in an ole frame, we have to replace
It In 2 frame windows: The OLE frame and the original frame. The current active
View shocould be destroyed, a new view shocould be created within the original frame
And be later connected to the OLE frame. The active views shocould be set to <code
ND = "10"> null </code> first, so no windows message cocould be sent by the Framework
To a non-valid view stored in the <code nD = "11"> m_pactiveview </code> member
The frame windows. </P>
<P nD = "12"> to perform this, we need a method in our document class, which shoshould
Receive the new view type as a parameter and return a success flag.
Advantage of having this method in the document class becomes obvious when there
Are several document types each of which can have different view types. Note
That the document shoshould be a <code nD = "13"> coleserverdoc </code> descendant.
This is how it is done: </P>
<Div class = precollapse id = premain0 style = "width: 100%"> Style = "cursor: Hand" Height = 9 src = "http://www.codeproject.com/images/minus.gif"
Width = 9 preid = "0"> collapse </div> <PRE id = pre0 style = "margin-top: 0px" nD = "15"> bool cmydoc :: inplaceswitchtoview (cruntimeclass * pnewviewclass)
{
// The original parent window is saved in m_porigparent.
Cframewnd * pframe = (cframewnd *) m_porigparent;
// Get the active view.
View * poldactiveview = m_pinplaceframe-> getactiveview ();
// If we're already displaying this kind of view, no need to go further.
If (poldactiveview-> iskindof (pnewviewclass ))
Return true;
// No currently active views in either Frame
Pframe-> setactiveview (null );
M_pinplaceframe-> setactiveview (null );
// Set flag so that document will not be deleted when view is destroyed.
Bool bautodelete = m_bautodelete;
M_bautodelete = false;
// Delete existing View
Poldactiveview-> destroywindow ();
// Restore flag
M_bautodelete = bautodelete;
// Create new view.
Cview * pnewview = (cview *) pnewviewclass-> Createobject ();
If (pnewview = NULL)
{
Trace1 ("Warning: Dynamic create of view type % FS failed/N ",
Pnewviewclass-> m_lpszclassname );
Return false;
}
// Draw new view.
Ccreatecontext context;
Context. m_pnewviewclass = pnewviewclass;
Context. m_pcurrentdoc = this;
Context. m_pnewdoctemplate = NULL;
Context. m_plastview = NULL;
Context. m_pcurrentframe = pframe;
If (! Pnewview-> Create (null, null, afx_ws_default_view, crect (0, 0, 0, 0 ),
Pframe, afx_idw_pane_first, & context ))
{
Trace0 ("Warning: couldn't create view for frame/N ");
Delete pnewview;
Return false;
}
// Keep the view active in the original frame
Pframe-> setactiveview (pnewview );
// Connect the view to the OLE Frame
Connectview (m_pinplaceframe, pnewview );
Pnewview-> modifystyleex (ws_ex_clientedge, 0, swp_drawframe );
Pnewview-> sendmessage (wm_initialupdate, 0, 0 );
// Wm_initialupdate is defined in afxpriv. h
Pnewview-> updatewindow ();
Return true;
} </PRE>
(2) http://www.codeproject.com/docview/replacingview.asp
Introduction
Sometimes the way a document is being visualized needs to be significantly changed. for example, in PowerPoint, you can see the slides in a WYSIWYG form or view only the text in a text edit window. in the MFC world, one can find an amazingly large quantity of programs that implement this behavior defining one cview descendant and making it responsible for all visualization changes. this path has several disadvantages:
- Big, difficult to manage class definition file.
- Diminished reusability: You cocould reuse one big cview descendant or nothing.
- Hard to maintain (what if you want to modify or add a new "look" to the document ?).
- Wasted memory: some variables (objects) will exist in memory and won't be used.
For an application using the MFC Document-view architecture, it is more appropriate to define different view classes and switch between them when necessary. this shall overcome all the disadvantages listed before. there probably will be some features common to all views for the same type of document, so it is a good idea to have a direct cview descendant that implement all the functionality common to all view types. the views used by the document shocould be descendants of this class ("grandchildren" of cview ).
The code needed to implement view switching depends on the frame window containing the view. there are three common cases: the view is contained within a cframewnd (SDI application), the view is contained within a brief ichildwnd (MDI application) and the view is a pane of a splitter window, either in SDI or MDI applications. in all cases what we need is a method in our document class to switch to the desired view. this method shoshould receive the new view type as a parameter and return a success flag. the advantage of having this method in the document class becomes obvious when there are several document types each of which can have different view types. lets start with an SDI application that doesn' t have splitters:
Collapse
BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass){ CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd(); CView* pOldActiveView = pMainWnd->GetActiveView(); // If we're already displaying this kind of view, no need to go further. if (pOldActiveView->IsKindOf(pNewViewClass)) return TRUE; // Set the child window ID of the active view to AFX_IDW_PANE_FIRST. // This is necessary so that CFrameWnd::RecalcLayout will allocate // this "first pane" to that portion of the frame window's client // area not allocated to control bars. Set the child ID of // the previously active view to some other ID. ::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0); // create the new view CCreateContext context; context.m_pNewViewClass = pNewViewClass; context.m_pCurrentDoc = this; CView* pNewView = STATIC_DOWNCAST(CView, pMainWnd->CreateView(&context)); if (pNewView != NULL) { // the new view is there, but invisible and not active... pNewView->ShowWindow(SW_SHOW); pNewView->OnInitialUpdate(); pMainWnd->SetActiveView(pNewView); pMainWnd->RecalcLayout(); // destroy the old view... pOldActiveView->DestroyWindow(); return TRUE; } return FALSE;}
In the case of an MDI Application (again without splitters ):
Collapse
BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass){ CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd(); // Get the active MDI child window. CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive(); // Get the active view attached to the active MDI child window. CView* pOldActiveView = pChild->GetActiveView(); // If we're already displaying this kind of view, no need to go further. if (pOldActiveView->IsKindOf(pNewViewClass)) return TRUE; // Set flag so that document will not be deleted when view is destroyed. BOOL bAutoDelete = m_bAutoDelete; m_bAutoDelete = FALSE; // Delete existing view pOldActiveView->DestroyWindow(); // restore flag m_bAutoDelete = bAutoDelete; // Create new view. CView* pNewView = (CView *)pNewViewClass->CreateObject(); if (pNewView == NULL) { TRACE1("Warning: Dynamic create of view type %Fs failed/n", pNewViewClass->m_lpszClassName); return FALSE; } // Draw new view. CCreateContext context; context.m_pNewViewClass = pNewViewClass; context.m_pCurrentDoc = this; context.m_pNewDocTemplate = NULL; context.m_pLastView = NULL; context.m_pCurrentFrame = pChild; if (!pNewView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), pChild, AFX_IDW_PANE_FIRST, &context)) { TRACE0("Warning: couldn't create view for frame/n"); delete pNewView; return FALSE; } // WM_INITIALUPDATE is defined in afxpriv.h pNewView->SendMessage(WM_INITIALUPDATE, 0, 0); pChild->RecalcLayout(); pNewView->UpdateWindow(); pChild->SetActiveView(pNewView); return TRUE;}
When the view to replace is a pane of a splitter window, there is also a small difference between SDI and MDI applications, related to the retrieval of the current active view. in the method below you must comment out what you don't need depending on your application type:
Collapse
BOOL CMyDocument::SwitchToView(CRuntimeClass* pNewViewClass){/* Uncomment this if this is a SDI application CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd(); CView* pOldActiveView = pMainWnd->GetActiveView();*//* Uncomment this if this a MDI application CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd(); // Get the active MDI child window. CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive(); // Get the active view attached to the active MDI child window. CView* pOldActiveView = pChild->GetActiveView();*/ // If we're already displaying this kind of view, no need to go further. if (pOldActiveView->IsKindOf(pNewViewClass)) return TRUE; CSplitterWnd* pSplitter = (CSplitterWnd *)pOldActiveView->GetParent(); int row, col; ASSERT(pSplitter->IsChildPane(pOldActiveView, row, col)); CRect viewrect; pOldActiveView->GetWindowRect(&viewrect); // set flag so that document will not be deleted when view is destroyed m_bAutoDelete = FALSE; // Delete existing view pOldActiveView->DestroyWindow(); // set flag back to default m_bAutoDelete = TRUE; // Create new view CCreateContext context; context.m_pNewViewClass = pNewViewClass; context.m_pCurrentDoc = this; context.m_pNewDocTemplate = NULL; context.m_pLastView = NULL; context.m_pCurrentFrame = NULL; if (!pSplitter->CreateView(row, col, pNewViewClass, viewrect.Size(), &context)) return FALSE; // Set active CView* pNewView = (CView *)pSplitter->GetPane(row, col); pSplitter->GetParentFrame()->SetActiveView(pNewView); pSplitter->RecalcLayout(); pNewView->SendMessage(WM_PAINT); return TRUE;}
Now that we have a method in our document class that will replace the current view, lets use it. the new view type shoshould be decided (in response to a menu selection, for instance), and the function must be called as follows:
CRuntimeClass* pNewViewClass = RUNTIME_CLASS(CMyView); if (!SwitchToView(pNewViewClass)) // failed else // succeeded
One final word to the Class Wizard fans. when you have a descendant of a cview descendant, the Class Wizard won't allow you to edit this class. to change this behavior, change all Class Wizard comments replacing the name of your direct cview descendant with cview. class Wizard will now work.