1. 程式人生 > >視窗切換分割詳解

視窗切換分割詳解

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

一般常用的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的例子我回頭加上。