今天在我的項目中遇到這樣的需求,我想在點擊工具欄某個自定義的視圖切換按鈕時,靜態切分窗口中的某個視圖被一個新視圖替換(例如:CFormView被CEditView替換)。我開始的想法是:在那個視圖按鈕被點擊的消息響應函數中發送WM_CREATE消息,引發OnCreate,然後是如下的調用次序:OnCreateHelper——OnCreateClient——CreateView,經過實踐發現只有第一次切換時,WM_CREATE消息被髮送並得到響應,後來切換就不靈了!
最初的想法往往是最不成熟的,後來又在CodeProject上面找到別人重寫的CSplitterWnd類,可以replace某個視圖,於是乎直接拿來用了,代碼如下:
BOOL CUsefulSplitterWnd::ReplaceView(int row, int col,CRuntimeClass * pViewClass,SIZE size)
{
CCreateContext context;
BOOL bSetActive;
if ((GetPane(row,col)->IsKindOf(pViewClass))==TRUE)
return FALSE;
// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDocument();
CView * pActiveView=GetParentFrame()->GetActiveView();
if (pActiveView==NULL || pActiveView==GetPane(row,col))
bSetActive=TRUE;
else
bSetActive=FALSE;
// set flag so that document will not be deleted when view is destroyed
pDoc->m_bAutoDelete=FALSE;
// Delete existing view
((CView *) GetPane(row,col))->DestroyWindow();
// set flag back to default
pDoc->m_bAutoDelete=TRUE;
// Create new view
context.m_pNewViewClass=pViewClass;
context.m_pCurrentDoc=pDoc;
context.m_pNewDocTemplate=NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NULL;
CreateView(row,col,pViewClass,size,&context);
CView * pNewView= (CView *)GetPane(row,col);
if (bSetActive==TRUE)
GetParentFrame()->SetActiveView(pNewView);
RecalcLayout();
GetPane(row,col)->SendMessage(WM_PAINT);
return TRUE;
}
但是又出現了新的問題,視圖切換後不能被初始化,即OnInitialUpdate函數沒有被執行。調試運行,進入了CSplitterWnd::CreateView(int row, int col,CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)發現如下代碼:
BOOL bSendInitialUpdate = FALSE;
CCreateContext contextT;
if (pContext == NULL)
{
// if no context specified, generate one from the currently selected
// client if possible
CView* pOldView = (CView*)GetActivePane();
if (pOldView != NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
{
// set info about last pane
ASSERT(contextT.m_pCurrentFrame == NULL);
contextT.m_pLastView = pOldView;
contextT.m_pCurrentDoc = pOldView->GetDocument();
if (contextT.m_pCurrentDoc != NULL)
contextT.m_pNewDocTemplate =
contextT.m_pCurrentDoc->GetDocTemplate();
}
pContext = &contextT;
bSendInitialUpdate = TRUE;
}
........
if (bSendInitialUpdate)
pWnd->SendMessage(WM_INITIALUPDATE);
如果pContext 爲空則發送WM_INITIALUPDATE初始化,反之則不發送。下面是摘自MSDN中的一段:
When passed as an argument for window creation, as in CWnd::Create, CFrameWnd::Create, and CFrameWnd::LoadFrame, the create context specifies what the new window should be connected to. For most windows, the entire structure is optional and a NULL pointer can be passed.
也就是說MFC通過pContext 來判斷是否是第一次創建該視圖,如果是則初始化,不是則不初始化。所以我們可以把上面ReplaceView函數代碼稍微改一下,把CreateView(row,col,pViewClass,size,&context);改爲CreateView(row,col,pViewClass,size,NULL);即可。再次運行程序,通過!o(∩_∩)o...哈哈