MFC的窗口分割的设计与实现以及CSplitterWnd 类分析

1 引言

在MicrosoftVC++ 6.0 中,基于MFC 的应用程序一般分为以下几种:多文档界面(MDI)、单文档界面(SDI)以及基于对话框的应用程序。其中单文档又可分为单视图的和多视图的,一般情况下,单文档仅需要单视图就够了,如Windows 自带的记事本、画图程序等等,但在一些情况下,单文档需要多视图支持,比如同时观察文档的不同部分,同时从不同的角度观察同一文档等。在MFC 的框架下,文档对象(CDocument)有一个保存其所有视图的列表,并提供了增加视图(AddView)与删除视图(RemoveView)函数,以及当文档内容改变时通知其所有视图的方法(UpdateAllViews)。通过多文档框架的窗口复制机制和单文档框架的分割窗口机制是实现单文档多视图的主要方法。

 

2 单文档的多视图

一般地,单文档与多视图有三种情况:

 

(1)在多文档界面MDI 中,每个视图位于MDI 的一个独立子文档框架中,视图对象基于同一个视图类。用户可以通过“窗口| 新窗口”菜单,为同一文档的视图再创建一个窗口,通过新创建的窗口,可以编辑和观察文档的另一部分,同一文档各个视图之间自动实现同步,用户修改一个视图的内容,在另外的视图中也自动更新。MFC 框架通过复制原来的子框架窗口和其中的视图来实现上面的功能,并且是完全自动的。

 

(2)视图对象基于同一视图类,所有视图位于同一文档框架中。分割窗口将单文档窗口的视图区分割成几个独立的视图,框架从同一视图类创建多个视图对象。Word 的子窗口即属于这种类型。

 

(3)视图对象基于不同的视图类,所有的视图位于同一文档框架中。多个视图共享同一文档框架,但从不同的视图类创建,每个视图可以为文档提供不同的观察和编辑方法。比如在一个窗口里观察文档的不同部分,或者是在一个窗口里用不用类型的视图观察同一个文档。这种类型的实现方法是通过重载框架类CMainFrame 的成员函数OnCreateClient 实现,用户可以根据不同需要将窗口分为垂直或水平的多个分割窗口。

 

下面通过实例设计,介绍单文档多视图的窗口分割和多视图之间的通信的实现方法。

实例为一个基于单文档的MFC应用程序,通过静态分割窗口的方式三叉切分窗口,即共有

三个窗格。程序实现的功能是用户可以输入学生的信息,并添加到列表视图中。程序最终运

行的结果如下图:

其中左侧的基本信息输入的窗格采用的是CFormView 类型的视图,在用户可以其中进行信息的录入,单击“提交”按钮,数据就添加道文档中了,并在右侧的列表视图中显示。右侧信息显示的窗格采用的是CListView 类型的视图,显示文档中存储的所有学生信息。而底部的窗格采用的是CEditView 类型的视图,用于提示用户上一步添加的数据。下面介绍具体的实现过程。

创建工程

使用AppWizard 创建一个基于单文档的应用程序框架工程,工程名为“Guo”,其余的现

象均采用默认设置。

添加视图类

需要为 3 个窗格添加3 个视图类。CLeftFormView、CTopListView、CBottomEditView,其

基类分别为CFormView、CListView 和CEditView。

1、CLeftFormView 类的实现

A、 添加对话框资源模板:添加CLeftFormView 类之前,首先要向工程中添加

CLeftFormView视图中对话框模板,如下图所示:

对话框模板的ID 为“IDD_DIALOG1”,其Style 属性设置为“Child”,Bolder 属性设置为

“None”。


B、添加CLeftFormView 类。执行“Insert”→“New Class”菜单命令,弹出“New Class”对话

框,在其中的Name 编辑框中输入类名“CLeftFormView”,在Base Class 列表框中选择基类

“CFormView”选项,在Dialog ID 列表框中选择“IDD_DIALOG1”对话框资源。单击Ok 即可

实现CLeftFormView 类的添加。

C、添加CLeftFormView 类的相关资源:利用Class Wizard 在CLeftFormView 中,为对话框

模板的4 个编辑控件分别添加CString 类型的成员变量m_Num、m_Name、m_Magor、

