Switching to other views in a doc-view application
Introduction
In a previous article, we discussed how to replace views in a document-view application. The views to replace were destroyed, and new views were created to replace them. Sometimes it's better to preserve the views and use them later rather than to destroy and recreate them. In this article, we show how to switch to other views without destroying the old ones, so we could use them later.
In the code below, the inactive views are disconnected from the document (so they don't receive
OnUpdate
messages) using CDocument::RemoveView
. When needed, they are reconnected to the document and resynchronized using CDocument::AddView
. In order to keep the inactive views synchronized with the document, we should have to implement custom hints (using the pHint
parameter in the CView::OnUpdate
member function). The views are hidden and shown using the CWnd::ShowWindow
member function.The Code
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 CMDIChildWnd
(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 should receive the new view as a parameter and return the view that was replaced. This returned view is not contained in the document's list anymore. 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. Let's start with an SDI application that doesn't t have splitters:CView* CMyDocument::SwitchToView ( CView* pNewView )
{
CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
CView* pOldActiveView = pMainWnd->GetActiveView();
ASSERT(pOldActiveView != NULL);
ASSERT_VALID(pOldActiveView);
ASSERT(pOldActiveView->GetDocument() == this); // must be attached to us
/* 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);
::SetWindowLong(pNewView->m_hWnd, GWL_ID, AFX_IDW_PANE_FIRST);
// Show the newly active view and hide the inactive view.
pNewView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
// Connect the newly active view to the document,
// and disconnect the inactive view
AddView(pNewView);
RemoveView(pOldActiveView);
pMainWnd->SetActiveView(pNewView);
pMainWnd->RecalcLayout();
return pOldActiveView;
}
In the case of an MDI application (again without splitters):CView* CMyDocument::SwitchToView ( CView* pNewView )
{
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();
// Set flag so that document will not be deleted when view is dettached.
BOOL bAutoDelete = m_bAutoDelete;
m_bAutoDelete = FALSE;
// Dettach existing view
RemoveView(pOldActiveView);
// restore flag
m_bAutoDelete = bAutoDelete;
// Show the newly active view and hide the inactive view.
pNewView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
// Attach new view
AddView(pNewView);
pChild->RecalcLayout();
pNewView->UpdateWindow();
pChild->SetActiveView(pNewView);
return pOldActiveView;
}
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:CView* CSDISplitDoc::SwitchToView ( CView* pNewView )
{
/* Uncomment this if this is a SDI application
CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
CView* pOldActiveView = pMainWnd->GetActiveView();
*/
/* Uncomment this if this is 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();
*/
CSplitterWnd* pSplitter = (CSplitterWnd *)pOldActiveView->GetParent();
int row, col;
ASSERT(pSplitter->IsChildPane(pOldActiveView, row, col));
// set flag so that document will not be deleted when view is destroyed
m_bAutoDelete = FALSE;
// Dettach existing view
RemoveView(pOldActiveView);
// set flag back to default
m_bAutoDelete = TRUE;
/* Set the child window ID of the active view to the ID of the corresponding
pane. Set the child ID of the previously active view to some other ID.
*/
::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0);
::SetWindowLong(pNewView->m_hWnd, GWL_ID, pSplitter->IdFromRowCol(row, col));
// Show the newly active view and hide the inactive view.
pNewView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
// Attach new view
AddView(pNewView);
// Set active
pSplitter->GetParentFrame()->SetActiveView(pNewView);
pSplitter->RecalcLayout();
pNewView->SendMessage(WM_PAINT);
return pOldActiveView;
}
The SwitchToView
functions above receive a pointer to an existing view, so a view must have already been created without attaching it to a document. Note that this imposes restrictions on view creation code, which should not make use of the document in any way (for example, the OnInitialUpdate
member function). Otherwise, exceptions might occur. The newly activated view is shown before it is attached to the document, so functions in the view that respond to Windows messages such as WM_SIZE
or WM_GETMINMAXINFO
should not make use of the document either.The view must be created with correct parent window and window ID. Both parameters depend on the frame windows containing the view, just the same as the
SwithToView
function. The non-active views could be created the first time the menu to select one of them was selected or somewhere in the document initialization code. Supposing we have a m_pView1
member in the document class that is a pointer to a view, this is how it should be created in a SDI application:if (!m_pView1)
{
// create the new view
m_pView1 = new CView1;
m_pView1->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CFrameWnd::rectDefault,
AfxGetMainWnd(), AFX_IDW_PANE_FIRST+1, NULL);
}
In a MDI application: CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();
// Get the active MDI child window.
CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive();
if (!m_pView1)
{
// create the new view
m_pView1 = new CView1;
m_pView1->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0),
pChild, AFX_IDW_PANE_FIRST, NULL);
}
And finally, if the view is a pane of a splitter window (read the comments to difference between SDI and MDI applications):/* Uncomment this if this is a SDI application
CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
CView* pActiveView = pMainWnd->GetActiveView();
CSplitterWnd* pSplitter = (CSplitterWnd *)pActiveView->GetParent();
*/
/* Uncomment this if this is a MDI application
CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();
CMDIChildWnd* pChild = (CMDIChildWnd*)pMainWnd->MDIGetActive();
CView* pActiveView = pChild->GetActiveView();
CSplitterWnd* pSplitter = (CSplitterWnd *)pActiveView->GetParent();
*/
if (!m_pView1)
{
// create the new view
m_pView1 = new CView1;
m_pView1->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
CRect(0, 0, 0, 0), pSplitter, 0, NULL);
}
When we already have an existing view ( m_pView1
in our example), we can make this view active as follows: CView* pOldActiveView = SwitchToView(m_pView1);
if (!pOldActiveView)
// there was not an active view
else
// pOldActiveView is a pointer to the now inactive view
Note that inactive views destroy themselves when their parent window is destroyed, so you don't have to worry about destroying them.Reprinted from: http://www.codeproject.com/KB/docview/switchingviews.aspx
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
콜백 함수를 Angular 하위 구성 요소에 전달이 예제는 구성 요소에 함수를 전달하는 것과 관련하여 최근에 직면한 문제를 다룰 것입니다. 국가 목록을 제공하는 콤보 상자 또는 테이블 구성 요소. 지금까지 모든 것이 구성 요소 자체에 캡슐화되었으며 백엔드에 대한 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.