olegich
A 1В начале немного теории.
В технологии Документ/Представление у документа (class CDocument) может быть несколько представлений (class CView и его наследники), которые в свою очередь находяться (отображаются) во Фреймах (CFrameWnd). Представление может отображаться только в одном Фрейме, но во Фрейме могут отображаться несколько представлений.
Как я понял вопрос был по поводу динамического переключения Представлений (т.е. класса CView и/или любого его наследника, которым и является класс CFormView) внутри одного Фрейма. Библиотека MFC прямого решения не предоставлет, но тем не менее такая возможность есть. MFC каждому представлению присваивает идентификатор от AFX_IDW_PANE_FIRST до AFX_IDW_PANE_LAST (всего 256 вчем легко убедиться посмотрев их значения) но на экран отображается только Представление с ID=AFX_IDW_PANE_FIRST. Поэтому задача сводиться к корретному изменению ID.
Теперь практика. Пусть имеется готовое SDI приложение (с технологией Документ/Представление). Создаем дополнительное Представление. Это делается в функции CFrameWnd::OnCreateClient примерно так:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) {
// class CNewView – это наше новое представление
pContext->m_pNewViewClass = RUNTIME_CLASS(CNewView);
// обратите внимание на идентификатор нового Представления
// переменная m_pNewView описана в CMainFrame как CNewView* m_pNewView;
m_pNewView = STATIC_DOWNCAST(CNewView, CreateView(pContext, AFX_IDW_PANE_FIRST+1));
m_pNewView->ShowWindow(SW_HIDE); // для сброса флага WS_VISIBLE
return CFrameWnd::OnCreateClient(lpcs, pContext);
}
Теперь нам нужна функция переключения активного Представления на Представление только что нами созданное.
void CMainFrame::SwitchView(CView *pView) {
CFormView *pNewView=STATIC_DOWNCAST(CFormView, pView),
*pActiveView=STATIC_DOWNCAST(CFormView, GetActiveView());
if (pNewView!=NULL && pActiveView!=NULL) {
UINT tempID=::GetWindowLong(pActiveView->GetSafeHwnd(), GWL_ID);
::SetWindowLong(pActiveView->GetSafeHwnd(), GWL_ID, ::GetWindowLong(pNewView->GetSafeHwnd(), GWL_ID));
::SetWindowLong(pNewView->GetSafeHwnd(), GWL_ID, tempID);
pActiveView->ShowWindow(SW_HIDE);
pNewView->ShowWindow(SW_SHOW);
pNewView->OnInitialUpdate();
SetActiveView(pNewView);
RecalcLayout(); // можно делать можно и не делать
pNewView->Invalidate();
}
}
Похожий способ описан в Microsoft Knowledge Base (Q141334 – проект VSWAP32).
Ilya Zharkov
A 2Как и указывается в вопросе, эту задачу можно решать различными способами. Если виды переключаются часто, логичнее создать их один раз, а затем показывать один из них и скрывать все остальные. Если их переключают редко, имеет смысл создавать их динамически. Рассмотрим оба варианта.
Способ 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 во многих местах предполагают, что идентификатор активного вида именно такой.
Читать дальше