m_Home,并为“提交”按钮添加BN_CLICKED 消息响应函数OnSubmit()。

2、CTopListView 类的实现

同样使用“NewClass”对话框,添加CTopListView 类,将其基类选择类型为CListView。

然后使用ClassWizard 重载该类的PreCreateWindow()函数,在其中定义列表视的类型,代码如下:

BOOL CTopListView::PreCreateWindow(CREATESTRUCT& cs)

{

    //TODO:  在此添加专用代码和/或调用基类

    cs.style= cs.style | LVS_REPORT;// 设置成报告列表的显示形式

    return CListView::PreCreateWindow(cs);

}

使用ClassWizard 重载CTopListView 类的OnInitialUpdate()函数,在其中添加列表的表头,代码如下:

void CTopListView::OnInitialUpdate()

{

    CListView::OnInitialUpdate();

    //TODO:  在此添加专用代码和/或调用基类

    CString m_ColumnLabelStr[] = { L"学号", L"姓名", L"专业", L"籍贯" };

    //表头字段

    CListCtrl& listctrl = GetListCtrl();//获取列表的控件

    DWORD dwStyle = listctrl.GetExtendedStyle();

    dwStyle |= LVS_EX_FULLROWSELECT;

    // 选中某行使整行高亮(只适用与report 风格的listctrl

    dwStyle |= LVS_EX_GRIDLINES;

    dwStyle |= LVS_EX_UNDERLINEHOT;

    listctrl.SetExtendedStyle(dwStyle);//列表风格

    int width[6] = { 80, 80, 110, 150 };

    for (int i = 0; i < 4; i++)

    {

        listctrl.InsertColumn(i,m_ColumnLabelStr[i], LVCFMT_LEFT,width[i]); // 设置表头

    }

}

3、CBottomEidtView 类的实现

同样使用 NewClass 对话框,添加CBottomEditView 类,将其基类选择为“CEditView”。

而后使用ClassWizard 重载该类的OnInitialUpdate()函数,在其中实现初始化设置,代码

如下:

void CBottomEditView::OnInitialUpdate()

{

    CEditView::OnInitialUpdate();

    //TODO:  在此添加专用代码和/或调用基类

    CEdit &mEdit = GetEditCtrl(); //获取编辑视图的控件

    mEdit.SetWindowText(L"等待用户输入学生的信息!");//设置显示信息

    mEdit.EnableWindow(FALSE); //编辑控件不可编辑

}

静态分割窗口的实现

窗口的分割过程中是首先在主框架 CMainFrame 中,将窗口分割成上下两个窗格,对应的视图分别为CGuoView 和CBottomEditView。而后,再在CGuoView 视图中将窗格分为左右两个窗格,对应的视图分别为CLeftFormView 和CTopListView,实现过程如下。

 

1、在 CMainFrame 类的头文件中,声明一个CSplitterWnd 类的成员变量m_Splitterwnd1,用于第一个窗口的分割

protected // 控件条嵌入成员

    CMFCMenuBar      m_wndMenuBar;

    CMFCToolBar      m_wndToolBar;

    CMFCStatusBar    m_wndStatusBar;

    ......

   CSplitterWnd   m_Splitterwnd1; //用于产生第一次的静态的分割

2、使用 Class Wizard 重载CMainFrame 类的OnCreateClient()函数,在其中实现第一次的

窗口分割。

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)

{

    //TODO:  在此添加专用代码和/或调用基类

    CRect rect;

    GetClientRect(&rect); //产生第一次静态分割

    m_Splitterwnd1.CreateStatic(this, //父窗口指针

        2, 1); //行数与列数

    m_Splitterwnd1.CreateView(0,0, //窗格的行列序数

        RUNTIME_CLASS(CGuoView),//视图类

        CSize(rect.Width(), rect.Height() - rect.Height() / 5), pContext);//父窗口创建参数

    m_Splitterwnd1.CreateView(1,0, RUNTIME_CLASS(CBottomEditView),

        CSize(rect.Width(), rect.Height() / 5), pContext);

    //不在调用基类的OncreateClient 函数

    return true;

    //returnCFrameWndEx::OnCreateClient(lpcs, pContext);

}

包含相应的头文件,在MainFrame.cpp 文件的开始加入下列语句

