MFC之文档/视图结构应用程序

文档/视图结构应用程序

一、文档/视图结构分析

  • MFC 通过多个类提供了对程序框架的支持,使用这些类可以简单地实现文档/视图结构;其中主要包括以下五个类:
  • 应用程序类(CWinAPP):是 MFC 程序的应用程序管理类,也是程序的入口类。
  • 文档模板类(CDocTemplate):用于管理应用程序的一组文档视图和框架。
  • 框架类(CMainFrame):用于管理 Windows 对话框类,宽假对话框会保存当前视图的指针,当其他视图被激活时,指针会随时更新。
  • 文档类(CDocument):保存用户数据,提供用户定义的文档类的基本功能。文档表示用户使用 File|Open 命令打开和使用 File|Save 命令保存的数据的单位。CDocument 类提供标准操作,如创建文档、装载文档和保存文档。框架使用 CDocument 定义的接口操作文档。
  • 视图类(CView):主要完成数据的显示功能。

1、文档/视图结构中的各个对象可以互相访问的其他对象

对象可以访问的对象
文档调用 GetFirstViewPosition() 函数和 GetNextView() 函数可以访问文档的视图列表;调用 GetDocTemplate() 函数可以获取文档模板。
视图调用 GetDocument() 函数可以获得与其相关的文档;调用 GetParentFrame() 函数可以获得框架对话框。
文档框架对话框调用 GetActiveView() 函数可以获得当前视图;调用 GetActiveDocument() 函数可以获得与当前视图相连的文档。
MDI 框架对话框调用 MDIGetActive() 函数可以获得当前激活的 CMDIChildWnd 对象。

2、全局函数

  • MFC 提供了下面几个用于访问 CWinAPP 对象和其他全局信息的全局函数

    函数名功能
    AfxGetApp()获取 CWinAPP 对象指针
    AfxGetInstanceHandle()获取当前应用程序的实例句柄
    AfxGetResourceHandle()获取应用程序的资源句柄
    AfxGetAppName()获取应用程序名称,等同于 CWinAPP 对象的 m_psExeName 成员变量。

3、InitInstance() 函数

BOOL CMFCApplication1App::InitInstance()
{
	// 如果一个运行在 Windows XP 上的应用程序清单指定要
	// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
	//则需要 InitCommonControlsEx()。  否则,将无法创建窗口。
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// 将它设置为包括所有要在应用程序中使用的
	// 公共控件类。
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);
	CWinAppEx::InitInstance();

	// 初始化 OLE 库
	if (!AfxOleInit())
	{
		AfxMessageBox(IDP_OLE_INIT_FAILED);
		return FALSE;
	}

	AfxEnableControlContainer();
	EnableTaskbarInteraction();

	// 使用 RichEdit 控件需要 AfxInitRichEdit2()
	// AfxInitRichEdit2();

	// 标准初始化
	// 如果未使用这些功能并希望减小
	// 最终可执行文件的大小,则应移除下列
	// 不需要的特定初始化例程
	// 更改用于存储设置的注册表项
	// TODO: 应适当修改该字符串,
	// 例如修改为公司或组织名
	//设置注册表键
	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
	LoadStdProfileSettings(4);  // 加载标准 INI 文件选项(包括 MRU),即加载标准配置设置

	// 注册应用程序的文档模板。  文档模板
	// 将用作文档、框架窗口和视图之间的连接
	CMultiDocTemplate* pDocTemplate;  //定义多文档变量
	pDocTemplate = new CMultiDocTemplate(IDR_MFCApplication1TYPE,
		RUNTIME_CLASS(CMFCApplication1Doc),
		RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
		RUNTIME_CLASS(CMFCApplication1View));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate); //增加到文档模板集合
	// 创建主 MDI 框架窗口
	CMainFrame* pMainFrame = new CMainFrame;  //定义 CMainFrame 类
	//装载框架
	if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
	{
		delete pMainFrame;
		return FALSE;
	}
	m_pMainWnd = pMainFrame;  //为主窗口类赋值

	// 分析标准 shell 命令、DDE、打开文件操作的命令行
	CCommandLineInfo cmdInfo;  //定义命令行信息变量
	ParseCommandLine(cmdInfo);  //解析命令行

	// 调度在命令行中指定的命令。  如果
	// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;
	// 主窗口已初始化,因此显示它并对其进行更新
	pMainFrame->ShowWindow(m_nCmdShow);  //显示主窗口
	pMainFrame->UpdateWindow();  //刷新主窗口

	return TRUE;
}

