使用CSplitterWnd實現拆分窗口(多視圖顯示)


MFC支持兩種類型的拆分窗口:靜態的和動態的。

靜態拆分窗口的行列數在拆分窗口被創建時就設置好了,用戶不能更改。但是用戶可以縮放各行各列。一個靜態拆分窗口最多可以包含16行16列。

要找一個使用了靜態拆分窗口的應用程序,只要看一下windows管理器即可。

動態拆分窗口最多可以有兩行兩列,但它們可以相互拆分和合並。Vc就使用了動態拆分窗口使得可以同時編輯源程序文件的兩個以上不同的部分。

選擇靜態或動態拆分的一個准則是是否希望用戶能夠交互地修改拆分窗口的行列配置。另一個決定因素是計划在拆分窗口中使用的視圖種類。

在靜態拆分窗口中很容易使用兩個以上不同種類的視圖,因為您可以在每個窗格中指定所用的視圖類型。但是在動態拆分窗口中,MFC管理着視圖,

除非從 CsplitterWnd派生一個新類並修改拆分窗口的默認操作性能,否則拆分窗口中的所有視圖使用的都是相同的視圖類。

靜態拆分窗口是用CsplitterWnd::CreateStatic而不是CsplitterWnd::Create創建,並且由於MFC不會自動創建靜態拆分窗口中顯示的視圖,

所以您要親自在CreateStatic返回之后創建視圖。CsplitterWnd為此提供了名為 CreateView的函數。

你應按如下步驟創建一個CSplitterWnd對象:

       1. 在父框架中嵌入一個CSplitterWnd成員變量。

  2. 重載父框架的CFrameWnd::OnCreateClient成員函數。

  3. 從重載的OnCreateClient函數中調用類CSplitterWnd的Create或CreateStatic成員函數,並調用CreateView來創建視圖。

 

使用靜態拆分窗口的一個優點是由於您自己給窗格添加視圖,所以可以控制放入視圖的種類。

關鍵函數

BOOL CreateStatic( CWnd* pParentWnd, int nRows,int nCols, DWORD dwStyle = WS_CHILD | WS_VISIBLE, UINT nID = AFX_IDW_PANE_FIRST );

 函數有5個參數,意義如下:

  ● pParentWnd:切分窗口的父窗口指針

  ● nRows:水平方向分隔窗口的數目

  ● nCols:垂直方向分隔窗口的數目

  ● dwStyle:切分窗口的風格

  ● nID:子窗口的ID值,默認為系統定義的AFX_IDW_PANE_FIRST

  返回值:如果創建成功,返回非零值(TRUE),否則返回0(FALSE)。

     m_wndSplitter.CreateStatic(this, 2,1);// 切分為2行1列

 

  virtual BOOL CreateView( int row, int col, CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext );

 

  函數有5個參數,意義如下:

  ● row:窗格的行標,從0開始

  ● col:窗格的列標,從0開始

     ● pViewClass:視圖的執行期類CRuntimeClass指針,可以用宏RUNTIME_CLASS獲得

  ● sizeInit:一個SIZE(或者CSize)類型的數據,指定窗格的初始大小

  ● pContext:一般是由父窗口傳遞過來,包含窗口的創建信息

  返回值:如果創建成功,返回非零值(TRUE),否則返回0(FALSE)。

      m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CTest),CSize(190,100),pContext)

      

 

實現的關鍵代碼

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
{
	// TODO: Add your specialized code here and/or call the base class
	if(!m_wndSplitter.CreateStatic(this,1,2))
	{
		return FALSE;
	}

	CRect rect;
	GetClientRect(&rect);
	if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CTest),CSize(rect.Width()/4,rect.Height()),pContext)||
		!m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CSplitterSDIView), CSize(rect.Width()/4*3,rect.Height()),pContext))
	{
		return FALSE;
	}
	return TRUE;
}
重載該函數之前需要做的步驟:
1. 在CMainFrame類中添加protected成員CSplitterWnd m_wndSplitter;
2. 建立對話框資源(IDD_FORMVIEW),並以CFormView類為基類建立相應的視類;
3. 重載OnCreateClient函數(如上述代碼)。
4. 如果需要更多的划分,則再添加其他的CSplitterWnd成員變量,但在m_wndSplitter2.CreateStatic()
   函數里的第一個參數則采用需要划分的那個子視圖,如m_wndSplitter2.CreateStatic(&m_wndSplitter1,
   1, 2, WS_CHILD|WS_VISIBLE, m_wndSplitter1.IdFromRowCol(0,0)) ),當然也需要自己創建需要的所
   有視圖。