#include"GuoView.h"

#include"BottomEditView.h"

3、在 视图 窗 口 类 CGuoView 的头文件中声明一个CSplitterWnd 类的成员变量m_Splitterwnd2,用于第二次窗口分割。

protected:

    CSplitterWnd   m_Splitterwnd2;//用于第二次窗口的分割

4、使用 Class Wizard 重载CGuoView 类的OnCreate()和OnSize()函数,实现窗口第二次分割并设置窗格的大小。

int CGuoView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CView::OnCreate(lpCreateStruct)== -1)

        return -1;

    //TODO:  在此添加您专用的创建代码

    CRect rect;

    GetClientRect(&rect);

    //获得窗口的创建信息指针

    CCreateContext *pContext = (CCreateContext*)lpCreateStruct->lpCreateParams;

    m_Splitterwnd2.CreateStatic(this,1,2);//产生第二次的静态分隔

    //为第一个窗格产生视图

    m_Splitterwnd2.CreateView(0,0, RUNTIME_CLASS(CLeftFormView), CSize(rect.Width() / 4, rect.Height()),pContext);

    //为第二个窗格产生视图

    m_Splitterwnd2.CreateView(0,1, RUNTIME_CLASS(CTopListView), CSize(1, 1), pContext);

    return 0;

}

void CGuoView::OnSize(UINT nType, int cx, int cy)

{

    CView::OnSize(nType, cx, cy);

    //TODO:  在此处添加消息处理程序代码

    CRect rect;

    GetClientRect(&rect);

    int x = rect.Width();

    int y = rect.Height();

    m_Splitterwnd2.MoveWindow(-2,-2, x, y + 3);

    m_Splitterwnd2.SetColumnInfo(0,x / 4, 0); //左边窗格位置

    m_Splitterwnd2.SetColumnInfo(1,x - x / 4, 0); //右边窗格位置

    m_Splitterwnd2.RecalcLayout();

}

至此,窗口的分割完成,编译运行程序,就会发现三叉窗口已经实现。如果在编译连接

程序的时候出现如下面的错误:

c:\documentsand settings\chenqi\桌面\guo\guoview.h(23) :error C2143: syntax error : missing

';'before '*'

c:\documentsand settings\chenqi\桌面\guo\guoview.h(23) :error C2501: 'CGuoDoc' : missing

storage-classor type specifiers

c:\documentsand settings\chenqi\桌面\guo\guoview.h(23) :error C2501: 'GetDocument' :

missingstorage-class or type specifiers

则可以在CGuoView 类头文件的前面加上这么一句class CGuoDoc; 就没有问题了。

窗格视图与文档的交互

窗口中分割的各窗格视图对应着同一文档对象CGuoDoc,每个CView 派生类都已经继

承了GetDocument()函数,因此只要在调用后进行类型的强制转换就可以获取文档的对象。

如:CGuoDoc*pDoc=(CGuoDoc*)GetDocument();

本实例在文档对象CGuoDoc 中,通过数组类对象存储学生信息,当在CLeftFormView

视图中,输入学生信息单击“提交”按钮时,就将输入信息写入文档中的数组对象,并重绘

各视图。

1、在 CGuoDoc 类的头文件中声明数组对象和数据修改标记,如下:

Public:

CStringArrayinfoArray[4];

booladd;

并在构造函数中将add 的值初始化为FALSE。

2、在 CLeftFormView 类的按钮响应函数OnSubmit()中,添加代码实现控件数据的保

存并更新所有视图。

void CLeftFormView::OnSubmit()

{

    //TODO:  在此添加控件通知处理程序代码

    UpdateData(TRUE); // 获取对话框的控件数据

    if (m_Num.IsEmpty() || m_Name.IsEmpty()) //判断是否为空

    {

        AfxMessageBox(L"学号和姓名不能为空!"); return;

    }

    CGuoDoc* pDoc = (CGuoDoc*)GetDocument();// 获取文档

    pDoc->infoArray[0].InsertAt(0,m_Num); // 输入数据插入数据

    pDoc->infoArray[1].InsertAt(0,m_Name);

    pDoc->infoArray[2].InsertAt(0,m_Magor);

    pDoc->infoArray[3].InsertAt(0,m_Home);

    pDoc->add= true; //添加了数据

    pDoc->UpdateAllViews(NULL); //更新所有视图

    m_Num = _T("");

    m_Name = _T("");

    m_Magor = _T("");

    m_Home = _T("");

    UpdateData(FALSE); //各控件的内容清空

}

