窗口切換分割詳解

這裏寫一下窗口的切換於分割。一般這裏說的是單文檔界面或者多文檔界面的各種分割與切換。多文檔的作法和單文檔沒有什麼區別,這裏就以單文檔爲例。在本文最後我會列一個分割對話框的例子。這部份內容不是很少,在書上查得到的我就不詳細說了。

一般常用的MFC視窗結構是文檔/視窗結構(document/view architecture)。有很多人說這個結構浪費不少資源,不夠節約。但我覺得作到界面這一級浪費點資源沒什麼太大問題。只要不漏內存,不影響效率就已經足夠好了。何況這是微軟最推崇的標準界面。
文檔/視窗(document/view architecture)結構主要由四個class組成。document類,view類,framework類和app類。app類是程序的引擎,在MFC中是最不不要關心的一個類。framwork是窗口的框架,在程序運行開始的時候先生成框架,然後是document class,這裏是用來存儲數據的。然後是view類,用來顯示數據同時作數據交換的。單文檔界面只有一個document class,但可以有多了view class。至少有一個view class是active的。可以用GetActiveView()得到它的指針。沒個和document class 關聯的view class都有一個control ID,這個ID是一個整數。如果總共只顯示一個view class,這個class的control ID是AFX_IDW_PANE_FIRST,如果同時顯示好幾個view class就需要用分割器(splitter)割開。class 名字叫CSplitterWnd。CSplitterWnd有兩種不同的切割framework的方式。一種叫動態的,用Create()來實現,切的很不理想。沒見過多少class用這種切法。真正應用廣泛的是靜態切割,用CReateStatic實現。當然從名字上就可以看出靜態切割的缺點,就是不能動態重新切分。在本文中我會介紹一個可以實現靜態切割的程序。被分割器隔開的窗口的Control ID可以通過IdFromRowCol(row, col)函數得到,row和col是窗口的行數和列數。其數值也是在AFX_IDW_PANE_FIRST。也是一個比較大的數字。所以隱藏當前不想顯示的view時把他的control ID改成一個1,2,3之類的很小的數就可以了。

基本知識就說這些,肯定不夠詳細,大家可以參照Visual C++的各種教程找到詳細資料。下面開始說一些具體問題了。從單窗口開始。
1。在Framework中顯示一個View。通過菜單或按鈕切換成不同的view。假設有三種view: CViewA, CViewB,CViewC。用三個常數表示他們不顯示時的control ID.

enum eView {ViewA, ViewB, ViewC};
在CMainFrame加上下面一個函數就可以實現不同窗口的切換了。很易懂,唯一沒有說的就是CCreateContext context,這是每次Create一個view時必須設定的。其實也就是m_pCurrentDoc這個指向當前document class的指針需要設定,其它的取默認值就可以了。

void CMainFrame::SwitchToView(eView nView)
{
    CView* pOldActiveView = GetActiveView();
    CView* pNewActiveView = (CView*) GetDlgItem(nView);
    if (pNewActiveView == NULL)
    {
        switch (nView)
        {
        case ViewA:
            pNewActiveView = (CView*) new CViewA;
            break;
        case ViewB:
            pNewActiveView = (CView*) new CViewB;
            break;
        case ViewC:
            pNewActiveView = (CView*) new CViewC;
            break;
        }
        CCreateContext context;
        context.m_pCurrentDoc = pOldActiveView->GetDocument();
        pNewActiveView->Create(NULL, NULL, WS_BORDER|WS_CHILD,
            CFrameWnd::rectDefault, this, nView, &context);
        pNewActiveView->OnInitialUpdate();
    }

    SetActiveView(pNewActiveView);
    pNewActiveView->ShowWindow(SW_SHOW);
    pOldActiveView->ShowWindow(SW_HIDE);
    pOldActiveView->SetDlgCtrlID(m_nCurrentView);
    pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
    m_nCurrentView = nView;
    RecalcLayout();
}


2。顯示1個,2個或4個窗口。
需要用splitter class,這裏就不詳細說了,任何Visual C++書上都有。無論是Dynamic的還是Static的。

3。顯示1個,2個或4個窗口。同時窗口可以切換。
這裏只講靜態窗口的切換,動態的效果不是很好,用戶不想切的時候也會自動切。
靜態窗口的切換的效果就是Window Explorer那樣,左邊的目錄欄一點右面就跟着變了。這裏需要在已有的CSplitterWnd的基礎上寫一點小小的增強。需要一個切換功能。從CSplitterWnd繼承出一個class,例如叫CDynViewSplitter。

BOOL CDynViewSplitter::ReplaceView(int row, int col,CRuntimeClass * pViewClass,SIZE size)
{
  CCreateContext context;
  BOOL bSetActive;
          
  
   // 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;
}
這裏對用完了的view是destroy掉了,處理和第一種不大一樣。其它的沒什麼值得說的。


4。這是個以前沒有想過的問題,靜態窗口的重新切分,時分時合。由於有了上面兩個例子結合一下就可以了。需要知道的是CSplitterWnd在最開始切分窗口CreateStatic的時候不可以切成一行一列,也就是不切。CreateStatic一定要作真正的切割。這給整個問題帶來了不少麻煩。好在CSplitterWnd的員程序全都可以讀到,只有兩千多行。看一看construct之後作的事情的確很多,但desctructor很簡單,所以合併之前把自己的CSplitterWnd刪掉就可以了。下面是這個例子可以在當窗口CViewA,單窗口CViewB,雙窗口CViewMenu/CViewA之間互相切換,在窗窗口的時候還可以實現右邊窗口CViewA到CViewB的切換。



5。多個窗口的分割,不只1X1,1X2,2X1,2X2。可以分得十分複雜,比VC IDE上的窗口還多都可以。這時需要用多個Splitter。
6。對話框的切分,沒有標準的MFC class,需要自己寫一個。
5和6的例子我回頭加上。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章