4、文档类、视图类核心函数

  • MFC 中,在主框架下可以通过文档类和视图类进行数据管理。

  • CDocument 类提供文档类的基本功能。在文档/视图结构中,当文档数据被修改时,其对应的每个视图都必须反映这些修改,CDocument 类提供 UpdateAllView() 成员函数通知视图文档有变化,因此视图可以根据数据重绘界面。

  • CDocument 类核心函数

    函数功能
    AddView()附加视图到文档中
    GetDocTemplate()返回描述文档类型的文档模板指针
    GetFirstViewPosition()返回与文档相关的第一个视图,可用于枚举
    GetNextView()枚举与文档相关的视图列表
    GetTitle()返回与文档相关的数据文件的路径
    IsModified()返回文档的标题
    RemoveView()返回表示自从上次保存后,文档是否被修改过
    SetModifiedFlag()从文档中卸载一个视图
    SetPathName()设置标记,表示自从上次保存后,文档修改过
    SetTitle()设置文档使用的数据文件的路径
    UpdateAllViews()设置文档标题
    CanCloseFrame()通知与文档相关的所有视图,文档数据发生了变化
    DeleteContents()当清除文档时调用此函数
    OnChangeViewList()向文档中添加视图或移除视图时调用此函数
    OnCloseDocument()当关闭文档时调用此函数
    OnNewDocument()当创建新文档时调用此函数
    OnOpenDocument()当打开已存在的文档时调用此函数
    OnSaveDocument()当保存文档到磁盘时调用此函数
    ReportSaveLoadException()当打开或保存文档发生异常时调用此函数
    GetFile()返回 CFile 对象的指针
    ReleaseFile()释放文件,可以被其他程序调用
    SaveModified()重载函数,询问用户是否可以保存文档
    PreCloseFrame()框架对话框关闭前调用的函数
    OnFileSendMail()通过邮件消息发送文档
    OnUpdateFileSendMail()如果支持邮件功能,则打开发送邮件命令
  • 从 CView 派生而来的视图类

    派生的视图类功能
    CView所有视图的基类
    CCtrlViewCTreeView、CListView、CEditView和CRichEditView类的基类,这些类允许文档/视图结构使用 Windows 标准控件初始化
    CEditView基于 Windows 编辑控件的简单视图;允许用户输入和编辑文本,用于创建简单的文本编辑程序
    CRichEditView包含 CRichEditCtrl 对象的视图;与 CEditView 类似,主要区别在于此类处理格式化文本
    CListView包含 CListCtrl 对象的视图
    CTreeView包含 CTreeCtrl 对象的视图
    CScrollViewCFormView、CRecordView 和 CDaoRecordView 的基类,实现滚动视图内容的功能
    CFormView包含控件的对话框视图,基于对话框的应用程序提供一个或多个这样的窗体接口。
    CHtmlViewWeb 浏览器视图,允许用户浏览 WWW 站点、本地文件系统和网络中的文件夹,也可用于作为活动文档容器
    CRecordView在控件中显示 ODBC 数据记录的窗体视图,与 CRowset 相连
    CDaoRecordView在控件中显示 DAO 数据记录的窗体视图,与 CDaoRecordset 相连
    COleDBRecordView在控件中显示 OLE DB 数据记录的窗体视图,与 CRowset 相连
  • 上述类中,除了 CCtrlView 和 CDaoRecordView 外,其余都可以在 MFC 的应用向导中使用。

  • CView 类核心函数

    函数功能
    DoPreparePrinting()显示打印对话框并创建打印机设备上下文,当重载 OnPreparePrinting() 成员函数时,会调用此函数
    GetDocument()返回与视图相关的文档
    OnInitialUpdate()当第一次视图附加到文档上时,调用此函数
    IsSelected()检测文档项是否被选择
    OnActivateView()当激活视图时,调用此函数
    OnActivateFrame()当激活包含视图的框架时,调用此函数
    OnBeginPrinting()当开始打印任务时,调用此函数
    OnDraw()在显示屏、打印或打印预览中渲染文档图像
    OnEndPrinting()当结束打印任务时,调用此函数
    OnEndPrintPreview()当退出打印预览时,调用此函数
    OnPrePareDC()当显示屏调用 OnDraw() 成员函数前,以及为打印或打印预览调用 OnPrint() 成员函数前调用此函数
    OnPreparePrinting()在打印或打印预览文当前,调用此函数
    OnPrint()打印或打印预览一页文档
    OnUpdate()通知视图文档内容已经修改

