1、-1-本章目标 熟悉文档、视图对象的创建过程 熟悉文档/视图结构的消息传递过程 熟悉程序框架中的主要类及相互关系 熟悉文档类、视图类核心函数及作用 熟悉新建、保存和打开的实现的原理 掌握文档/视图程序的界面设计方法,包括,菜单栏、工具栏、状态栏 掌握文档/视图程序消息处理的方法 掌握文档/视图的文本编程方法,包括插入符创建、显示 掌握通过串行化的方法保存加载对象文档/视图框架 文档/视图结构是MFC中结构最复杂、体系最庞大而又最富有特色的部分。在文档/视图结构里,文档是一个应用程序基本数据元素的集合,它构成应用程序所使用的数据单元;视图是应用程序数据显示部分。使用MFC的程序向导(AppWiz
2、ard)可以方便的创建文档/视图框架程序。-2-文档/视图框架文档视图框架概念 Document在MFC的CDocument中被实例化,用来承载(容纳)数据,并至少提供专门负责文件读写操作的Serialize函数 View负责描述或呈现Document中的数据p负责数据的显示p负责数据的编辑 Frame是将UI的管理机制隔离出来,专门用于管理UI-3-文档/视图框架文档/视图结构相互作用文档和视图的关系-4-文档/视图框架Document Template的作用 CwinApp负责完成选择DocTemplate的重任,DocTemplate负责把MVC合成一个有机的整体./注册应用程序的文档模
3、板。/文档模板将用作文档、框架窗口和视图之间的连接CSingleDocTemplate*pDocTemplate;pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMytestDoc),RUNTIME_CLASS(CMainFrame),/主 SDI 框架窗口RUNTIME_CLASS(CMytestView);if(!pDocTemplate)return FALSE;AddDocTemplate(pDocTemplate);/分析标准外壳命令、DDE、打开文件操作的命令行./根据命令行中的信息启动程序./唯一的
4、一个窗口已初始化,因此显示它并对其进行更新.-5-文档/视图框架文档/视图结构概述 使用文档/视图结构的意义:p将数据操作、数据显示和用户界面分离开。这是一种“分而治之”的思想,这种思想使得模块划分更加合理、模块独立性更强。pMFC在文档/视图结构上提供了许多标准操作。如新建文件、打开文件、保存文件、打印等操作,它们被框架自动完成,减轻了程序员的工作量。-6-文档/视图框架文档/视图结构概述 不宜采用文档/视图结构的情况:p不是面向数据的或数据量很少的应用程序。典型的是一些工具程序,例如磁盘扫描程序、时钟程序。p不使用标准的窗口用户界面的程序。比如游戏程序。-7-文档/视图框架文档/视图结构概
5、述 单文档与多文档:pSDI程序中,用户在同一时刻只能操作一个文档。典型应用为Windows下的Notepad记事本程序。在这些应用中,打开新文档时需要关闭当前已打开的活动文档。pMDI程序中,允许用户同时操作多个文档。如VS2010就是多文档程序。在MDI程序中,用户可以通过切换活动窗口激活相应的文档进行操作。-8-文档/视图框架单文档主要类 单文档应用程序框架中,主要包含5个类:pSDICWinApp(应用程序类)pCFrameWnd(框架窗口类)pCView(视图类)pCDocument(文档类)pCDocTemplate(类文档模板)-9-文档/视图框架单文档主要类 单文档程序框架中的
6、类对象之间的关系:-10-文档/视图框架单文档主要类 SDI框架程序中各对象之间的访问:成员函数成员函数函数说明函数说明文档对象调用GetFirstViewPosition()函数和GetNextView()函数访问文档视图的列表调用GetDocTemplate()函数获取文档模板指针,以访问文档模板视图对象调用GetDocument()函数获得同视图关联的文档指针,以访问文档调用GetParentFrame()函数获取框架窗口指针,以访问框架窗口框架窗口对象调用GetActiveView()函数获取活动的视图指针,以访问视图调用GetActiveDocument()函数获取同当前视图相关联的
7、文档,以访问文档应用程序对象在程序的任何位置,调用全局函数AfxGetApp()可以获得CWinApp应用程序类指针,而通过AfxGetApp()-m_pMainWnd则可以获得框架窗口指针-11-文档/视图框架文档视图对象的创建过程./注册应用程序的文档模板。/文档模板将用作文档、框架窗口和视图之间的连接CSingleDocTemplate*pDocTemplate;pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMytestDoc),RUNTIME_CLASS(CMainFrame),/主 SDI 框架窗口R
8、UNTIME_CLASS(CMytestView);if(!pDocTemplate)return FALSE;AddDocTemplate(pDocTemplate);/分析标准外壳命令、DDE、打开文件操作的命令行./根据命令行中的信息启动程序./唯一的一个窗口已初始化,因此显示它并对其进行更新.-12-文档/视图框架文档视图核心函数 文档类核心函数:成员函数成员函数函数说明函数说明GetFirstViewPosition()这两个函数配合使用,访问同文档关联的视图列表。GetNextView()GetPathName()获得文档的文件名和路径,若文档未命名则返回空字符串GetTitle(
9、)获得文档的标题,若文档未命名则返回空字符串IsModified()若文档未保存数据,则返回非0,否则返回0SetModifiedFlags()设置或清除文档的修改标志,如果设置了修改标记,则系统自动实现关闭文档时,提示用户是否保存对文档的修改。UpdateAllViews()文档对应多个视图时,更新所有视图,它是通过调用每个视图类的OnUpdate()函数来更新所有当前文档关联的视图*OnNewDocument()当创建一个新文档时由应用程序框架调用(在AppWizard生成的用户程序中已自动重写该函数)。在派生类中重定义这个函数可以实现在新文档创建之前初始化文档对象,例如为指针申请内存空间
10、*OnOpenDocument()当从磁盘读入文档时由应用程序框架调用。在派生类中重写这个函数可以实现在新文档读入之前初始化文档的其他非序列化数据成员*DeleteContents()由应用程序框架调用,以删除文档内容。在派生类中重写这个函数可以实现在文档关闭之前释放文档所占用的内存和资源*Serialize()由应用程序框架调用(已自动重写)对文档进行串行化(详见后面章节),重写这个函数可以实现特定的保存或读入文档数据-13-文档/视图框架文档视图核心函数 视图类核心函数:成员函数成员函数函数说明函数说明GetDocument()每个视图对象只有一个文档与其相关联,用户可以通过成员函数Get
11、Document()获取与其关联的文档。此后,就可以在视图类中对文档类的公有成员函数及成员变量进行访问*OnDraw()实现文档数据的显示(已自动重写)。由于应用程序的数据存放在文档类中,因此在OnDraw()函数中可以调用GetDocument()函数返回指向文档类的指针,以访问文档中的数据*OnInitialUpdate()在视图与文档关联之后但在显示之前,或者用户选择了菜单“File”“New”或者“File”“Open”时,框架都将会调用此虚函数,重定义这个函数对重新读入或新创建文档的视图进行初始化*OnUpdate()当文档内容发生变化,视图需要更新时,调用该函数来实现窗口的重绘,它
12、将引发OnDraw()函数被调用-14-文档/视图框架在VIEW中画线 选择要添加的消息类 添加消息映射函数 添加类所需要的成员变量 在消息映射函数中画图-15-文档/视图框架文档视图界面设计 在MFC编程中,可以通过三种方法设计界面:p手工代码编程:功能最强大,可以实现几乎所有Windows支持的界面功能;pAppWizard:指在用向导生成程序时,通过向导提供的“用户界面功能”初步选择界面元素;p资源编辑器:设计界面更加直观,能实现大部分Windows支持的界面功能,也是最常见的设计界面的一种方法。-16-菜单编程菜单的概念-17-菜单编程菜单的开发步骤 简单的菜单编程一般可分为下面三个步
13、骤:编辑菜单资源,设置菜单属性(包括菜单名和ID);通过类的属性化编程自动映射菜单消息;手工编辑消息映射函数,加入菜单消息处理代码。-18-菜单编程菜单的创建与编辑-19-菜单编程菜单消息 MFC程序可以处理两种菜单消息:pWM_COMMAND:菜单命令消息。pUPDATE_COMMAND_UI:在菜单显示前,框架将发送该命令,以方便处理菜单状态的动态变化(例如是否要被设置选中标志、禁止菜单项等)。-20-菜单编程菜单消息 添加消息映射函数:-21-菜单编程菜单消息 添加菜单更新消息:-22-菜单编程为菜单项设置快捷键 有两种菜单快捷键:pALT快捷键:在填写菜单的“Caption”属性时用“
14、&”标示的快捷键字符。p程序全局快捷键:在程序运行的时候,不打开菜单,而是直接使用快捷键执行相应的菜单项-23-菜单编程为菜单项设置快捷键为菜单项添加快捷提示快捷键资源编辑器创建快捷键资源-24-菜单编程弹出菜单 创建弹出菜单资源:-25-菜单编程弹出菜单 弹出菜单的加载 void CMyNotepadView:OnRButtonUp(UINT nFlags,CPoint point)/TODO:在此添加消息处理程序代码和/或调用默认值CMenu menu;/使用CMenu类实例化菜单对象menu.LoadMenu(IDR_MYPOP_MENU);/加载菜单资源CMenu*pMenu=menu
15、.GetSubMenu(0);/0是索引值,这里指顶层菜单的第一项子菜单ClientToScreen(&point);/将以客户区坐标转换成屏幕坐标pMenu-TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);/弹出菜单CView:OnRButtonUp(nFlags,point);-26-工具栏编程工具栏CToolBar简介 CToolBar类中常用的函数及用途:类别类别函数名函数名用途用途工具栏构造相关函数Create()创建工具栏控件,并将它与CToolBar对象关联SetSize()设置按钮及其表明位图的大小SetHeight()设置
16、工具栏的高度LoadToolBar()载入工具栏资源LoadBitmap()载入位图SetBitmap()设置位图图像工具栏操作函数CommandToIndex()根据指定的命令ID,获取相应工具栏中的按钮序号GetItemID()获取指定序号的按钮或分隔符的命令IDGetItemRect()获取指定序号按钮所占显示区域的大小SetButtonStyle()设置按钮风格,例如下压式按钮、复选按钮或单选按钮GetButtonStyle()获取按钮风格SetButtonInfo()设置按钮的信息,包括ID风格位图序号等GetButtonInfo()获取按钮的信息SetButtonText()设置按
17、钮显示的文字GetButtonText()获取按钮上显示的文字-27-工具栏编程栏编辑和创建工具栏-28-工具栏编程加载工具栏 工具栏的创建工作是在CMainFrame:OnCreate()函数中完成的:int CMainFrame:OnCreate(LPCREATESTRUCT lpCreateStruct)./为窗口创建工具栏if(!m_wndToolBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC)|!m_w
18、ndToolBar.LoadToolBar(IDR_MAINFRAME)TRACE0(未能创建工具栏n);return-1;/未能创建./TODO:如果不需要可停靠工具栏,则删除这三行m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);return 0;-29-工具栏编程工具栏命令处理 与菜单命令相同,MFC程序可以处理两种工具栏按钮消息:命令消息WM_COMMAND和更新命令消息UPDATE_COMMAND_UI。通过类的属性窗口可以方便地为工具按钮添加这两种消息的映射函数,其实现过程与菜单项完全相同-30-状态栏编程CStatusBar类 类别类别函数名函
19、数名用途用途状态栏构造相关函数Create()创建状态栏控件,并将它与CStatusBar对象关联CreateEx()创建一个具有嵌入式CStatusBarCtrl对象附加风格的CStatusBar对象SetIndicator()根据ID指示器数组设置状态栏窗格状态栏操作函数CommandToIndex()根据指示器的字符串ID,获取相应状态栏中指示器序号GetItemID()根据指示器的索引获取字符串IDGetItemRect()根据指示器索引获取其大小位置SetPaneInfo()设置给定索引的指示器的风格和宽度等GetPaneInfo()获取指示器的信息SetPaneStyle()设置指
20、示器的风格GetPaneStyle()获取指示器的风格SetPaneText()设置指示器要显示的文本GetPaneText()获取指示器的文本-31-状态栏编程状态栏创建 CStatusBar m_wndStatusBar;在CMainFrame类中定义状态栏对象 在CMainFrame:OnCreate()函数中创建状态栏int CMainFrame:OnCreate(LPCREATESTRUCT lpCreateStruct).if(!m_wndStatusBar.Create(this)|!m_wndStatusBar.SetIndicators(indicators,sizeof(i
21、ndicators)/sizeof(UINT)TRACE0(未能创建状态栏n);return-1;/未能创建 .-32-状态栏编程状态栏编程/状态栏窗格数组static UINT indicators=ID_SEPARATOR,/状态行指示器ID_INDICATOR_CAPS,/显示Caps Lock键状态ID_INDICATOR_NUM,/显示Num Lock键状态ID_INDICATOR_SCRL,/显示Scroll Lock键状态 ID_INICATOR_CLOCK,/自定义,用于显示时间;状态栏窗格-33-文本编程创建插入符 void CreateSolidCaret(int nWid
22、th,int nHeight);创建插入符 显示插入符 ShowCaret();改变插入符的位置 static void PASCAL SetCaretPos(POINT point);-34-文本编程字符输入 void CMyNotepadView:OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)/存储用户输入的字符CString str(TCHAR)nChar);GetDocument()-m_strText+=str;/定义DCCClientDC dc(this);/获得字符串的宽度CSize sz=dc.GetTextExtent(GetDocum
23、ent()-m_strText);CPoint pt;pt.SetPoint(GetDocument()-m_ptCaretPos.x+sz.cx,GetDocument()-m_ptCaretPos.y);/显示插入符SetCaretPos(pt);/显示字符dc.TextOut(GetDocument()-m_ptCaretPos.x,GetDocument()-m_ptCaretPos.y,GetDocument()-m_strText);CView:OnChar(nChar,nRepCnt,nFlags);窗体响应字符输入的消息是WM_CHAR,消息映射函数的参数nChar即是来自键盘
24、的字符。-35-串行化串行化概述 串行化又称持久化,是储存和恢复对象的过程,在程序需要或结束的时候储存对象或者在程序激活时再恢复回来 串行化的基本思想是将一个类对象成员变量中的当前状态存储,之后也可以通过串行化的读取操作重新创建对象。在MFC中,能够支持串行化操作的类必须从CObject派生,并且必须重新改写(覆盖)继承自父类CObject的Serialize()函数。-36-串行化文档/视图结构对串行化的支持-37-串行化文档/视图结构对串行化的支持-38-串行化CArchive类 CArchive类没有基类,它提供了一种串行化对象从文件中进行读/写的类型安全的缓冲机制。使用CArchive
25、对象之前,必须先创建一个CFile对象,同时保证CArchive的读写标志设置(见CArchive构造函数说明)和文件打开方式相一致。对于一个CArchive对象,可以进行存储操作,也可以读取,但二者不能同时进行。实际的读写操作由与CArchive对象相关联的CFile或派生类对象来执行。可以把CArchive对象想象成一种二进制流,就像C+中的输入/输出流一样,可以顺序高效的处理二进制对象数据。-39-串行化CArchive类 构造CArchive对象CArchive(CFile*pFile,UINT nMode,int nBufSize=4096,void*lpBuf=NULL);-40-
26、串行化CArchive类 和操作符 BYTE bSomeByte;WORD wSomeWord;int iSomeint;./将数据写入ar对象相关联的文件ar bSomeByte wSomeWord和操作符对CObject派生类对象进行操作时,需要使用指针类型:CTestClass obj;/CTestClass类是可串行化的类,声明对象.arpObj;/当对可串行化的类进行串行化操作时,也可以直接调用Serialize()函数进行:CTestClass obj;/CTestClass类是可串行化的类,声明对象.obj.Serialize(ar);/对obj的地址进行操作,将obj写入ar对
27、象相关联的文件.CTestClass*pObj=new CTestClass;/CTestClass类是可串行化的类,声明指针/从ar所关联的文件中读取数据信息,并动态创建CTestClass对象给pObj指针pObj-Serialize(ar);-42-串行化CArchive类 判断CArchive读写状态/CMyNotepadDoc 序列化void CMyNotepadDoc:Serialize(CArchive&ar)if(ar.IsStoring()/TODO:在此添加存储代码else/TODO:在此添加加载代码-43-串行化CArchive类 当不再使用CArchive对象时,需要调
28、用CArchive的Close()成员函数关闭CArchive。Close()函数会将缓冲区的所有数据写到文件中,终止和文件的关联。-44-串行化可串行化的类 使一个类支持可串行化需要经过以下5个步骤:p 从CObject类派生,或从CObject派生的某个类派生;p 定义不带参数的构造函数;p 重写Serialize()成员函数;p 使用DECLARE_SERIAL宏,该宏要写在类的定义文件中(一般是在.h文件);p 使用IMPLEMENT_SERIAL宏,该宏要写在类的实现文件中(一般是在.cpp文件)。-45-串行化CObArray和CObList对串行化的支持 MFC中的CObArra
29、y和CObList类结合了DECLARE_SERIAL和IMPLEMENT_SERIAL宏支持串行化,以转储该集合类中的元素。这两个集合类都有一个Serialize()函数,要对CObArray和CObList类对象进行串行化,直接在文档类内的Serialize()函数内调用集合类的Serialize()函数即可。void CDemoDoc:Serialize(CArchive&ar)if(ar.IsStoring().else.m_obList.Serialize(ar);-46-小结 文档/视图框架是开发基于文档应用程序的基本框架 文档/视图框架中的文档是数据容器,视图是数据显示的窗口 文档/视图框架主要包括5个类:CWinApp、CFrameWnd、CView、CDocument、CDocTemplate 文档/视图框架中的框架窗口、文档、视图的创建是在InitInstance()函数内完成的 文档/视图框架的命令消息遵循MFC规定的路径进行循环处理 利用VS2008提供的属性化编程方法可以方便的给类添加消息映射函数和虚函数覆盖 串行化是MFC提供的一种将对象保存到文件或从文件读入到程序中的机制谢 谢 Thanks for listening.