【转】 MFC 分割窗体(Splitter Windows)
2011-03-20 18:32
转载自 651405842
最终编辑 drunkdream
      MFC 分割窗体(Splitter Windows)
<reference MSDN Microsoft Foundation Class Library and Templates TNO 029>
文档描述MFC中的CSplitterWnd类,该类用来支持窗体的分割和管理分割后每一个子窗格(pane)的大小
1 分割窗体风格(Splitter Styles)
CSplitterWnd类支持2种不同风格的分割窗口
1.1 静态分割(static splitter)
分割的窗格(pane)在分割窗体(splitter window;)创建时创建,而且顺序,数量不会变化,分割控制条(Splitter Bar)用来控制每个窗格的大小.通常不同的窗格是不同的视图类(view class);
例如 Visual C++graphics Windows文件管理器就是这个风格的分割窗体;
该风格分割窗体不会用到分割格(splitter box)
1.2 动态分割(dynamic splitter)
当用户控制视图的分割或停止分割, 附加窗格会被创建或销毁; 这种动态分割开始于一个单独的视图,分割格(splitter box)用于初始化分割;当一个视图可以在2个方向上被分割时,会有3个新的视图被创建并显示为3个新的窗格;当分割(split)被激活(active)时,分割格(splitter box)会如同分割控制条(splitter bar)一样在各个窗格间被绘制;当用户除去分割时,附加窗格会被销毁,只有原来的视图(分割时位于00列位置的窗格)会被保留直到分割窗体本身被销毁;
例如 Microsoft Excel Microsoft Word都是这种风格的分割窗体;
当创建任意一种风格的分割窗体时,必须给定分割窗体的最大的行列数;
静态风格的分割窗体,每个窗格必须被创建CSplitterWnd::CreateView
动态风格的分割窗体 第一个窗格会在CSplitterWnd对象创建被自动创建
静态风格的分割窗体的最大行列数为 16 x 16
动态风格的分割窗体的最大行列数为 2 x 2
推荐的分割方式为:
1 X 2          2 X 1        2 X 2
2 分割窗体的例子(Splitter Samples)
很多MFC的列子程序都直接或间接的用到了分割窗体 MFC入门程序Scripple程序的第4 部中就用到动态切分窗体
MFC标准示例中ViewEx展示静态使用切分窗体的方法,包括如何在一个切分窗体中包含另一个切分窗体
3 分割窗体中使用的术语(Terminology Used By Implement)
CsplitterWnd(分割窗体):
负责提供窗格切分空间和滚动条(同行(row)共享竖直滚动条(Vertical ScrollBar),同列(column)共享水平滚动条(Horizontal ScrollBar) );同时行列的下标从0 0开始,言即第一个窗格为第0 0列的窗格
Pane(窗格)
CSplitterWnd管理的应用程序显示数据的窗体,一般而言窗格是一个视图的派生类对象,实际上窗格可以是任意的从CWnd中派生的对象;
Splitter Bar(分割控制条)
pane在窗格行列间的控件,用于控制行列上窗格的大小
Spliiter Box(分割格)
动态分割窗体位于竖直滚动条最上的或水平滚动条最左位置的按键,用于创建新的分割窗格
Splitter Intersection(分割交叉点)
竖直或水平分割控制条的交叉点,可用于同步控制水平,竖直窗格的大小
4 共享滚动条(Shared Scroll Bars)
CSplitterWnd支持共享滚动条,滚动条是CSplitterWnd的子窗体并且在分割窗体中为不同的窗格所共享;
如在创建1X 2 CSplitterWnd时声明了WS_VSCROLL分割 那么2个窗格共享的竖直滚动条会创建如:
[      ][      ][^]
[pane00][pane01][|]
[      ][      ][v]
当移动滚动条时,WM_VSCROLL消息会发送到2个窗格中同样,同列的窗格可以共享水平滚动条
注意
在分割窗体中共享滚动条是非常有用的,如果是2种不同的视图使用共享滚动条 那么就必须要协调滚动位置和视图的显示;所有视图的派生类可以通过CWnd中的操作ScrollBar的方法来控制共享滚动条,但是一些非视图派生类,没有使用ScrollBar控件的类 通过标准Window的实现方法的类如CEditView将不适合于共享滚动条
5 最小大小(Minimum size)
在分割窗体中每一行都有最小高度限制,每一列都有最小宽度限制,这2个最小值用于控制窗格显示数据最小大小;
静态分割窗体的最小大小为 00 动态分割窗体的最小大小可以在CSplitterWnd::创建时设定;
这些值可以同过SetColumnInfo SetRowInfo来改变;
6 未公布的接口函数(protected interface)
以下将讨论一些未公布的CSplitterWnd的技术细节以便高级用户来定制CSplitterWnd
这些API没有官方文档公布同时在将来版本的MFC中极可能会被改变:
6.1 分割控制条(splitter bar)分割格(box) 分割位置跟踪(tracker)的绘制
enum ESplitType    { splitBox, splitBar, splitIntersection, splitBorder };virtual void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect);virtual void OnInvertTracker(const CRect& rect);这些虚函数可以用来实现不同图形表现的分割窗体
6.2 创建视图和控件
virtual BOOL CreateScrollBarCtrl(DWORD dwStyle, UINT nID);默认行为时创建滚动条,但是可以重载用于在滚动条边创建新控件
6.3 动态切分窗体
virtual void DeleteView(int row, int col);virtual BOOL SplitRow(int cyBefore);virtual BOOL SplitColumn(int cxBefore);virtual void DeleteRow(int row);virtual void DeleteColumn(int row);用于实现动态分割窗体的逻辑(当分割窗体拥有风格SPLS_DYNAMIC_SPLIT)
Trackback: tb.blog.csdn/TrackBack.aspx?PostId=697418
VC设计分割视图通用创建框架
目前基于分割视图的应用开发十分流行,分割视图技术是在同一个框架窗口下同时显示多个视图的一项技术。运用分割视图,可以在较短时间内给用户更多的信息量,从而使得用户界面更加的友好,增强了软件的可操作性。本文提出一个分割视图的通用创建框架。
  1.分割视图创建框架
  分割视图的创建大体上分为两个步骤:其一是创建分割窗体;然后就是处理鼠标和键盘等消息。
  1) 创建分割窗体
  MFC提供分割窗体类CsplitterWnd,它提供了很多对于分割窗体操作的成员函数,每一个分割窗体都是一个CsplitterWnd的对象。本文提出的框架由于需要对定制的分割窗体进行扩充处理,所以首先从CsplitterWnd继承一个子类CFixSplitterWnd,然后每个分割窗体是一个CfixSplitterWnd的对象,这样以后只需要对CfixSplitterWnd进行改写后就可以增强分割窗体的功能。(后面将提出这种改写)
