一個單文檔界面中存在多個視圖,並且可以根據需要進行視圖的動態切換,這是當前比較流行的界面風格,它可以滿足許多用戶在操作和顯示方面的需要。這種界面風格的主要代表軟件是Outlook Express。下面歸納總結出兩種實現方法(這些代碼都在VC++ 6.0下調試通過)。
方法一:靜態創建切換法
步驟描述:
1.在窗口顯示之前先將需要切換的所有的視圖對象創建好,除首先顯示的視圖以外,其他在創建時都設置爲不可見屬性。
CMyWinApp::InitInstance() { ...... m_pViews[0] = pView1; m_pViews[1] = (CView*) new CView2; CDocument* pCurrentDoc = ((CFrameWnd*) m_pMainWnd)->GetActiveDocument(); // 初始化創建上下文相關指針 CCreateContext newContext; newContext.m_pNewViewClass = NULL; newContext.m_pNewDocTemplate = NULL; newContext.m_pLastView = NULL; newContext.m_pCurrentFrame = NULL; newContext.m_pCurrentDoc = pCurrentDoc; // 最初激活視的ID爲AFX_IDW_PANE_FIRST, //對新創建的視圖增加這個值,注意對CSplitterWnd不能這樣使用 UINT viewID[2]; viewID[1] = AFX_IDW_PANE_FIRST + 1; CRect rect(0, 0, 0, 0); for ( int nView=1; nView<NUMVIEWS; nView++ ) { // 創建新的視圖,創建的視圖在應用中永久存在,直到應用程序退出, //應用程序會自動刪除新創建的視圖 m_pViews[nView]->Create(NULL, NULL, (AFX_WS_DEFAULT_VIEW & ~WS_VISIBLE), // AFX_WS_DEFAULT_VIEW代表(WS_BORDER | WS_VISIBLE | WS_CHILD) rect, m_pMainWnd, viewID[nView], &newContext); } // 當文檔模板創建視圖的時候,會自動發送WM_INITIALUPDATE消息, //因此對於我們自己創建的視圖,需要人工發送這條消息 ((CForm2*)m_pViews[1])->OnInitialUpdate(); ((CVswapView*)m_pViews[2])->OnInitialUpdate(); ...... } |
2.視圖的切換
CView* CMyWinApp::SwitchView( UINT nIndex ) { ASSERT( nIndex >=0 && nIndex < NUMVIEWS ); CView* pNewView = m_pViews[nIndex]; CView* pActiveView =((CFrameWnd*) m_pMainWnd)->GetActiveView(); if ( !pActiveView ) // 當前沒有激活的視圖 return NULL; if ( pNewView == pActiveView ) // 當前視圖和需要切換的視圖相同 return pActiveView; // 交換視圖的窗口ID,使RecalcLayout()可以工作 UINT temp = ::GetWindowLong(pActiveView->m_hWnd, GWL_ID); ::SetWindowLong(pActiveView->m_hWnd, GWL_ID, ::GetWindowLong(pNewView->m_hWnd, GWL_ID)); ::SetWindowLong(pNewView->m_hWnd, GWL_ID, temp); // 顯示新的視圖,隱藏前一個視圖 pActiveView->ShowWindow(SW_HIDE); pNewView->ShowWindow(SW_SHOW); ((CFrameWnd*) m_pMainWnd)->SetActiveView(pNewView); ((CFrameWnd*) m_pMainWnd)->RecalcLayout(); pNewView->Invalidate(); return pActiveView; } |
方法二:動態創建切換法
步驟描述:
1.刪除當前的視圖
首先需要獲得當前視圖的指針,不能使用GetActiveView()和GetActiveDocument()這兩個函數,當前視圖有可能處在未激活狀態,
所以應該使用EnumChildWindows這個Win32API函數,函數定義如下:
BOOL EnumChildWindows( HWND hWndParent, // 父窗口的句柄 WNDENUMPROC lpEnumFunc, // 用戶自定義回調函數 LPARAM lParam // 傳給回調函數的自定義參數 ); 回調函數的定義如下: BOOL CALLBACK EnumChildProc( HWND hwnd, // 字窗口的句柄 LPARAM lParam // 自定義參數 ); |
EnumChildWindows函數遍歷父窗口的所有子窗口,遞歸調用用戶定義的回調函數,當回調函數返回FALSE時,停止遍歷,
至於何時返回FALSE,這根據用戶自己需要編寫的回調函數來決定。
刪除視圖使用DeleteWindow()這個函數,用delete也可以刪除,但還要其他底層的操作,這裏就不詳細介紹了,因爲刪除視圖使用DeleteWindow()最合適、方便了。在刪除視圖的時候還要注意不能將文檔同時自動刪除。
刪除視圖的代碼如下:
{ ...... CWnd* pWnd; CWnd* pWndToDelete; // 使用EnumChildWindows查找從CView繼承的子窗口 ::EnumChildWindows(m_hWnd, MyWndEnumProc, (LPARAM)&(pWnd)); if(pWnd == NULL) {// 沒有發現子窗口 return FALSE;} // 發現子窗口,找到級別最高的子窗口,即父窗口爲CMainFrame的窗口 while( lstrcmp(pWnd->GetRuntimeClass()->m_lpszClassName, ″CMainFrame″) ) { pWndToDelete = pWnd; pWnd = pWnd->GetParent(); } // 確保視圖被刪除時文檔不被刪除 pDoc->m_bAutoDelete = FALSE;} // 刪除視圖 |
用戶定義的回調函數:
BOOL CALLBACK MyWndEnumProc(HWND hWnd, LPARAM ppWndLPARAM) { CWnd* pWndChild = CWnd::FromHandlePermanent(hWnd); CWnd** ppWndTemp = (CWnd**)ppWndLPARAM; if( pWndChild && pWndChild->IsKindOf(RUNTIME_CLASS(CView)) ) { // 發現任何從CView繼承的子窗口,將子窗口指針傳遞出去 *ppWndTemp = pWndChild; // 停止繼續搜索 return FALSE; } else { *ppWndTemp = NULL; // 繼續搜索 return TRUE; }} |
2.創建新的視圖
CDocument* pCurrentDoc = ((CFrameWnd*) m_pMainWnd)->GetActiveDocument(); // 初始化創建上下文相關指針 CCreateContext newContext; newContext.m_pNewViewClass = RUNTIME_CLASS(CView1); newContext.m_pCurrentDoc = pCurrentDoc; newContext.m_pNewDocTemplate = NULL; newContext.m_pLastView = NULL; newContext.m_pCurrentFrame = NULL; CView* pNewView = STATIC_DOWNCAST(CView, CreateView(&newContext)); if( pNewView == NULL ) { return FALSE; } // 使用CreateView創建的視圖不能自動調用OnInitialUpdate函數,需要人工調用OnInitialUpdate函數或者發送WM_INITIALUPDATE消息 pNewView->OnInitialUpdate(); // 使用CreateView創建的視圖不會自動顯示並且激活,需要人工操作 pNewView->ShowWindow(SW_SHOW); SetActiveView(pNewView); RecalcLayout(); |
注:RUNTIME_CLASS宏含義
每一個從CObject類繼承的類,在定義DECLARE_DYNAMIC、DECLARE_DYNCREATE、DECLARE_SERIAL三個中任意一個宏時都會產生一個CRuntimeClass結構的靜態對象,RUNTIME_CLASS返回的就是這個對象的指針,這個對象包含了其基類和本身在運行時刻的信息。