5、新建、保存和打开的实现

  • 文档/视图结构应用程序最要的是创建和管理文档;创建文档的方式有两种:

    1)使用 File|New命令创建新文档,由 CDocument类的 NoNewDocument 重载函数完成文档初始化。

    2)使用 File|Open 命令打开文档并从中读取内容,由 CDocument 类的 OnOpenDocument 重载函数完成文档初始化。

  • 创建文档后,需要创建视图并完成试图初始化,可以通过重写 CView 类的 OnInitialUpdate 成员函数完成初始化;如果要使文档内容每次发生改变时,都能重新初始化或调整视图显示则需要重载视图类的 OnUpdate 成员函数。

  • 文档的生命周期如下:

    1)调用文档的构造函数,动态创建文档对象;

    2)每个新文档都会调用 CDocument 类的 OnNewDocument 或 OnOpenDocument,在这两个函数里初始化自定义数据;

    3)在文档生命周期内,用户可以进行交互;如通过视图选择或修改文档数据,视图将这些修改传递给文档,文档保存修改并更新对应的视图显示;

    4)当文档关闭时,框架调用 DeleteContents 函数进行内存释放;

    5)调用文档类的析构函数。

6、多文档应用程序框架

  • 通常一个应用程序包含一个文档,对应一个视图,并统一由一个框架窗口管理;但有些应用程序需要同时支持多文档,主要分为以下情况
1、多文档类型应用程序框架
  • 每种文档类型由自己的文档类表示,并有与其对应的视图;当用户选择 File|New 命令时,框架显示一个对话框,在其中列出支持的文档类型,然后用户选择要创建的文档类型;每种文档类型由自己的文档模板对象管理。
  • 要创建额外的文档类,可以使用类向导新增一个派生自 CDocument 类的派生类,设置文档信息,然后实现新类的数据。
  • 要使框架识别文档类,则必须在应用程序的 InitInstance()重在函数中调用 AddDocTemplate() 函数将新文档类型注册到框架对象中。
2、多视图类型应用程序
  • 要实现多视图,文档对象要保存一个视图列表,提供增加视图或移除视图的成员函数,并提供 UpDateAllView 函数,允许当文档数据改变时,通知多个视图。MFC 中存在以下3种类型的多视图结构:

    1)在多文档框架中,支持多个同类型的视图对象。此种结构下,用户选择 New|Windows 命令打开相同文档的新视图框架,然后可以通过多个视图框架查看同一文档内容。

    2)在单文档框架中,支持多个同类型视图对象,即分割对话框——将单文档的视图空间分为多个视图。此种方式下,框架会从同一个视图类中创建多个视图对象。

    3)在单文档框架中,支持多个不同类型的视图对象。此方式下,多个视图共享单个框架对话框;视图从不同类中构造,每个视图提供对相同文档的不同方式的视图。如一个视图在普通模式下显示字处理文档,而另一个视图可以在全屏模式下显示。

二、开发文档/视图结构应用程序

1、目标

  • 为文档的多个视图同步数据
  • 管理和显示应用程序的数据
  • 提供文档数据的访问接口
  • 完成文件读写
  • 完成打印
  • 处理应用程序的命令和消息