包含CGuoDoc 类的头文件,在CLeftFormView.cpp 文件开始加入下列语句:

#include"GuoDoc.h"

3、重载视图类 CTopListView 和CBottomEditView 中OnUpdate()函数,实现视图更新。

void CTopListView::OnUpdate(CView* /*pSender*/, LPARAM /*lHint*/, CObject* /*pHint*/)

{

    //TODO:  在此添加专用代码和/或调用基类

    CGuoDoc* pDoc = (CGuoDoc*)GetDocument(); //获取文档指针

    if (pDoc->add) //添加了数据

    {

        CListCtrl& listctrl = GetListCtrl(); // 获取列表的控件

        listctrl.DeleteAllItems(); //删除所有项

        for (int i = 0; i < pDoc->infoArray[0].GetSize(); i++) //列表框中插入数据

        {

            listctrl.InsertItem(i,pDoc->infoArray[0].GetAt(i));

            listctrl.SetItemText(i,1, pDoc->infoArray[1].GetAt(i));

            listctrl.SetItemText(i,2, pDoc->infoArray[2].GetAt(i));

            listctrl.SetItemText(i,3, pDoc->infoArray[3].GetAt(i));

        }

    }

}

void CBottomEditView::OnUpdate(CView* /*pSender*/, LPARAM /*lHint*/, CObject* /*pHint*/)

{

    //TODO:  在此添加专用代码和/或调用基类

    CGuoDoc* pDoc = (CGuoDoc*)GetDocument(); // 获取文档指针

    if (pDoc->add) // 添加了数据

    {

        CString str;

        str =L"添加了学号为" + pDoc->infoArray[0].GetAt(0) + L"的学生信息!";

        CEdit &mEdit = GetEditCtrl(); //获取编辑视图控件

        mEdit.SetWindowText(str); //显示信息

    }

}

同样需要在这两个视图类的资源文件中包含文档对象的头文件,如下:

#include"GuoDoc.h"

至此,实例开发结束,编译运行工程,即可实现要求的结果。

注意:编译可能会报:

errorC2143: 语法错误 : 缺少“;”(在“*”的前面)

errorC2501: “CTestView::CTestDoc” : 缺少存储类或类型说明符

errorC2501: “CTestView::GetDocument” : 缺少存储类或类型说明符

warningC4183: “GetDocument”:缺少返回类型;假定为返回“int”的成员函数

解决方法:

C***View.h文件头添加

#include"C***Doc.h"

同时可以把C**View.cpp中上面去掉.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Class)是微软提供的一种C++库,用于快速开发Windows界面应用程序。其中的拆分窗口可以有效地将窗口分割成多个子窗口,方便用户在一个窗口中同时显示多个视图或控件。 动态拆分示例是指在程序运行时根据用户的操作或其他条件来动态地改变拆分窗口的布局。以一个简单的文本编辑器为例,用户可能希望在编辑文本的同时查看另一部分文档内容。在这种情况下,可以使用动态拆分窗口实现在编辑区域和预览区域的同时显示。 在MFC中,可以使用拆分窗口(CSplitterWnd)来实现动态拆分窗口。通过在代码中响应用户的操作或其他条件的变化,可以动态地改变拆分窗口的布局。比如,可以通过捕获用户的鼠标事件来改变拆分窗口的大小和位置,或者根据用户的选择来动态地增加或减少子窗口的数量。 动态拆分窗口实现需要一定的编程经验和理解MFC的相关知识。在使用MFC的拆分窗口进行动态拆分时,需要根据具体的需求和UI设计来选择合适的拆分方式和布局。同时,为了提高用户体验,还需要注意在动态改变拆分窗口布局时保持界面的流畅和稳定。 总之,MFC的拆分窗口提供了丰富的功能和灵活的布局方式,可以通过动态拆分实现更加灵活和复杂的界面布局。通过合理的设计实现,动态拆分窗口可以为用户提供更加便捷和高效的操作体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值