使用CSplitterWnd类作窗口分割

使用平台:win7 64bit

使用环境:VS2012

1、CSplitterWnd介绍


上图是从MSDN中截取的类的继承图表,CSplitterWnd类继承自CWnd类。这个类主要就是提供窗口分割的功能。


2、使用CSplitterWnd类作窗口分割

建立单文档程序,选择不支持文档-视图框架。目的是为了使得程序结构更加的灵活,适合自己DIY。建立好的程序结构中就回存在有三个基础的类。

CXXXApp类        tips: (XXX)是建立工程的时候自己取得名字,代表的是这个工程的实例

CChildView类

CMainFrame类

窗口的分割要在CMainFrame类中进行完成。目前例子里面我们分割出如图所示的效果:


从上图中可以看到窗口分割成了5个部分,左边的白色部分和右边的四个黑色窗口部分。

在CMainFrame类中添加两个成员:

CSplitterWnd m_spliter1;               //原版的CSplitterWnd
CMySplitterWndEx m_spliter2;     //在CSplitterWnd上做修改之后

然后重载CMainFrame类的OnCreateClient()函数,在该函数中添加如下代码:

  1. BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)  
  2. {  
  3.     CRect rct;  
  4.     GetClientRect(&rct);                                //获取窗口大小  
  5.     int nRcWidth = rct.Width();  
  6.     int nRcHeight = rct.Height();  
  7.   
  8.     //用第一个分隔条分割成1行2列  
  9.     if(m_spliter1.CreateStatic(this, 1, 2)==NULL) return FALSE;  
  10.     //分割的第一行第一列指定动态创建对象CLeftView  
  11.     //指定窗口宽度是1/4界面宽度,高度为整个界面高度  
  12.     m_spliter1.CreateView(0,0,RUNTIME_CLASS(CLeftView),CSize(nRcWidth / 4, nRcHeight), pContext);  
  13.       
  14.     //对m_spliter1分割的第二个区域再使用m_spliter2进行分割,分割成2*2个区域  
  15.     if (m_spliter2.CreateStatic(&m_spliter1, 2, 2, WS_CHILD|WS_VISIBLE, m_spliter1.IdFromRowCol(0, 1)) == NULL) return FALSE;  
  16.     //和上述差不多,指定动态创建的对象为CLTView  
  17.     m_spliter2.CreateView(0, 0, RUNTIME_CLASS(CLTView), CSize(nRcWidth / 8 * 3, nRcHeight/2), pContext);  
  18.     m_spliter2.CreateView(0, 1, RUNTIME_CLASS(CLTView), CSize(nRcWidth / 8 * 3, nRcHeight/2), pContext);  
  19.     m_spliter2.CreateView(1, 0, RUNTIME_CLASS(CLTView), CSize(nRcWidth / 8 * 3, nRcHeight/2), pContext);  
  20.     m_spliter2.CreateView(1, 1, RUNTIME_CLASS(CLTView), CSize(nRcWidth / 8 * 3, nRcHeight/2), pContext);  
  21.   
  22.     return CFrameWnd::OnCreateClient(lpcs, pContext);  
  23. }  
上述代码中的CLeftView和CLTView都是自己向工程中添加的类。详细的可以看分享的代码(博文结尾处)。当然,在MFC中默认生成的CChildView类和相关的程序我都已经把它们删掉或者注释掉了,为了是更清晰的程序结构。

3、分割条的风格DIY

       在上面的代码中两个风格条中有一个分割条是自己在原有的分割条上DIY后的,所有才有了黑底蓝色线条的分割,然而默认的分割风格是比较丑陋的。下面来DIY这个类。

向工程中添加一个MFC,记住是MFC类,很关键!!!

选择其父类为CWnd,因为选择框里面并不能选择CSplitterWnd类,生成之后我们手动把他的继承类改成CSplitterWnd。添加鼠标左键按下的消息响应,并重载OnDrawSplittter函数。

类的定义就成了这个样子(名字可以随便取),为什么前面说的是要添加MFC类?因为MFC类里面才会给我们自动的添加上DECLARE_DYNAMIC(),DELARE_MESSAGE_MAP()这样的宏。也就才能有消息响应的发生。不然这里重载的OnDrawSplittter是无法生效的。

  1. class CMySplitterWndEx : public CSplitterWnd  
  2. {  
  3.     DECLARE_DYNAMIC(CMySplitterWndEx)  
  4.   
  5. public:  
  6.     CMySplitterWndEx();  
  7.     virtual ~CMySplitterWndEx();  
  8.   
  9. protected:  
  10.     DECLARE_MESSAGE_MAP()  
  11. public:  
  12.     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);  
  13.     afx_msg void OnMouseMove(UINT nFlags, CPoint point);  
  14.     virtual void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect);  
  15. };  

在类的构造函数里面就可以对分割条的Size进行调整了。

  1. CMySplitterWndEx::CMySplitterWndEx()  
  2. {  
  3.     m_cxSplitterGap = 2;  
  4.     m_cxSplitter = 5;  
  5.     m_cySplitterGap = 2;  
  6.     m_cySplitter = 5;  
  7. }  
这个是调整分割条横向和竖向的宽度,默认的宽度是10,太宽了不好看!宽度修改了之后再修改颜色。在OnDrawSplittter函数中。

  1. void CMySplitterWndEx::OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect)  
  2. {  
  3.     CSplitterWnd::OnDrawSplitter(pDC, nType, rect);  
  4.     if (pDC != NULL)  
  5.     {  
  6.         CBrush  brush;  
  7.         brush.CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));  
  8.         pDC->FillRect(&rect,&brush);  
  9.     }  
  10. }  
很好理解,这个函数就和视图宽口中的OnDraw的功能差不多。

鼠标左键的消息响应纯粹是为了不想让鼠标左键拖动风格窗条的。也就是在消息传递中截获该消息,不让它传向基类。

4、管理分割窗口

         分割后窗口之间怎么通信?怎么进行相互数据交互?如果不能管理好分割的窗口,很容易使得程序的框架变得混乱。给维护和升级带来很大的麻烦。这个有过相关痛苦经历的人肯定有切身的体会。首先试图窗口都在CMainFrame类中进行创建的,CMainFrame类的对象指针也是很容易获取的。那么我们就可以再CMainFrame类中记录所有视图窗口的对象指针,然后通过访问CMainFrame类的成员来对我们想要交互的窗口进行操作。这样就很好的解决了这个问题,也使得在修改窗口分割后不用大量的去修改原来的视图窗口的代码。具体做法:
在CMainFrame类中添加成员。


然后在分割窗口后就用创建好的成员指向关联的窗口。



这样在视图窗口中就可以直接一句代码就可以访问到想访问的类了:
((CMainFrame*)AfxGetMainWnd())

((CMainFrame*)AfxGetMainWnd())->你想访问的视图


下载链接