2、创建基本程序框架

  • 视图的功能是图形化显示文档数据,并接收用户的输入,对视图的操作有以下几种:

    1)重写 OninitialUpdate() 函数完成视图类的特殊初始化。

    2)在视图类的 OnDraw() 成员函数中,编写显示文档数据的代码。

    3)重写 OnUpdate() 函数完成当视图需要重绘时的指定操作。

    4)连接适当的 Windows 消息和用户接口对象(如菜单项)到视图类的消息处理函数中。

    5)完成用户输入的处理。

    6)对于多页文档,必须重载 OnPreparePrint() 初始化打印对话框,传入打印页码和其他信息。

3、创建文档数据

  • 在派生自 CDocument 类的文档类中增加储存文档数据的变量,并重载 Serialize() 序列化成员函数,完成文档数据到磁盘文件的读写;此外,根据需要重载 OnNewDocument 和 OnOpenDocument 函数,并初始化文档类的数据成员;还需要重写 DeleteContents() 函数销毁动态分配的数据。
  • 通常做法是将文档数据成员变量在文档类中定义,并在文档类中设置和获取数据成员的成员函数操作文档数据或执行更复杂的操作;如定义一个 CString 类型的 m_data,定义一个 CString GetData() 成员函数获取 m_data 的值。
  • 视图通过 GetDocument 函数 获取文档指针访问文档对象,获取文档对象后,要确保将其转化成自己的文档类型,然后通过指针访问文档的公共成员函数;当需要频繁交换数据,或不希望使用文档的非共有成员时,可以将视图类定义为文档类的友元类。

4、绘图操作

  • 在文档/视图结构的应用程序中,几乎所有的绘制工作都在视图的 OnDraw 成员函数中实现,重写视图类的步骤:

    1)通过调用文档类中的获取文档数据的成员函数获取数据;

    2)通过调用框架传给 OnDraw 函数的设备上下文对象的成员函数显示数据。

  • 当文档数据发生变化时,视图必须重绘反映变化——通过调用 UpDataAllView 函数,而UpDateAllView 函数调用每个视图的 OnUpDate 函数。

  • MFC 为了提高重绘速度,在CDocument 类的 UpDateAllView 函数和 CView 的OnUpdate函数传入文档修改部分的信息,这样程序可以只定位到需要重绘的区域,OnUpDate 的函数原型:

    void MFCSampleView::OnUpdate(
    		CView* pSender,		//事件发生对象
    		LPARAM LHint,		//通过此参数可以传入需要的数据
    		CObject* pHint		//通过此参数可以传入继承自CObject类的派生类对象
    							)
    
  • 当视图需要重绘,操作系统会给视图发送 WM_PAINT 消息,而视图的 OnPaint() 处理函数会响应此消息,通过创建 CPainDC 类的设备上下文对象,并调用 OnDraw 函数;一般 OnPain 不需要重写。

  • 设备上下文DC包含设备绘制属性,如显示器或打印机信息;要在屏幕上绘制,通过OnDraw传入的CPainDC对象,要在打印机上绘制,通过OnDraw传入的CDC对象建立当前打印机的链接。

  • 在视图绘制时,首先要获取文档指针,然后通过设备上下文绘制,下面显示了OnDraw函数的处理过程

    void C文档视图应用程序View::OnDraw(CDC* pDC)
    {
    	C文档视图应用程序Doc* pDoc = GetDocument(); //获取与视图相关的文档
    	ASSERT_VALID(pDoc);  //验证文档对象的有效性
    	if (!pDoc)
    		return;
    
    	// TODO: 在此处为本机数据添加绘制代码
    	CString data = pDoc->GetData(); //获取文档数据
    	CRect rect;
    	GetClientRect(&rect); //获取客户区域
    	//设置文本对齐方式为居中
    	pDC->SetTextAlign(TA_BASELINE | TA_CENTER);
    	//在客户区中间绘制
    	pDC->TextOut(rect.right / 2, rect.bottom / 2, data, data.GetLength());
    }
    

    [外链图片转存失败(img-zLf9awNV-1564448097906)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1557745348295.png)]