多視類之間的交互

在MFC程序中,各個視類之間進行數據交互是通過Doc類來完成的,由CDocument類來處理文檔,

而由CView類來顯示。即將數據存儲到CDocument類中,而用到數據的時候再從該類中讀取。

處理按鈕事件:
void CTest::OnShowInt() 
{
	// TODO: Add your control notification handler code here
	CSplitterSDIDoc* pDoc =(CSplitterSDIDoc*) GetDocument();
	UpdateData(TRUE);
	pDoc->x=m_int;
	pDoc->UpdateAllViews(NULL);
}
在CSplitterSDIView中顯示:
void CSplitterSDIView::OnDraw(CDC* pDC)
{
	CSplitterSDIDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	CString str;
	str.Format("%d", pDoc->x);
	pDC->TextOut(0,0,str);
}
 

CSplitterWnd類的其他成員信息

有關該類的其他成員函數,可以參考MSDN。

其他信息

鎖定切分條
當用戶創建好分割窗口后,有時並不希望通過拖動切分條來調節窗口的大小。這時就必須鎖定切分條。鎖定切分條的最簡單的
方法莫過於不讓CSplitterWnd來處理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息,而是將這些消
息交給CWnd窗口進行處理,從而屏蔽掉這些消息。拿WM_LBUTTONDOWN處理過程來說。修改為如下: 
void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point) 
{         CWnd::OnLButtonDown(nFlags,point);} 
其余的處理方法類似。 
切分條的定制 
由Window自己生成的切分條總是固定的,沒有任何的變化,我們在使用一些軟件比如ACDSee的時候卻能發現它們的切分條
卻是和自動生成的切分條不一樣的。那么如何定制自己的切分條呢?通過重載CSplitterWnd的虛方法OnDrawSplitter和
OnInvertTracker可以達到這樣的目的。下面的代碼生成的效果是分割窗口的邊界顏色為紅色,分割條的顏色為綠色.代碼如下:
void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &rectArg){ 
                 if(pDC==NULL)
                   {
                   RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);
                  return;
                  } 
                  ASSERT_VALID(pDC); 
                 CRect rc=rectArg; 
                 switch(nType)
                   { 
                  case splitBorder: 
                 //重畫分割窗口邊界,使之為紅色 
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));  
                        rc.InflateRect(-CX_BORDER,-CY_BORDER); 
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); 
                                    return;
                   case splitBox:  
                        pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
                          rc.InflateRect(-CX_BORDER,-CY_BORDER); 
                          pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0)); 
                         rc.InflateRect(-CX_BORDER,-CY_BORDER);
                          pDC->FillSolidRect(rc,RGB(0,0,0)); 
                          pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
                          return; 
                  case splitBar:  
                 //重畫分割條,使之為綠色  
                         pDC->FillSolidRect(rc,RGB(255,255,255));
                          rc.InflateRect(-5,-5); 
                          pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); 
                                    return;  
                 default:
                           ASSERT(FALSE);
                   } 
                  pDC->FillSolidRect(rc,RGB(0,0,255));
}
 void CSplitterWndEx::OnInvertTracker(CRect &rect) 
{                   ASSERT_VALID(this); 
                 ASSERT(!rect.IsRectEmpty()); 
                  ASSERT((GetStyle()&WS_CLIPCHILDREN)==0);
                  CRect rc=rect;  
                 rc.InflateRect(2,2);
                  CDC* pDC=GetDC(); 
                  CBrush* pBrush=CDC::GetHalftoneBrush(); 
                 HBRUSH hOldBrush=NULL; 
                 if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC->m_hDC,pBrush->m_hObject);
                  pDC->PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS);  
                           if(hOldBrush!=NULL)
                   SelectObject(pDC->m_hDC,hOldBrush);
                  ReleaseDC(pDC); 
} 
同樣我們只要繼承CSplitterWnd中的其余的一些虛擬方法就可以生成具有自己個性的分割窗口了。

 http://www.cnblogs.com/feisky/archive/2010/03/07/1680222.html

 

 

