动态切分窗口:
动态切分窗口不能包含多于4个的窗格,要想得到4个以上的窗格,可以使用静态切分窗口。动态切分窗口中的几个窗格通常使用的是同一个视图类。
创建动态切分窗口的方式共有3种:
1、在创建应用程序时,利用应用程序向导AppWizard的选项设置切分窗口
2、在创建了应用程序之后,通过添加组件对窗口进行切分
3、通过手动添加代码编程的方式
使用AppWizard创建动态切分:
在MFC AppWizard Step 4 of 6对话框中,单击Advanced按钮,弹出Advanced Options对话框,选择Window Styles选项卡,选中该页的Use split window检查框。
使用Splitter Bar组件:
选择Project|Add To Project|Components and Controls...菜单,弹出Components and Controls Gallery对话框。双击Visual C++ Components目录,选择Splitter Bar项,插入Splitter Bar组件。
运行程序,打开【查看】菜单或者是【窗口】菜单,找不到前面使用AppWizard添加动态切分时的【分隔】菜单项。说明Splitter Bar组件的方式并没有添加相应的菜单项,程序员可以自己添加上去。
注意:菜单的Caption(标题)可以随便设定,但注意菜单项的ID必须是ID_WINDOW_SPLIT。ID_WINDOW_SPLIT是系统预先指定的,程序员一般不需要对此命令ID添加命令消息处理函数,就可以实现分隔,因为MFC应用程序框架中已经对该命令ID的消息进行了处理。
手动添加代码:
1、在框架窗口类(对单文档为主框架窗口,对多文档为子框架窗口)中声明一个CSplitterWnd类的成员变量m_wndSplitter
2、使用ClassWizard为框架窗口类添加OnCreateClient函数
注意:这里使用ClassWizard是重写父类的虚函数,而不是添加消息处理
3、在OnCreateClient函数中调用CSplitterWnd类的成员函数Create产生动态切分
4、为应用程序添加一个ID为ID_WINDOW_SPLIT的菜单项
CSplitterWnd切分窗口类:
CSplitterWnd的成员函数中,有3个使用非常频繁
1、Create函数
创建一个动态的切分窗口
2、CreateStatic函数
创建一个静态的切分窗口
3、CreateView函数
为静态切分窗口创建视图窗口。在框架显示一个静态切分窗口之前,该窗口的所有视图窗格都必须已被创建好。当动态切分窗口时,此函数被框架窗口自动调用。
静态切分窗口:
静态切分窗口是指在窗口创建时,切分窗口的窗格就已经创建好了,且窗格的数量和顺序不会改变。最多可以支持16行×16列的窗口(即256个窗格),但是必须在应用程序中创建窗格,而且不同的窗口往往使用不同的视图类。
创建静态切分的步骤:
1、在框架窗口类中声明一个CSplitterWnd类的成员变量m_wndSplitter
2、使用ClassWizard为框架窗口类添加OnCreateClient函数
3、在OnCreateClient函数中调用CSplitterWnd::CreateStatic产生静态切分
4、在OnCreateClient函数中调用CSplitterWnd::CreateView产生每个切分的视图窗口
为2DCAD应用程序添加静态切分功能:
1、声明切分窗口类对象m_wndSplitter
为主框架窗口类CMainFrame添加保护成员变量m_wndSplitter,代码如下:
CSplitterWnd m_wndSplitter; //用于产生静态切分
2、在框架窗口类中添加OnCreateClient函数
打开ClassWizard,选中MessageMaps选项卡,在Class name中选择CMainFrame类,在ObjectIDs列表中选择CMainFrame,在Message列表框中双击OnCreateClient项,为框架窗口添加OnCreateClient函数。
3、使用CreateStatic产生静态切分
在OnCreateClient函数中添加如下代码,就可以产生1×2的静态切分窗口:
m_wndSplitter.CreateStatic(this, 1, 2);
4、使用CreateView产生每个视图窗口
当用户建立一静态切分窗口时,应当同时建立窗口内的所以区域。可以在OnCreateClient函数中添加如下代码来为第一行第一列的窗格产生视图:
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CMy2DCADView),
CSize(450, 250), pContext);
其中,第3个参数,视图类的执行期类CRuntimeClass指针,可以用宏RUNTIME_CLASS来获得。
注意:(1)必须为每一个静态切分窗口创建视图窗口,不能漏掉一个
(2)由于使用到了视图类,因此,必须包含相应的类的头文件。在MainFrm.cpp文件的开始部分包括以下两个头文件:
#include "2DCADDoc.h"
#include "2DCADView.h"
(3)产生静态切分后,就不能再调用默认情况下的基类的OnCreateClient函数。因此,应该将下面的代码删除或者注释掉:
// return CFrameWnd::OnCreateClient(lpcs, pContext);
最后,完整的CMainFrame::OnCreateClient函数代码如下:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
//使用CreateStatic函数产生静态切分
m_wndSplitter.CreateStatic(this, //父窗口指针
1, //行数
2); //列数
//使用CreateView为第1个窗格产生视图
m_wndSplitter.CreateView(0, 0, //窗格的行、列序数
RUNTIME_CLASS(CMy2DCADView), //视图类
CSize(450, 250), //初始化大小
pContext); //父窗口的创建参数
//使用CreateView为第2个窗格产生视图
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CMy2DCADView),
CSize(450, 250), pContext);
return 1; //不再调用基类的OnCreateClient函数
// return CFrameWnd::OnCreateClient(lpcs, pContext);
}
这时,用户在一个视图中所作的修改还不能立刻反映到其他视图窗口中,还应添加相应的通知视图重画的代码。
5、通知视图重画
文档类有一个名为UpdateAllViews的函数,可以在文档数据发生改变时通知所有的视图窗口重画。
在视图类的OnLButtonDown函数中每个调用SetModifiedFlag函数的后面立即调用UpdateAllViews的函数,代码如下:
...
pDoc->SetModifiedFlag(); //设置文档“脏”标志
pDoc->UpdateAllViews(this); //重画所有视图
...
向当前工程中插入新类的两种方式:
1、使用New Class对话框添加新类
(1)MFC ClassWizard中的New Class对话框
使用ClassWizard对话框中的Add Class...按钮下的New...菜单项可以打开MFC ClassWizard对话框下的New Class对话框。这个对话框用来向当前工程中添加派生自MFC类库的新类。
(2)通用的New Class对话框
单击菜单命令Insert|New Class...可以打开一个通用的New Class对话框。实际上通用的New Class对话框相当于包含了3个对话框,用于产生几乎所有不同的类。
2、使用插入包含类定义的文件到当前工程中
(1)将所需类的.h、.cpp文件复制到目标工程目录下
(2)打开目标工程,单击Project|Add To Project|Files...菜单项插入所需类的.h、cpp文件到目标工程中
(3)在所需类的.cpp文件中,去掉包含原来的工程的头文件语句(比如2DCAD.h),添加包含目标工程的头文件语句(比如Mut.h)
添加新的视图类:
为2DCAD工程添加一个新的视图类CTextView,单击Insert|New Class...菜单项,在New Class对话框中确保Class type中选择的是MFC Class,在Name编辑框中输入CTextView,在Base class下拉列表中指定基类为CEditView。
添加了新的视图类之后,就可以在框架窗口的OnCreateClient函数中调用CreateView函数时使用这个新视图类了。接下来使用CTextView作为静态切分窗口中第二个窗格的视图类,在OnCreateClient函数中修改创建第二个窗口视图的代码如下:
//使用CreateView为第2个窗格产生视图
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CTextView),//使用文本视图类
CSize(250, 250), pContext);
由于使用到了新的视图类,因此,应该在MainFrm.cpp文件的开始部分添加包含CTextView类头文件的代码:
#include "TextView.h"
注意:CEditView封装了编辑框的功能,因此,如果要编写文字处理软件,可以使用CEditView作为视图基类。CEditView最多支持64K的文本,如果需要支持更丰富的文本,可以使用CRichEditView作为视图基类,但要包含头文件afxrich.h。
表单视图类CFormView:
CFormView就是可以在其中包含许多控件的视图基类。这个类也像CDialog对话框类一样,封装了数据交换功能。甚至可以将CFormView理解为作为视图的对话框。
1、创建对话框模板资源
(1)添加对话框模板,ID为IDD_FORMVIEW
(2)修改属性
在Style选项卡中,设置Style选项为Child,Border选项为None,并去掉对Title Bar复选框的勾选。
注意:由于视图类是一个子窗口,因此,必须设置Child选择,否则程序不能正常运行。
(3)添加控件
删掉默认的OK和Cancel按钮,依次添加一个静态文本、一个分组框和4个单选按钮,同时为第一个单选按钮添加上Group属性。
注意:对一组单选按钮来说,只需要对该组的第一个单选按钮设置Group属性就可以了。如果不设置Group属性,就无法添加对应的成员变量。
2、产生CFormView的派生类
(1)使用ClassWizard为IDD_FORMVIEW对话框模板资源添加一个名为CColorForm的视图类,注意选择基类为CFormView
(2)在Member Variables选项卡中为IDC_RADIO1添加一个名为m_nColor的整型成员变量
注意:对一组单选按钮只能对带有Group属性的按钮增加一个成员变量,并且这个成员变量必须是int型。0表示选择第一个,依次类推,-1表示未选择。
在CColorForm构造函数中令成员变量m_nColor的初始值为0,表示选择第一项。
3、修改切分窗口的视图类
打开OnCreateClient函数,修改创建第二个窗口视图的代码如下:
//使用CreateView为第2个窗格产生视图
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CColorForm),//使用表单视图类
CSize(250, 250), pContext);
由于使用到了新的视图类,因此,应该在MainFrm.cpp文件的开始部分添加包含CColorForm类头文件的代码:
#include "ColorForm.h"
可以像处理无模式对话框那样,在表单视图类中添加相应的消息处理函数。
一个多文档多视图的综合示例:
下面综合前面的知识,创建一个包含多文档、多视图、动态切分、三叉静态切分以及可以支持多种文档类型的综合示例。
支持多种文档类型:
在MFC应用程序中,要增加附加的文档类型,大致可分为以下几步:
1、使用ClassWizard添加新的文档类、子框架窗口类以及视图类(如果需要的话)
2、利用资源编辑器为新的文档类型添加字符串资源
3、利用资源编辑器添加附加的图标和菜单资源
4、创建新的文档模板对象并加入到应用程序中
5、完成文档的序列化以及视图的显示交互工作
下面为Mut添加支持多文档类型的功能(此时Mut本身是一个空的具有动态切分窗口的多文档程序):
1、添加新的文档类、子框架窗口类和视图类
一般来说,一个新的文档类型所需要的文档类、外边框窗口类和视图类有可能都不同于已经存在的类的,但也可能和现存的类相同(比如使用相同的子框架窗口)。因此,根据不同的需要会添加不同的类。这里,首先只添加一个新的视图类和子框架窗口类,而文档类则使用原来的类。
注意:多种文档类型的各个文档类一般是不同的
(1)添加新的视图类的方法可以参加《向当先工程中插入新类的两种方式》中的第二种方式,插入2DCAD程序中所用到的可以输入简单的文本的CTextView类。
(2)添加新的子框架窗口类CTextFrame,基类选择为CMDIChildWnd。具体方法参加向当先工程中插入新类的两种方式》中的第一种方式。
2、利用资源编辑器为新的文档类型添加字符串资源
参照Mut应用程序已经存在的文档模板字符串资源IDR_MUTTYPE,在字符串编辑器中增加一个新的字符串资源,指定ID为IDR_TEXTTYPE,内容为:
\nText\n文本视图窗口\nText Files(*.txt)\n.txt\nTextDocument\nText Document
可见文档模板字符串包含7个由“\n”结尾并分隔的子串,最后一个字符串后面不需要“\n”。这些子串描述了文档的类型,它们的含义按照顺序分别如下:
(1)窗口标题(2)文档名(3)新建文档类型名(4)过滤器名(5)文件扩展名(6)文档类型ID(7)注册文档类型名
3、利用资源编辑器添加附加的图标和菜单资源
(1)添加一个ID为IDR_TEXTTYPE的图标,可以利用画图工具编辑之
(2)添加一个ID为IDR_TEXTTYPE的菜单,将IDR_MUTTYPE菜单资源下的【文件】、【编辑】、【查看】、【帮助】菜单复制到IDR_TEXTTYPE中。注意:可以使用右键菜单中的Insert Copy...,然后再修改。
注意:这些资源的ID必须同第二步中创建文档模板字符串中所用的ID相同。
4、创建新的文档模板对象并加入到应用程序中
在应用程序类CMutApp的InitInstance函数中,参照应用程序框架生成的代码,创建另一个CMultiDocTemplate对象,并用CWinApp::AddDocTemplate成员函数注册该模板对象。
//这是应用程序框架生成的代码
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_MUTTYPE,
RUNTIME_CLASS(CMutDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMutView));
AddDocTemplate(pDocTemplate);
//创建新的文档模板对象
pDocTemplate = new CMultiDocTemplate(
IDR_TEXTTYPE, //新创建的字符串资源、图标资源和菜单资源
RUNTIME_CLASS(CMutDoc), //仍旧使用原来的文档类
RUNTIME_CLASS(CTextFrame), //新添加的子框架窗口类
RUNTIME_CLASS(CTextView)); //新添加的文本视图类
//将新的文档模板对象加入到应用程序中。
AddDocTemplate(pDocTemplate);
由于使用到了新的视图类CTextView和新的子框架窗口类CTextFrame,因此应该在Mut.cpp文件的开始部分包含它们的头文件:
#include "TextView.h"
#include "TextFrame.h"
注意:此时Mut程序就包含有两个文档模板对象,可以支持两种不同的文档了,但是,它们使用了相同的文档类,而实际应用中应该使用不同的文档类。
5、完成文档的序列化以及视图的显示交互工作
最后,根据用户的需要,可以为文档类添加上数据保存以及序列化功能,为视图类添加相应的显示数据和交换功能。
多文档的三叉静态切分:
这三个窗口的排列方式类似“|—”形状,所使用的视图类分别类似前面2DCAD工程中创建的CColorForm类以及Mut工程中创建的CTextView类和CMutView类。
1、添加新的文档模板对象
(1)添加新的视图类和子框架窗口类
在Mut工程中创建一个和2DCAD工程中相同的CFormView类的派生类CColorForm。
注意:可以使用文件复制再插入的方法,还需要将所需的对话框资源模板以及控件的ID值定义都复制过来。
再添加一个派生自CMDIChildWnd类的CThreeFrame类,作为文档的外边框窗口。
(2)编辑资源
添加ID都为IDR_THREETYPE的三个资源,字符串资源,图标资源和菜单资源。其中,字符串资源如下:
\nThree\n三叉切分窗口\nThree Files(*.thr)\n.txt\nThreeDocument\nThree Document
(3)创建新的文档模板对象
在应用程序类CMutApp的InitInstance函数中,参照应用程序框架生成的代码,创建另一个CMultiDocTemplate对象,并用CWinApp::AddDocTemplate成员函数注册该模板对象。
//创建三叉静态切分的文档模板对象
pDocTemplate = new CMultiDocTemplate(
IDR_THREETYPE, //用于三叉静态切分窗口的字符串资源、图标资源和菜单资源
RUNTIME_CLASS(CMutDoc), //仍旧使用原来的文档类
RUNTIME_CLASS(CThreeFrame), //新添加的三叉切分子框架窗口类
RUNTIME_CLASS(CColorForm)); //新添加的表单视图派生类
//将新的文档模板对象加入到应用程序中。
AddDocTemplate(pDocTemplate);
由于使用到了新的视图类CColorForm和新的子框架窗口类CThreeFrame,因此应该在Mut.cpp文件的开始部分包含它们的头文件:
#include "ColorForm.h"
#include "ThreeFrame.h"
2、添加三叉静态切分
(1)在子框架窗口类CThreeFrame中添加两个CSplitterWnd类的保护成员变量m_wndSplitter1和m_wndSplitter2,分别用于产生第一次静态切分和第二次静态切分。
(2)使用ClassWizard为CThreeFrame添加OnCreateClient成员函数。
(3)改写OnCreateClient成员函数代码如下:
BOOL CThreeFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
//使用CreateStatic函数产生第一次静态切分
m_wndSplitter1.CreateStatic(this, //父窗口指针
1, //行数
2); //列数,一行两列
//使用CreateView为第1个窗格产生CColorForm视图
m_wndSplitter1.CreateView(0, 0, //窗格的行、列序数
RUNTIME_CLASS(CColorForm), //表单视图类
CSize(250, 250), //初始化大小
pContext); //父窗口的创建参数
//再次使用CreateStatic函数对第一次切分产生的第二个窗口静态切分
m_wndSplitter2.CreateStatic(
&m_wndSplitter1, //父窗口是第一次的切分窗口
2, 1, //将这个窗口切分成两行一列
WS_CHILD | WS_VISIBLE, //窗口风格
m_wndSplitter1.IdFromRowCol(0, 1)
//指出新切分的窗口是上一次分隔的第一行第二个窗口
);
//使用CreateView为第二次切分的第1个窗格产生视图
m_wndSplitter2.CreateView(0, 0, RUNTIME_CLASS(CTextView),//使用文本视图类
CSize(150, 120), pContext);
//使用CreateView为第二次切分的第2个窗格产生视图
m_wndSplitter2.CreateView(1, 0, RUNTIME_CLASS(CMutView),//使用CMutView视图类
CSize(250, 250), pContext);
return 1; //不再调用基类的OnCreateClient函数
// return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
可见产生第一次切分的方法是,先使用CreateStatic函数产生第一次切分,然后再使用CreateStatic函数对第一次切分所产生的窗口再次切分。
注意:第二次调用CreateStatic函数切分时,函数的参数有些变化:一是父窗口是第一次的切分窗口,二是ID必须是上次切分窗格的ID。
(4)在ThreeFrame.cpp文件的开始部分包含下列头文件:
#include "MutDoc.h"
#include "MutView.h"
#include "TextView.h"
#include "ColorForm.h"