單文檔多視圖的創建

 

一個單文檔界面中存在多個視圖,並且可以根據需要進行視圖的動態切換,這是當前比較流行的界面風格,它可以滿足許多用戶在操作和顯示方面的需要。這種界面風格的主要代表軟件是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;}

  // 刪除視圖
   pWndToDelete->DestroyWindow();
   pDoc->m_bAutoDelete = TRUE;
   ......
   }

   用戶定義的回調函數:

   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返回的就是這個對象的指針,這個對象包含了其基類和本身在運行時刻的信息。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章