5、文档序列化 CArchive

  • 序列化就是从持久存储媒体(如磁盘)中读数据或向其中写数据的过程。

  • 基本思想就是对象可以记录当前状态,由成员变量标识序列化存储;之后,对象可以通过序列化存储中读取或反序列化对象状态重新创建对象。

  • 关键点就是对象本身负责读取和记录自身状态。

  • MFC 中,通过 CArchive 归档对象实现序列化,可以满足很多应用程序的需求,如读取整个文件到内存的应用程序;但是有时候使用归档对象是不能满足需要的,如数据库程序,只编辑大文件的一部分,程序只能写入文本文件,并且多个程序共享数据会发生访问冲突,此时可以使用 CFile 对象实现序列化。

  • 使用 CFile 类的 Open()、Read()、Write()、Close()和Seek() 成员函数打开文件、移动文件中指定的文件指针、在指定点读取记录。

  • 可以使用 CArchive 类的 GetFile 成员函数获取 CFile 对象的指针。

  • 默认情况下,CDocument 类使用序列化处理 File 菜单的 Save 和 Save As 命令;MFC 框架在 CDocument 类生成了 Serialize() 成员函数提供对序列化的默认实现,只需写入相应的处理代码。

  • MFC 中,在要序列化的对象和存储媒体之间,使用 CArchive 类对象作为中间对象并总是与 CFile 对象相连,包含序列化必须的信息——文件名以及请求操作类型(读操作、写操作)。

  • 案例:

    void C文档视图应用程序Doc::Serialize(CArchive& ar)
    {
    	if (ar.IsStoring())
    	{
    		// TODO:  在此添加存储代码
    		ar << m_data;
    	}
    	else
    	{
    		// TODO:  在此添加加载代码
    		ar >> m_data;
    	}
    }
    

[外链图片转存失败(img-3ClofU60-1564448097907)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1557824461394.png)]

6、让文档/视图结构支持滚动条

  • MFC 支持在视图中使用滚动条和自动计算显示的框架对话框的大小。

  • 任何视图都可以在 OnHScroll() 和 OnVScroll() 成员函数中处理滚动条消息。

  • 在 MFC 中实现支持滚动条的视图,按以下步骤操作即可:

    1)将需要支持滚动条的视图类的基类修改为 CScrollView,可以通过替换 CView 基类实现。

    2)在视图类增加存储区的 CRect 成员变量,用于记录视图显示区域。

    3)修改视图类的构造函数和 OnInitialUpDate() 函数,在其中设置滚动条参数。

    C文档视图应用程序View::C文档视图应用程序View() noexcept
    {
    	// TODO: 在此处添加构造代码
    	m_rect = CRect(0, 0, 1024 * 3, -1024 * 3); //初始化视图区域
    
    }
    
    void C文档视图应用程序View::OnInitialUpdate()
    {
    	CScrollView::OnInitialUpdate();
    	//定义逻辑窗口大小为 40cm X 30cm
    	CSize sizeTotal(40000, 30000);
    	//定义每页大小为10cm X 10cm
    	CSize sizePage(sizeTotal.cx / 4, sizeTotal.cy / 4);
    	//定义每行大小为0.4cm X 0.3cm
    	CSize sizeLine(sizeTotal.cx / 100, sizeTotal.cy / 100);
    	//设置滚动参数
    	SetScrollSizes(MM_HIMETRIC, sizeTotal, sizePage, sizeLine);
    }
    

    4)在 OnKeyDown() 按键处理函数中处理滚动按键的单击事件。

    void C文档视图应用程序View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	switch (nChar)
    	{
    	case VK_UP:  //按向上按键
    		OnVScroll(SB_LINEUP, 0, NULL);
    		break;
    	case VK_DOWN:  //按向下按键
    		OnScroll(SB_LINEDOWN, 0, NULL);
    		break;
    	case VK_LEFT:  //按向左按键
    		OnHScroll(SB_LINELEFT, 0, NULL);
    		break;
    	case VK_RIGHT:  //按向右按键
    		OnHScroll(SB_LINERIGHT, 0, NULL);
    		break;
    	case VK_PRIOR:  //上页按键
    		OnVScroll(SB_PAGEUP, 0, NULL);
    		break;
    	case VK_NEXT:  //下页按键
    		OnVScroll(SB_PAGEDOWN, 0, NULL);
    		break;
    	case VK_HOME:  //开始按键
    		OnVScroll(SB_TOP, 0, NULL);
    		OnHScroll(SB_LEFT, 0, NULL);
    		break;
    	case VK_END:  //结束按键
    		OnVScroll(SB_BOTTOM, 0, NULL);
    		OnHScroll(SB_RIGHT, 0, NULL);
    		break;
    	default:
    		break;
    	}
    	CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
    }
    

    5)在视图类的 OnDraw() 函数中绘制数据内容。

    pDC->Ellipse(m_rect); //在文本中绘制圆形
    

    6)在 OnLButtonDown() 函数中处理 WM_LBUTTONDOWN 消息

    void C文档视图应用程序View::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	CClientDC dc(this);  //获取客户区上下文
    	OnPrepareDC(&dc); //转换上下文坐标
    	CRect rectDevice = m_rect;
    	//将设备区域从逻辑坐标转换为设备坐标
    	dc.LPtoDP(rectDevice);
    	InvalidateRect(rectDevice);  //重绘设备区域
    	CScrollView::OnLButtonDown(nFlags, point);
    }
    

三、对话框分割与多视图应用

1、对话框分割基础知识

  • MFC 提供 CSplitterWnd 类支持对框架窗口的分割,可以将框架窗口分割为两个或多个可滚动的面板。

  • 对话框分割分为两种:

    1)动态分割:允许用户将当前窗口分割为多个面板,通过滚动条查看文档的不同部分,同时允许用户动态删除分割窗口;但是动态分割后的各个窗口对应的视图类的类型必须是相同的,而且采用动态分割最多将视图分割为2*2子窗口。

    2)静态分割:是指在程序启动时,就将窗口分割好了,每个分割后的窗口作用可以是不同的,因为静态分割允许每个面板对应的视图类型是不同的,支持将视图分割为 16*16 个子窗口。

2、动态分割窗口的实现

  • 首先在 MFC 应用程序向导中,单击“用户界面功能”按钮,选择“拆分窗口”复选框。

  • 在经过上面步骤后,程序会自动添加对分割条的支持,会在框架类中定义 CSplitterWnd 类对象 m_wndSplitter,并在框架类的 OnCreateClent() 函数中调用 Create() 函数来动态分割对话框;在 Create 函数中需要指定当面板太小不能显示全部内容时的最小行高和列宽。

  • 调用 Create 函数后,还可以通过调用 SetColumnInfo 函数和 SetRowInfo 函数调整最小值。

    BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
    {
    	return m_wndSplitter.Create(this,
    		2, 2,			// TODO: 调整行数和列数
    		CSize(100, 100),	// TODO: 调整最小窗格大小
    		pContext);
    }
    

    [外链图片转存失败(img-75g2q81P-1564448097908)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1557836617094.png)]

3、多视图的实现

  • 创建静态分割对话框步骤:

    1)同动态分割对话框一样

    2)根据需要添加 CSplitterWnd 成员变量,例如三行三列的对话框分割,则需要定义两个 CSplitterWnd 成员变量—— m_wndSplitter1 和 m_wndSplitter2

    3)重载父框架类的 CFrameWnd::OnCreateClient() 成员函数,在其中调用 CSplitterWnd 类的 CreateStatic() 成员函数。

    BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
    {
     //创建一个静态分栏窗口,分为三行一列 
    	if (m_wndSplitter1.CreateStatic(this, 3, 1) == NULL)
    		return FALSE;
    	// 将CView1连接到0行0列窗格上
    	m_wndSplitter1.CreateView(0, 0, RUNTIME_CLASS(View1), CSize(10, 10), pContext);
    	// 将CView2连接到1行0列窗格上
    	m_wndSplitter1.CreateView(1, 0, RUNTIME_CLASS(CView2), CSize(10, 10), pContext);
    	// 将第2行再分开1行3列
    	if (m_wndSplitter2.CreateStatic(&m_wndSplitter1, 1, 3, WS_CHILD | WS_VISIBLE,
    		m_wndSplitter1.IdFromRowCol(2, 0)) == NULL)
    		return FALSE;
    	//将CView3类连接到第3行的第1列
    	m_wndSplitter2.CreateView(0, 0, RUNTIME_CLASS(CView3), CSize(300, 200), pContext);
    	//将CView4类连接到第3行的第2列
    	m_wndSplitter2.CreateView(0, 1, RUNTIME_CLASS(CView3), CSize(300, 200), pContext);
    	//将CView5类连接到第3行的第3列
    	m_wndSplitter2.CreateView(0, 2, RUNTIME_CLASS(CView5), CSize(300, 200), pContext);
    	return TRUE;
    	//return m_wndSplitter.Create(this,
    	//	2, 2,			// TODO: 调整行数和列数
    	//	CSize(10, 10),	// TODO: 调整最小窗格大小
    	//	pContext);
    }
    

四、文档/视图应用程序实例

//MDISampleDoc.h
CString m_Data;
//MDISampleDoc.cpp
m_Data = "Hello MDI!";

//View1.h
#pragma once
#include <afxext.h>
#include"MDISampleDoc.h"


class CView1 : public CEditView
{
protected:
	CView1();           // protected constructor used by dynamic creation
	DECLARE_DYNCREATE(CView1)

	// Attributes
public:

	// Operations
public:

	// Overrides
		// ClassWizard generated virtual function overrides
		//{{AFX_VIRTUAL(CView1)
public:
	virtual void OnInitialUpdate();
protected:
	virtual void OnDraw(CDC* pDC);      // overridden to draw this view
	//}}AFX_VIRTUAL

// Implementation
protected:
	virtual ~CView1();
	CMDISampleDoc* GetDocument(); // non-debug version is inline

#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

	// Generated message map functions
protected:
	//{{AFX_MSG(CView1)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//View1.cpp
#include "stdafx.h"
#include "View1.h"
#include"MDISample.h"
#include"MDISampleDoc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

IMPLEMENT_DYNCREATE(CView1, CEditView)

CView1::CView1()
{
}


CView1::~CView1()
{
}

BEGIN_MESSAGE_MAP(CView1, CEditView)
	//{{AFX_MSG_MAP(CView1)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CView1 drawing

void CView1::OnDraw(CDC* pDC)
{

}

/
// CView1 diagnostics

#ifdef _DEBUG
void CView1::AssertValid() const
{
	CEditView::AssertValid();
}

void CView1::Dump(CDumpContext& dc) const
{
	CEditView::Dump(dc);
}
#endif //_DEBUG

/
// CView1 message handlers

CMDISampleDoc* CView1::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMDISampleDoc)));
	return (CMDISampleDoc*)m_pDocument;
}

void CView1::OnInitialUpdate()
{
	CEditView::OnInitialUpdate();
	CMDISampleDoc* pDoc = GetDocument();
	this->GetEditCtrl().SetWindowText(L"这里是编辑框视图CView1,文档内容=" + pDoc->m_Data);
}
//View2.h
#pragma once
#include <afxext.h>
#include"MDISampleDoc.h"
#include"resource.h"

class CView2 : public CEditView
{
protected:
	CView2();           // protected constructor used by dynamic creation
	DECLARE_DYNCREATE(CView2)

	// Attributes
public:

	// Operations
public:

	// Overrides
		// ClassWizard generated virtual function overrides
		//{{AFX_VIRTUAL(CView1)
public:
	virtual void OnInitialUpdate();
protected:
	virtual void OnDraw(CDC* pDC);      // overridden to draw this view
	//}}AFX_VIRTUAL

// Implementation
protected:
	virtual ~CView2();
	CMDISampleDoc* GetDocument(); // non-debug version is inline

#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

	// Generated message map functions
protected:
	//{{AFX_MSG(CView1)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//View2.cpp
#include "stdafx.h"
#include "View2.h"
#include"MDISample.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CView1

IMPLEMENT_DYNCREATE(CView2, CEditView)

CView2::CView2()
{
}

CView2::~CView2()
{
}


BEGIN_MESSAGE_MAP(CView2, CEditView)
	//{{AFX_MSG_MAP(CView1)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CView1 drawing

void CView2::OnDraw(CDC* pDC)
{

}

/
// CView1 diagnostics

#ifdef _DEBUG
void CView2::AssertValid() const
{
	CEditView::AssertValid();
}

void CView2::Dump(CDumpContext& dc) const
{
	CEditView::Dump(dc);
}
#endif //_DEBUG

/
// CView1 message handlers

CMDISampleDoc* CView2::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMDISampleDoc)));
	return (CMDISampleDoc*)m_pDocument;
}

void CView2::OnInitialUpdate()
{
	CEditView::OnInitialUpdate();
	CMDISampleDoc* pDoc = GetDocument();
	this->GetEditCtrl().SetWindowText(L"若道天涯无回路,漫漫黄沙掩枯骨!文档内容=" + pDoc->m_Data);
}
//MDISampleView.cpp
void CMDISampleView::OnDraw(CDC* pDC)
{ 
	CMDISampleDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	pDC->TextOutW(0, 0, L"高楼独上,望尽天涯路,欲寄彩笺兼尺素,山长水阔知何处。文档内容=" + pDoc->m_Data);
}

//MDISample.cpp
BOOL CMDISampleApp::InitInstance()
{
    //....
    // 注册应用程序的文档模板。  文档模板
	// 将用作文档、框架窗口和视图之间的连接
	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_MDISampleTYPE,
		RUNTIME_CLASS(CMDISampleDoc),
		RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
		RUNTIME_CLASS(CMDISampleView));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);

	CMultiDocTemplate* pDocTemplate1;
	pDocTemplate1 = new CMultiDocTemplate(IDR_MENU1,
		RUNTIME_CLASS(CMDISampleDoc),
		RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
		RUNTIME_CLASS(CView1));
	if (!pDocTemplate1)
		return FALSE;
	AddDocTemplate(pDocTemplate1);

	CMultiDocTemplate* pDocTemplate2;
	pDocTemplate2 = new CMultiDocTemplate(IDR_MENU2,
		RUNTIME_CLASS(CMDISampleDoc),
		RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
		RUNTIME_CLASS(CView2));
	AddDocTemplate(pDocTemplate2);
//...
}
  • 4
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
文档视图结构(Document/View Architecture)是MFC的精髓,也是Observer模式的具体实现框架之一, Document/View Architecture通过将数据和其表示分开,提供了很好的数据层次和表现层次的解耦。然而, 虽然我们使用MFC AppWizard就可以很轻松地获得一个支持Document/View Architecture的MFC程序框架, Document/View Architecture本身的复杂性加上VC产生的一系列代码足够让我们眼花缭乱,一时陷入云里雾里不可自拔。 加上我们更多的人都没有经过Windows SDK Programming 的学习和经历就直接进行MFC Programming的学习和开发, 更是面对一堆的代码不知所措。 之于Document/View Architecture,侯捷先生的《深入浅出MFC》一书确实进行了很深入的分析和研究,网络上也有很多在侯捷先生著述 的基础上的进一步的文章出现,但是个人觉得这里面有一点瑕疵(仅代表k_eckel浅见):太过深入, 这些分析和研究都最终会定位到Windows SDK中窗口的创建过程、MFC中对Document/View Architecture支持所提供的复杂无序的宏等 对于没有Windows SDK Programming 经验和经历的学习者和对于MFC不是很熟悉的学习者无异于是徒增烦恼,一个本来就很复杂的问题 更加地复杂化了。我的观点是这个过程是必要的,然而不是每个人所必需的,或者说不是每个人在初期学习和绝大多数项目开发中所 必需的。我向来对众多的仅仅学会了拖拉点拽就以为会了MFC(侯捷先生在《深入浅出MFC》一书中对这个群体有一个照面,这里不罗嗦) 不以为然,但是我依然认为轻量级学习成本是重要的,容易上手,易于接受是一门技术成功或者说有价值的一个很大的决定性因素。 因此提供一个轻量级的学习过程对于学习来说是很有必要性的,本系列文章就遵循这样一个理念,对MFC中Document/View Architecture 进行一个分析(姑且也可以称之为深入),尽量将对Document/View Architecture的理论研究(侯捷先生书中很多内容)和实际的项目 开发结合起来,最后提供一个简单但是全面的Document/View Architecture项目开发(主要是界面框架设计和开发)的实际例子,供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御承扬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值