A2 Как и указывается в вопросе, эту задачу можно решать различными способами. Если виды переключаются часто, логичнее создать их один раз, а затем показывать один из них и скрывать все остальные. Если их переключают редко, имеет смысл создавать их динамически. Рассмотрим оба варианта.
Способ 1.
Сначала создаём все нужные нам виды. Поскольку в нормальном SDI-приложении вид, прописанный в шаблоне документа, создаётся в функции CFrameWnd::OnCreateClient, вполне естественно создать в ней и все остальные виды. Для этой цели удобно воспользоваться функцией CFrameWnd::CreateView, которая вызовет за нас и CView::CreateObject, и CView::Create с нужными параметрами. Всё, что требуется от нас – это подменить член m_pNewViewClass в структуре CCreateContext, указатель на которую передаётся в OnCreateClient. Выглядит это следующим образом.
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) {
CRuntimeClass *ppFormViewClasses[] = {
RUNTIME_CLASS(CFormView1),
RUNTIME_CLASS(CFormView2)
// :
};
for(int i=0; i < sizeof(ppFormViewClasses)/sizeof(CRuntimeClass*); i++) {
pContext->m_pNewViewClass = ppFormViewClasses[i];
if (!CreateView(pContext, AFX_IDW_PANE_FIRST+i+1))
return FALSE;
}
SwitchView(AFX_IDW_PANE_FIRST+1);
return TRUE;
}
Чтобы использовать этот фрагмент в реальной программе, нужно подставить правильные имена классов в массив ppFormViewClasses. Функция SwitchView добавляется в класс главного окна приложения и используется каждый раз, когда нужно переключиться с одного вида на другой. Она получает идентификатор нужного вида (в приведённом выше коде виды получают идентификаторы от AFX_IDW_PANE_FIRST+1 до AFX_IDW_PANE_FIRST, где N – число видов, но это поведение легко изменить на любое другое). Её код может выглядеть так:
void CMainFrame::SwitchView(UINT ID) {
if(ID == m_CurID) return;
CView *pOldView;
CView *pNewView;
pOldView = GetActiveView;
pNewView = (CView *)GetDlgItem(ID);
if (pOldView) {
pOldView->SetDlgCtrlID(m_CurID);
pOldView->ShowWindow(SW_HIDE);
}
m_CurID = ID;
pNewView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
pNewView->ShowWindow(SW_SHOW);
SetActiveView(pNewView);
RecalcLayout;
RecalcLayout;
}
Чтобы эта функция работала правильно, необходимо добавить переменную m_CurID типа UINT в класс главного окна. В ней временно хранится идентификатор текущего вида, поскольку при переключении он заменяется на AFX_IDW_PANE_FIRST. Это необходимо, так как функции класса CFrameWnd во многих местах предполагают, что идентификатор активного вида именно такой.
Способ 2.
Его реализация проще. Допустим, нам требуется создать для просмотра документа вид класса CSomeView. Для этого мы уничтожаем текущий вид, создаём новый и перерисовываем экран. Функцию, которая делает всё это, можно вставить в класс главного окна. Выглядит она так:
void CMainFrame::ReplaceView(CRuntimeClass *pRuntimeClass) {
CView *pActiveView = GetActiveView;
if(pActiveView->IsKindOf(pRuntimeClass)) return;
CCreateContext context;
context.m_pCurrentDoc = GetActiveView->GetDocument;
context.m_pNewViewClass = pRuntimeClass;
context.m_pCurrentFrame = this;
SetActiveView((CView *)CreateView(&context, AFX_IDW_PANE_FIRST));
pActiveView->DestroyWindow;
RecalcLayout;
}
Эта функция вызывается следующим образом:
ReplaceView(RUNTIME_CLASS(CSomeView));
Обратите внимание, что во втором случае функция OnInitialUpdate будет вызвана только для первого вида (который прописан в шаблоне документа). Если она выполняет важные действия и в других классах, можно добавить её вызов в функцию ReplaceView.
В заключение отмечу, что я нигде не использовал тот факт, что виды порождаются от CFormView. Поэтому описанные мною способы годятся для любых других видов (например, порождённых от CScrollView или CListView).