创建分割窗体最重要的函数是主框架类的OnCreateClient函数,它将在主框架创建的时候调用,本文将创建一个如下显示的分割窗体:
1
/Files/BeyondPic/2005-12/24/155661.gif
  则可以如下实现:
//成员变量声明
CFixSplitterWnd m_wndSplitterH; //用于横向切割
CFixSplitterWnd m_wndSplitterV; //用于纵向切割
BOOL m_bCreateSplitter;
//分割窗体的实现
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
//对整个主框架进行混合分割视图
BOOL bResult=m_wndSplitterV.CreateStatic(this,1,2);
ASSERT(bResult);
m_wndSplitterH.CreateStatic(&m_wndSplitterV,4,1,WS_CHILD | WS_VISIBLE,m_wndSplitterV.IdFromRowCol(0,1));
//创建各自子窗片的对应的视图
m_wndSplitterV.CreateView(0,0,RUNTIME_CLASS(CSceneView),CSize(600,600),pContext);
m_wndSplitterH.CreateView(0,0,RUNTIME_CLASS(CPitchView),CSize(100,100),pContext);
m_wndSplitterH.CreateView(1,0,RUNTIME_CLASS(CYawView),CSize(100,100),pContext);
m_wndSplitterH.CreateView(2,0,RUNTIME_CLASS(CRollView),CSize(100,100),pContext);
m_wndSplitterH.CreateView(3,0,RUNTIME_CLASS(CControlView),CSize(100,100),pContext);
//设置窗格的初始化的大小
m_wndSplitterV.SetRowInfo(0,IDEAL_RAWHEIGHT,0);
m_bCreateSplitter=TRUE;
//激活sceneview使得其可以接受命令消息
m_wndSplitterV.SetActivePane(0,0,NULL);
return bResult;
}
//主框架窗体大小发生变化,调节相应的窗体大小
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CMDIFrameWnd::OnSize(nType, cx, cy);
CRect rect;
GetClientRect(rect);
if (m_bCreateSplitter)
{
m_wndSplitterV.SetColumnInfo(0,rect.Width() *3/4,10);
m_wndSplitterV.SetColumnInfo(1,rect.Width() *1/4,10);
m_wndSplitterH.SetRowInfo(0,rect.Height() /6,10);
m_wndSplitterH.SetRowInfo(1,rect.Height() /6,10);
m_wndSplitterH.SetRowInfo(2,rect.Height() /6,10);
m_wndSplitterH.SetRowInfo(3,rect.Height()/2,10);
m_wndSplitterV.RecalcLayout();
m_wndSplitterH.RecalcLayout();
}
}
  注意m_wndSplitterH.CreateView 中的第二个参数,这个参数将分割窗体和相应的视图类相对应。
  通过上述的程序代码即可创建图1所示的分割窗体,那么由于这里每个分割窗体都是一个CfixSplitterWnd对象,所以可以通过改写CfixSplitterWnd类的虚函数或消息处理函数来完成自己特定的应用实现。(注意,如果需要对定制有特定属性的分割窗体,一定要派生自己的分割窗体类而不能是MFCCsplitterWnd类)这里我们需要分割窗体不能随鼠标拖动而改变其大小,即所有窗格的大小都是一定的,不能在运行时刻改变。所以必须在CfixSplitterWnd类的实现中加入如下代码:
void CFixSplitterWnd::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd::OnMouseMove(nFlags, point); //防止鼠标出现拖动状
// CSplitterWnd::OnMouseMove(nFlags, point); //鼠标会在窗体边界出现拖动状
}
  至此,分割窗体已经创建完毕,下面需要在分割窗体里处理消息。
2) 分割窗体处理消息在分割窗体里处理消息和一般的文档视图模型处理消息大致一样,但它也有其特殊之处。具体来说,由于各个分割窗体已经与具体的视图类相联系了,所以在需要处理各个分割窗体中的消息时,可以直接到相应的视图类中进行处理;另外,多视图之间的切换会导致目标焦点之间的变更,这样会影响菜单中与视图有关的命令的执行。比如在图1中所示的分割窗体中,有一个开始命令必须是焦点在CsceneView视图上时才能执行,否则就应该让该命令不能执行(即该菜单呈现灰),则实现时可以首先对鼠标进行点击测试,判断是否在CsceneView视图范围内,如果是的话就允许执行,否则就不允许执行。
2.结论
通过本文提出的分割视图创建框架,可以满足对视图进行复杂控制的需求,希望本文可以给大家一个启发,从而能够创建更为完美的分割视图应用程序
标准方式视图获取数据通知文档处理.文档处理完成  ,  通知所有的视图更新.


//  视图收到数据,传递给文档进行修改
void  COneView::SetData(int  i)
{
        GetDocument()-> SetData(i);
}


//////////////////////////////////
//  文档接受视图的修改请求,完成成通知所有的视图更新
void  CXXXDoc::SetData(int  i)
{
        m_iData  =  i;


        UpdateAllViews(NULL);
}

//////////////////////////////////////////
//  文档通过这个函数返回给视图数据

int  CXXXDoc::GetData()
{
        return  m_iData;
}

//////////////////////////////////////
//  视图收到文档的更新通知更新显示



void  COneView::OnUpdate(...)
{
        //  重绘客户区
        Invalidate();    //    引起OnDraw()  的调用
}


void  COneView::OnDraw(CDC*  pDC)
{
        CXXXDoc*  pDoc  =  GetDocument();
        CString  str;
        str.Format( "i=%d ",  pDoc-> GetData());
        pDC-> TextOut(0,0,str);
}



///////////////////////////////////
//  另一个视图也会收到通知(所有与文档关联的视图都会收到)

void  COtherView::OnUpdate(...)
{
        //  这个更新控件(假设是从CFormView派生的,m_iData关联在一个EDIT控件上)
        m_iData  =  GetDocument()-> GetData();
        UpdateData(FALSE);
}