另外參考:

用MFC將SDI窗口三叉拆分並初始化各個View

最近做MFC界面,發現《深入淺出MFC》等很多資料里只是教我們如何將窗口三叉拆分,但是拆分后每個View類的初始化和設置很少有資料設計,讓我這種初學者郁悶樂半天。這里是我自己的總結,十分簡陋……

      我需要三個試圖,一個ListView,一個TreeView和一個EditView。類似於vc6的界面最下方為list,上部分左邊為tree,右邊為edit。首先,創建一個工程PaperInfoCrawler,選擇SDI窗口,並將CPaperInfoCrawer繼承CListview

      在MainFram中加入變量 CSplitterWnd m_wndSplitter和CSplitterWnd m_wndTopSplitter並重載函數virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext),下面為函數內容{ m_wndSplitter.CreateStatic(this,2,1);
m_wndSplitter.SetRowInfo(0,450,450);
m_wndTopSplitter.CreateStatic(&m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE,m_wndSplitter.IdFromRowCol(0,0));
m_wndTopSplitter.CreateView(0,0,RUNTIME_CLASS(CMyTreeView),CSize(300,0),pContext);
m_wndTopSplitter.CreateView(0,1,RUNTIME_CLASS(CMyEditView),CSize(0,0),pContext);
m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CPaperInfoCrawerView),CSize(0,0),pContext);
return TRUE;}

      這其中CMyTreeView和CMyEditView是自己繼承的View類,CPaperInfoCrawerView為自動生成的類,此時需要在MainFram.cpp中include相應的頭文件。這時候編譯通過應該窗口已經被划分為三部分了,下面開始對各個View進行初始化。

      MyTreeView.cpp中增加變量 CTreeCtrl* ptheTree重載virtual void OnInitialUpdate()函數內容為{
CTreeView::OnInitialUpdate();
ptheTree=&GetTreeCtrl();
ptheTree->ModifyStyle(0,TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_EDITLABELS);
TVINSERTSTRUCT tvInsert;
HTREEITEM hTreeItem;
tvInsert.hInsertAfter = NULL;//TVI_LAST;
tvInsert.hParent = TVI_ROOT;
tvInsert.item.mask = TVIF_TEXT;
tvInsert.item.pszText = "搜索引擎";
hTreeItem = ptheTree->InsertItem(&tvInsert);
tvInsert.hParent = hTreeItem;
tvInsert.item.pszText = "Google";
ptheTree->InsertItem(&tvInsert);
tvInsert.item.pszText = "Baidu";
ptheTree->InsertItem(&tvInsert);
ptheTree->Expand(hTreeItem,TVE_EXPAND); //默認為合上的TVE_COLLAPSE,打開的TVE_EXPAND
}

      PaperInfoCrawler.cpp中增加變量 CListCtrl* ptheList重載virtual void OnInitialUpdate()函數內容為{
CListView::OnInitialUpdate();

ptheList=&GetListCtrl();
ptheList->ModifyStyle(0,LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SORTASCENDING);
ptheList->SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE,0,LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
ptheList->InsertColumn(0,"標題",LVCFMT_LEFT,200,0);
ptheList->InsertColumn(1,"URL",LVCFMT_LEFT,200,0);
ptheList->InsertColumn(2,"引擎",LVCFMT_LEFT,200,0);
int pos;
pos = ptheList->InsertItem(0,"123");
ptheList->SetItemText(pos,1,"http:\\\\");
ptheList->SetItemText(pos,2,"Google");
}

    編譯通過就可以發現每個窗口都按我們的要求進行了划分和初始化

//獲取主窗口

CMainFrame* pFrame=static_cast<CMainFrame*>(AfxGetMainWnd());


//獲取某個view

CMyTreeView* pView=static_cast<CMyTreeView*>(pFrame->m_wndTopSplitter.GetPane(0,0));


//激活View                                                                                                                                                     

pFrame->SetActiveView(pView);                                                                                                                  

pFrame->m_wndTopSplitter.RecalcLayout();


//想干什么就干什么                                                                                                                                                                 

pView->XXXXXX();
pView->SendMessage(WM_PAINT);

通過以上代碼我們就可以在各個view間互相通信了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM