Visual C++面向对象与可视化程序设计(第5版)课件 -07-resource-mfc_第1页
Visual C++面向对象与可视化程序设计(第5版)课件 -07-resource-mfc_第2页
Visual C++面向对象与可视化程序设计(第5版)课件 -07-resource-mfc_第3页
Visual C++面向对象与可视化程序设计(第5版)课件 -07-resource-mfc_第4页
Visual C++面向对象与可视化程序设计(第5版)课件 -07-resource-mfc_第5页
已阅读5页,还剩90页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1第7章文档与资源的应用2VC的MFC库支持三种不同的应用程序单文档界面(SDI)多文档界面(MDI)基于对话框的应用程序SDI的应用程序只有一个窗口MDI的应用程序每次可以读写多个文件或文档,可同时对多个文档进行操作,可以有多个子窗口主要差别在创建SDI界面的应用程序时,不生成CChildFrame类,CMainFrame类的基类为CFrameWnd在创建MDI界面的应用程序时,CMainFrame类的基类为CMDIFrameWnd使用AppWizard创建SDI和MDI界面的应用程序的过程几乎完全一样7.1文档操作中的几个关键概念3MFC对“文档”的设计思想是:一个类中的所有成员变量,可以通过文档/视图的串行化(Serialize)机制,既能够保存到一个文件中去,也能够从一个文件中读出并加载到该类相应的成员变量中去。在MFC中,文档类负责数据的管理,提供保存和加载数据的功能;视图类负责数据的显示,以及给用户提供对数据的编辑和修改功能。MFC给我们提供Document/View结构,将一个应用程序所需要的“数据处理与显示”的函数空壳(构架)都设计好了,但这些函数都是虚函数,我们可以在派生类中重写这些函数。那些与文件读写有关的操作在CDocument的Serialize函数中进行,与数据和图形显示有关的操作在CView的OnDraw函数中进行。因此我们仅需要关注Serialize和OnDraw函数就可以了。47.1.1文档/视图的概念

利用应用程序向导生成单文档和多文档程序框架时,由它所创建的各个类在一起工作,构成一个相互关联的结构,称此结构为文档/视图结构。在这个框架中,数据的维护及其显示,分别由两个不同但又彼此紧密相关的类——文档类和视图类负责。由CWinApp类派生的应用程序对象(即一个运行的应用程序)扮演几种角色,管理应用程序的初始化、负责保持文档、视图、框架窗口类之间的关系、接收Windows消息、将消息调度到需要的目标窗口。5CWinApp类

CWinApp类代表主程序,CWinApp本身是不可见的,它负责维护进程的启动、终止、消息循环、命令行参数、资源管理MFC中的主应用程序类封装用于Windows操作系统的应用程序的初始化、运行和终止。基于框架生成的应用程序必须有且仅有一个从CWinApp派生的类的对象。在创建窗口之前先构造该对象。部分常用的CWinApp类常用的成员成员描述m_hInstance当前实例句柄m_bHelpMode这是一个布尔值,当为“真”时,支持Shift+F1作为“帮助”的快捷键m_lpCmdLine命令行参数m_nCmdShow窗口初始化状态参数m_pszExeName可执行文件名m_pszProfileName基于应用程序名的缺省名字m_pszHelpFilePath缺省文件路径m_pszRegistrKey配置注册表主键值LoadCursor加载光标资源LoadStandardCursor加载标准光标LoadOEMCursor加载OEM光标LoadIcon加载图标资源GetProfileInt从配置文件返回一整数值WriteProfileInt向配置文件写入一整整数值GetProfileString从配置文件返回一字符串AddDocTemplate添加一个文档模板AddToRecentFileList向“最近打开的文件”菜单项添加一个字符串CreatePrinterDC从系统缺省的打印机上创建一个DCGetPrinterDeviceDefaults获取缺省打印设备OnFileNew()创建新文件OnFileOpen()打开一个文件7CWinApp常用方法LoadCursor载入光标资源LoadIcon载入图标资源LoadStandardIcon载入WINDOWS,H中IDI_常量所指定的Windows预定义图标LoadOEMIcon载入WINDOWS,H中OIC_常量所指定的WindowsOEM预定义图标ParseCommandLine解析命令行中的每个参数和标志ProcessShellCommand处理命令行参数和标志GetProfileInt从应用程序的,INI文件的一个入口中获取一个,整数WriteProfileInt将一个整数写到应用程序的,INI文件的入口GetProfileString从应用程序的,INI文件的一个入口中获取一个字符串WriteProfileString将一个字符串写到应用程序的,INI文件的入口AddDocTemplate将一个文档模板加到应用程序的可用文档模板列表中GetFirstDocTemplatePosition获取第一个文档模板的位置GetNextDocTemplate获得文档模板的位置,可以递归调用OpenDocumentFile由框架调用,用以从文件打开一个文档SelectPrinter选择先前由用户在打印对话框中指定的打印机8命令操作函数OnFileNew实现ID_FILE_NEW命令OnFileOpen实现ID_FILE_OPEN命令OnFilePrintSetup实现ID_FILE_PRINT_SETUP命令OnContextHelp处理,应用程序,内的SHIFT+F1命令OnHelp处理应用程序内的F1帮助命令(使用当前的上下文)OnHelpIndex处理ID_HELP_INDEX命令,提供缺省的帮助主题OnHelpFinder处理ID_HELP_FINDER和ID_DEFAULT_HELP命令OnHelpUsing处理ID_HELP_USING命令文档是用来保存数据以及关于数据的处理的,每当SDI/MDI响应File(Open)/File(New)的时候都会打开一份文档。文档可以拥有多个视图。文档的任务是对数据进行管理和维护,数据通常被保存在文档类的成员变量中。CDocument类10最常用的是SetModifiedFlag()和UpdateAllViews()。文档内容被修改后,一般要调用SetModifiedFlag()来设定一个标志。UpdateAllView()刷新所有和文档关联的视图。实际上该函数调用各个视图类的OnUpdate()函数。这样做可以保证各个视图之间的同步。从CDocument类派生新的文档类的一般过程如下:为每一个文档类型从CDocument类派生一个相应的文档类。为文档类添加成员变量,这些变量主要用来保存文档的数据,并使其它的对象(如视图对象)可以访问这些成员变量,从而实现文档和视图的相互搭配使用。重载Serialize成员函数,实现文档数据的串行化。

无论是保存文档或是打开文档,应用程序都是通过调用文档类的Serialize串行化成员函数来完成操作的。因此,在大多数情况下,我们都需要重载Serialize成员函数。Serialize成员函数带有一个CArchive类型的参数,这是一个与所打开的文件相关联的对象。一般情况下,总是使用CArchive对象来保存和打开文档。

视图在Windows中就是一个窗口,也就是一个可视化的矩形区域。视图是用来表示文档的数据的。但是每个视图必需依附于一个框架(SDI中是MainFrame,MDI是ChildFrame)。视图类是文档和用户之间的中介。视图可以直接或间接的访问文档类中的这些成员变量,它从文档类中将数据读出来,然后在屏幕上显示。值得注意的是,虽然每个文档可以有多个视图,但每个视图只能对应于一个确定的文档。至于说框架,它实际上也是一个Windows窗口。但是在框架上可以放置菜单、工具栏、状态栏等。而视图则放在框架的客户区。因此MFC中我们看到的窗口实际上Frame和View共同作用的结果。7.1.2SDI程序中文档、视图对象的关联关系13SDI程序中框架窗口、文档和视图的关联是在应用程序类的InitInstance()成员函数中通过文档模板类完成的,通过下述代码注册应用程序的文档模板。文档模板将用作文档、框架窗口和视图之间的连接。我们先尝试创建一个工程文件名为7-1的基于单文档的应用程序。这时,系统生成了一个名为CMy71App的类,考察这个类下面的成员函数InitInstance(),发现包含了如下代码:pDocTemplate=newCSingleDocTemplate(//创建单文档模板类对象

IDR_MAINFRAME,

RUNTIME_CLASS(CMy71Doc),//CMy71Doc是应用程序中的文档类RUNTIME_CLASS(CMainFrame),//CMainFrame是应用程序中的框架窗口

RUNTIME_CLASS(CMy71View));//CMy71View是应用程序中的视图类

if(!pDocTemplate) returnFALSE;AddDocTemplate(pDocTemplate);//加载文档模板类对象到文档模板列表从上面的程序中可以看到,系统首先创建了一个单文档模板类,该类主要用来将程序中的文档类、视图类和框架窗口类联系在一起进行管理。在单文档模板类的构造函数的参数中含有资源的ID和文档、视图和框架窗口的类名和RUNTIME_CLASS宏。该宏对于所制定的类返回指向CRuntimeClass的指针,主要目的是使得主结构可以在运行的时候动态创建这些类的对象。157.1.3CView类视图类(CView)是从CWnd类下派生的视图类具有CWnd的所有功能如:创建、移动、显示和隐藏窗口等。CView类可以接收任何Windows消息,而CDocument类则不行。它提供了文档类所需要的最基本的功能实现。IsSelected():确定文档是否被选中OnScroll():当用户滚动时,CView的响应OnInitialUpdate():在类第一次构造后由MFC调用OnDraw():由MFC调用发出文档到设备描述表OnUpdate():由MFC调用对文档的修改进行响应OnPrepareDC():在调用OnDraw()前允许修改设备描述表由MFC调用CView主要虚拟方法16方法说明GetDocument()返回与视图关联的文档DoPreparePrinting()显示打印对话框并创建打印环境,当重写OnPreparePrinting成员函数时调用。IsSelected()确定文档项是否被选中OnDragEnter当项目首次拖动到视图的拖放区域时调用OnDragLeave当项目离开拖动到视图的拖放区域时调用OnDrop当项目被放置到视图的拖放区域时调用OnScroll()当在视图中需要滚动对象时调用OnInitialUpdate()当视图第一次与文档关联时调用,进行初始化操作OnPrepareDC()在为屏幕显示调用OnDraw成员函数之前或在为打印或打印预览调用OnPrint成员函数之前调用。CView的方法

CView类中最常用的是OnDraw函数,该函数在屏幕发生变化或因为焦点的变化需要重绘时调用,没有该函数,就不可能在程序的切换后保证屏幕的正确显示。OnDraw:只要是需要重绘的时候都会调用OnDraw,无论是往屏幕画还是往打印机画.WM_PAINT:只负责往屏幕上绘制,不负责往打印机打印的。注意:尽量不要在OnDraw之外的函数调用绘图方法,那些方法不会在视图需要重新绘制的时候被自动调用。若想在数据更新的时强制视图更新,可调用Invalidate方法和UpdateWindow方法来实现。OnUpdate函数会在每次视图数据更新的时候被调用,对维护程序的正确显示负有重要的责任。函数UpdateAllViews则是实现单文档多视图程序所不可缺少的手段(在一个文档的任一视图发生变化时,通过该类实现各视图的正确显示)。CEditView:简单的文本编辑器,类似NotepadCListView:基于列表的视图,类似文件夹浏览CTreeView:基于树状控件的视图,类似文件浏览左侧的树状结构CRichEditView:支持多种字体、OLE和RTF格式的高级编辑器CScrollView:支持滚动条的视图CFormView:窗体视图,支持在上边使用对话框控件CRecordView:连接到ODBC数据库的视图CDaoRecordView:连接到DAO数据库的视图CView的子类7.1.4串行化处理20

文档操作中经常要遇到串行化处理、文档的消息映射、文档消息传递和文件的打开保存等基本操作。

所谓串行化,是一种用于对对象进行文件I/O的一种机制,描述将对象写入字节流和从字节流恢复对象的操作。

之所以使用字节流而不是使用文件,是因为串行化除了可以使用文件保存对象之外,还可以通过网络、串口传输对象。

如上面介绍的我们创建了一个基于单文档的名为7-1的应用程序,在系统生成的CMy71Doc类中,有如下代码:voidCMy71Doc::Serialize(CArchive&ar){ if(ar.IsStoring()) { //TODO:在此添加存储代码 } else { //TODO:在此添加加载代码 }}成员 描述WriteString 写入字符串ReadString 读取字符串ReadClass 读取类信息WriteClass 写入类信息Close 关闭档案GetObjectSchema 读取对象版本号SetObjectSchema 设置对象版本号m_pDocument 使用该档案的文档Read 读取字节内容Write 写入字节内容GetFile 获取底层的CFile对象operator<< 将基本类型写入流中IsLoading 是否处于读取状态operator>> 从流中读取基本类型IsStoring 是否处于保存状态Flush 将缓冲中的数据强制写入流中Abort 在不发送异常的情况下关闭档案ReadObject 读取串行化对象WriteObject 写入串行化对象CArchive类的常用成员及方法在进行串行化处理时,通常是通过CArchive(档案)类来完成的7.2单文档应用实例【例7-1】创建一个应用程序,其界面的标题为7_1。在应用程序的主窗口中显示一文本“您好,单文档界面的例程!”,并始终出现在窗口的中央,如图所示

具体步骤如下:创建名为7_1的单文档的工程文件;(2)由于需要在文档中显示字符串,因此,要添加一个字符型的变量,来存放这个字符串,因此,为CMy71Doc类添加一个CString类型的成员变量m_str;(3)文档变量初始化。针对类CMy71Doc的构造函数,将m_str初始化内容置为"您好,单文档界面的例程!",如下所示:CMy71Doc::CMy71Doc()noexcept{ m_str=_T("您好,单文档界面的例程!");}24(4)视图的输出在7_1的视图类CMy71View类的OnDraw成员函数中添加代码:

voidCMy71View::OnDraw(CDC*pDC){CMy71Doc*pDoc=GetDocument();ASSERT_VALID(pDoc);if(!pDoc) return;//TODO:在此处为本机数据添加绘制代码

CRectrectClient; //创建一个Crect这个矩形类的对象GetClientRect(rectClient); //获取当前客户区的尺寸大小CSizesizeClient=rectClient.Size(); //将获取的客户区的大小保存在参数sizeClient中

CSizesizeTextExtent=pDC->GetTextExtent(pDoc->m_str);

//获取字符串的宽度和高度

pDC->TextOut((sizeClient.cx-sizeTextExtent.cx)/2,(sizeClient.cy-sizeTextExtent.cy)/2,pDoc->m_str); //输出字符串}7.3菜单及菜单项的消息响应机制25

在基于文档的编程中,应用程序可以使用几种不同类型的资源,如加速键、位图、光标、对话框、菜单、工具条和字符串等,这些都需要编写消息响应的代码。267.3.1菜单资源及其应用菜单是图形用户界面中的重要组成部分。菜单可以使用户直观方便地操作程序,为用户提供各种功能。菜单系统菜单程序主菜单快捷菜单提供系统对程序主窗口的管理功能,通常在程序中不需要改动这种菜单。位于应用程序的最顶端,如File、Edit、View等菜单就属于程序主菜单,其菜单项包含了程序的大部分功能在Windows应用程序中很常见。通常通过单击鼠标右键而触发27【例7-2】创建一个基于单文档结构的应用程序,在视图中显示一行字符串“HelloWorld!”,通过建立包含“显示”和“颜色选择”两个菜单项的“操作”菜单来控制字符串,菜单项“显示”用以控制字符串的显示与否,菜单项“颜色选择”中包含一个二级子菜单,内容为“红”、“绿”和“蓝”三个菜单项。(1)首先创建基于单文档的7_2工程文件(2)根据如下操作:资源视图

Menu

IDR_MAINFRAME,可以看到菜单资源编辑器如图所示。可通过可视化编辑来创建菜单资源。

28ID_OPER_REDID_OPER_GREENID_OPER_BLUEID_OPER_SHOWHelloWorld!本例操作到这里,系统生成的宏如下:#defineID_OPER_SHOW 32776#defineID_OPER_RED 32777#defineID_OPER_GREEN 32778#defineID_OPER_BLUE 3277929在CMy72View类中加入如下public成员

DWORD m_nColorIndex; //当前所选颜色索引

CString m_strShow; //显示的内容

BOOLm_bShow;//是否显示COLORREFm_nColors[3];//用户可选颜色数组最后一项需要手动添加30由于要预先定义三种不同的颜色,这些定义应在视类的构造函数中完成,因此,在构造函数中加入如下代码:

CMy72View::CMy72View()noexcept{ //TODO:在此处添加构造代码 m_nColors[0]=RGB(255,0,0); //定义红色 m_nColors[1]=RGB(0,255,0); //定义绿色 m_nColors[2]=RGB(0,0,255); //定义蓝色 m_nColorIndex=0; //颜色数组下标初值 m_strShow=L"HelloWorld!"; //显示的字符串内容 m_bShow=TRUE; //显示状态}在voidCMy72View::OnDraw(CDC*pDC)中加入如下代码绘制字符串:if(m_bShow){pDC->SetTextColor(m_nColors[m_nColorIndex]); //设置输出字符串颜色pDC->TextOut(100,100,m_strShow);//输出字符串}

若编译运行程序,可看到程序输出一行红色的字符串,但颜色设置菜单项还没有起作用327.3.2菜单项的消息响应构架下面将介绍如何通过菜单项来控制程序,在介绍菜单项的响应时,必须先了解几个消息响应机制:COMMAND消息的响应UPDATE_COMMAND_UI消息的响应ON_COMMAND_RANGE对COMMAND消息的响应ON_UPDATE_COMMAND_UI_RANGE对UPDATE_COMMAND_UI消息的响应33(1)COMMAND消息的响应

首先添加“显示”菜单项对鼠标单击的消息相应。具体步骤:是在“类视图”中找到类CMy72View,单击鼠标右键,在弹出的菜单中选择“类向导”在“类向导”对话框中的“命令”选项卡中找到“显示”菜单项的ID(本例为ID_OPER_SHOW),“消息”类型选择COMMAND单击“添加处理程序”按钮选择缺省函数名称OnOperShow(),添加了对COMMAND消息的响应之后,代码7_2View.h中将在上述添加的四个变量的后面增加如下的代码:afx_msgvoid

OnOperShow();34在7_2View.cpp文件中,读者会看到ID_OPER_SHOW对应的COMMAND消息的绑定,代码如下:

ON_COMMAND(ID_OPER_SHOW,&CMy72View::OnOperShow) ……END_MESSAGE_MAP()

在7_2View.cpp文件的最后加入如下代码:voidCMy72View::OnOperShow(){m_bShow=!m_bShow;Invalidate(); //强制程序重新窗口}重新编译运行程序,可看到“显示”菜单项工作正常35(2)UPDATE_COMMAND_UI消息的响应UPDATE_COMMAND_UI消息是在窗口将要绘制菜单项的时候产生,上例中,仅仅只是使用“显示”菜单项来控制是否显示似乎还不够,如果“显示”菜单项能够配合主程序体现出当前是否显示的状态可能会更好一些。就像一个文本编辑软件,菜单上是“10号字”、“12号字”的功能,如果不在菜单上标识出来,那么使用者可能就搞不清当前的字是多大的。

为ID_OPER_SHOW添加UPDATE_COMMAND_UI消息。在自动生成消息处理函数中加入如下代码:voidCMy72View::OnUpdateOperShow(CCmdUI*pCmdUI){

pCmdUI->SetCheck(m_bShow);}

此时可看到随着m_bShow的值的改变,显示菜单项的状态与实际是否显示字符串的状态一致了,通过菜单项前面的“√”标记来体现。36voidEnable(BOOLbOn=TRUE)禁止或者允许该菜单项voidSetCheck(intnCheck=1)设置菜单项/工具条按钮的check状态,显示标志为“√”voidSetRadio(BOOLbOn=TRUE)与SetCheck功能类似,显示标志为“·”voidSetText(LPCTSTRlpszText)设置菜单项的Caption属性CCmdUI类常用的方法37(3)ON_COMMAND_RANGE对COMMAND消息的响应ON_COMMAND_RANGE为处理具有连续ObjectID的菜单项提供了方便的途径。前面只响应了三种颜色操作,若有100种颜色可供选择,是否逐个定义其响应函数?显然工作量很大,我们可以使用ON_COMMAND_RANGE。这涉及到ID范围的上下界及当前的ID,若Resource.h中若干个ID不连续,要手工修改为连续的。部分常用的通用宏及其作用38宏/参数描述ON_COMMANDIDFunc处理WM_COMMAND消息控件IDvoidfunc(void)消息响应函数ON_COMMAND_RANGEIDFirstIDLastfunc处理一个ID范围内的WM_COMMAND消息范围内第一个控件ID范围内最后一个控件IDvoidfunc(WORDID)消息响应函数ON_UPDATE_COMMAND_UIIDfunc处理MFC请求,用于更新界面状态控件IDvoidfunc(CCmdUI*pCmdUI)消息响应函数ON_UPDATE_COMMAND_UI_RANGEIDFirstIDLastfunc同上,处理一个ID范围范围内第一个控件ID范围内最后一个控件IDvoidfunc(CCmdUI*pCmdUI)消息响应函数ON_NOTIFYCodeIDfunc处理来自新风格控件的WM_NOTIFY消息NOTIFY消息代码控件IDvoidfunc(NMHDR*pNotifyStruct,LRESULT*result)消息响应函数宏/参数描述ON_NOTIFY_RANGECodeIDFirstIDLastfunc同上,处理一个ID范围NOTIFY消息代码范围内第一个控件ID范围内最后一个控件IDvoidfunc(UINTid,NMHDR*pNotifyStruct,LRESULT*result)ON_CONTROLCodeIDfunc处理WM_COMMAND中的EN_和BN_消息NOTIFY消息代码控件IDvoidfunc(void)消息响应函数ON_CONTROL_RANGECodeIDFirstIDLastfunc同上,处理一个ID范围NOTIFY消息代码范围内第一个控件ID范围内最后一个控件IDvoidfunc(UINTID)消息响应函数ON_MESSAGEMsgfunc处理任意消息,包括用户自定义消息消息IDLRESULTfunc(WPARAMwParam,LPARAM,lParam)消息响应函数ON_REGISTERD_MESSAGEMsgfunc处理使用RegisterWindowMessage注册的消息消息IDLRESULTfunc(WPARAMwParam,LPARAMlParam)消息响应函数ON_COMMAND_RANGE为处理具有连续ObjectID的菜单项提供了方便的途径。由于“类向导”不支持ON_COMMAND_RANGE消息的自动映射,只能手工添加消息的处理。在7_2View.h中声明消息的处理函数public:afx_msgvoidOnOperShow();afx_msgvoidOnUpdateOperShow(CCmdUI*pCmdUI);

afx_msgvoidOnOperColorChange(UINT

nID);所处理的菜单项的ID41在7_2View.cpp的开头部分加入如下斜体标识的代码,完成消息映射BEGIN_MESSAGE_MAP(CMy72View,CView)ON_COMMAND_RANGE( ID_OPER_RED, //ID范围的最小值

ID_OPER_BLUE, //ID范围的最大值

&CMy72View::OnOperColorChange) //消息处理函数END_MESSAGE_MAP()

在7_2View.cpp的最后加入消息处理函数:voidCMy72View::OnOperColorChange(UINTnID){ m_nColorIndex=nID-ID_OPER_RED; Invalidate();

}

运行程序,可以通过菜单项来改变颜色了42(4)ON_UPDATE_COMMAND_UI_RANGEON_UPDATE_COMMAND_UI_RANGE与ON_UPDATE_COMMAND_UI的关系类似和ON_COMMAND_RANGE与ON_COMMAND的关系

下面仿照手工加入ON_COMMAND_RANGE过程加入ON_UPDATE_COMMAND_UI_RANGE宏。在7_2View.h中加入如下代码:afx_msgvoidOnUpdateOperColorChange(CCmdUI*pCmdUI);

三个颜色菜单项的消息响应处理好了,如果能够为表示每个颜色的菜单项加上check功能看起来会更人性化一些,比如在菜单项的前面加一个“点”在提示。同样我们也可以考虑基于“范围”的消息响应。我们可以考虑表7-7中的“ON_UPDATE_COMMAND_UI_RANGE”这个构架来处理。43在7_2View.cpp中加入如下代码:ON_UPDATE_COMMAND_UI_RANGE(ID_OPER_RED,ID_OPER_BLUE,

&CMy72View::OnUpdateOperColorChange)…voidCMy72View::OnUpdateOperColorChange(CCmdUI*pCmdUI){pCmdUI->SetRadio(m_nColorIndex== (pCmdUI->m_nID-ID_OPER_RED));}由于CCmdUI类的成员m_nID是调用OnUpdateOperColorChange时当前的菜单项ID,因此该函数没有nID这个参数。447.4快捷菜单的创建及其应用

在前面的基础上增加快捷菜单,实现“操作”菜单的功能

1.

创建菜单资源:在ResourceView菜单中右击Menu,选择InsertMenu,资源命名为IDR_POP_SHOWIDR_POP_SHOW要改成这个ID值,先取POP,然后单击右键,弹出属性,再点击左边的IDR_MENU1,就可以进行修改CMenu类提供了许多处理菜单和菜单项的方法,这些方法分别是构造方法、菜单操作方法、菜单项操作方法和虚拟方法。45

这里需要使用CMenu类,这个类比较特殊,继承自CObject类,而不像大部分Windows可视组件继承自CWnd类,因此在一些需要CWnd类的场合,无法使用CMenu来完成工作。方法说明Attach()把一个标准的Windows菜单句柄附加到CMenu对象上CreateMenu()创建一个空菜单并把它附加到CMenu对象上CreatePopupMenu()创建一个弹出式菜单并把它附加到CMenu对象上DeleteTempMap()删除由FromHandle()构造函数创建的任何临时CMenu对象DestroyMenu()去掉附加到CMenu对象上的菜单并释放该菜单占的内存Deatch()从CMenu对象上拆开Windows菜单句柄并返回该句柄FromHandle()当给定Windows菜单句柄时,返回CMenu对象指针GetSafeHmenu()返回由CMenu对象封装的菜单句柄成员LoadMenu()从可执行文件装入菜单资源并把它附到CMenu对象上LoadMenuIndirect()从内存中的菜单模板中装入菜单并把它附到CMenu对象上

菜单操作方法中的DeleteMenu()和TrackPopupMenu()是用来处理菜单的顶层操作。DeleteMenu()删除某个特定菜单中的菜单项,如果被删除的菜单项有相关的弹出式菜单,此弹出式菜单的句柄也要被删除并释放内存,TrackPopupMenu()在一个POINT结构所指定的位置显示一个快捷菜单。46方法说明AppendMenu()把一个新项加到给定的菜单的末端CheckMenuItem()在弹出式菜单中,把一个校验标记放到下一个菜单项或从一个菜单项中取消一个校验标记CheckMenuRadioItem()在此组中,把一个单选按钮放到菜单项旁边或从全部其它菜单项里取消一个已存在的单选按钮EnableMenuItem()激活(停止)一个菜单项GetMenuItemCount()获取菜单项个数GetMenuItemID()为设置在指定位置的菜单项获得菜单项标识符GetMenuState()获得指定菜单项的状态GetMenuString()获得指定菜单项的标记GetSubMenu()获得指向弹出式菜单的指针InsertMenu()在指定位置插入新的菜单项ModifyMenu()在指定位置修改已存在的菜单项RemoveMenu()从指定菜单中删除与弹出式菜单结合的菜单项47在7_2View.h中声明快捷菜单中对应的变量。CMenum_PopMenu; //Pop-up快捷菜单CMenu*m_pPop; //Pop-up快捷子菜单添加快捷菜单资源通过类向导为CMy72View类添加OnRButtonDown消息响应函数系统在7_2View.cpp中产生了如下函数的框架:voidCMy72View::OnRButtonDown(UINTnFlags,CPointpoint){ CView::OnRButtonDown(nFlags,point);}49

在类CMy72View的构造函数中,将菜单资源加载并绑定到CMenu的对象m_PopMenu上。如果不绑上,就无法操作。简单的说,就是没有得到课操作的菜单资源。CMy72View::CMy72View(){//TODO:在此处添加构造代码……

m_PopMenu.LoadMenu(IDR_POP_SHOW);//创建并加载菜单资源}

在CMy72View的析构函数中,释放m_PopMenu占用的菜单资源,CMy72View::~CMy72View(){

m_PopMenu.DestroyMenu();//释放菜单资源}50下面我们编写前面添加的OnRButtonDown()的代码,具体如下:voidCMy72View::OnRButtonDown(UINTnFlags,CPointpoint){//TODO:在此添加消息处理程序代码和/或调用默认值m_pPop=m_PopMenu.GetSubMenu(0);//获得第一个子菜单UINTnCheck=m_bShow?MF_CHECKED:MF_UNCHECKED; //更新“显示”的check状态m_pPop->CheckMenuItem(ID_OPER_SHOW,MF_BYCOMMAND|nCheck);m_pPop->CheckMenuRadioItem(ID_OPER_RED,ID_OPER_BLUE,ID_OPER_RED+m_nColorIndex,MF_BYCOMMAND);//在颜色菜单项上加上选中标识

ClientToScreen(&point); //将坐标由客户坐标转化为屏幕坐标m_pPop->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this); //显示Pop-up菜单CView::OnRButtonDown(nFlags,point);}51如果运行这个程序,可能有的读者会发现,界面上不仅出现了我们所创建的快捷菜单,还出现了一个多余的快捷菜单,如图所示的圈起来的部分,也就是同时出现了两个快捷菜单。如何处理呢?实际上系统定义了一个缺省的快捷菜单,可以去掉这个多余的快捷菜单,只要找到OnContextMenu函数,将如下的斜体加粗的一行代码注释掉就行了。voidCMy72View::OnContextMenu(CWnd*/*pWnd*/,CPointpoint){#ifndefSHARED_HANDLERS//theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT,point.x,point.y,this,TRUE);#endif}527.5工具条资源的创建及其使用

在Windows应用程序中,工具条可以看作是图形化的菜单,是一种更快捷、更有效、更直观的人机交互方式。7.5.1工具条类的层次位置及其常用方法工具条由CWnd类派生的,它们都连接到一个Windows应用程序窗口。因此,CWnd的所有功能如创建、移动、显示和隐藏窗口等在用控制条工作时都是可用的。53Create():创建一个工具条并把它附加到CToolBar对象上CreateEx():创建一个定义了边界的工具条并附加到CToolBar对象上SetSizes():设置按钮及位图大小SetHeight():设置工具条的高度LoadToolBar():装载工具条资源LoadBitmap():装载包含工具按钮图像的位图SetBitmap():设置位图图像SetButtons():设置按钮并使每个按钮与位图图像相关CToolBar的构造方法7.5.2添加自己的工具条一般需要以下几个步骤:(1)增加工具条资源:单击资源视图选择Toolbar,从快捷菜单中选择“插入Toolbar”。这时在资源编辑器中可以看到一个新的工具条资源。(2)将工具条添加到窗口中:添加了资源后,需要应用程序框架窗口(CMainFrame)加入工具条的对象。首先需要在应用程序的CMainFrame类中加入工具条对象m_wndToolBar。protected:CMFCToolBarm_wndToolBar;//自己定义的工具条(3)在框架窗口类的OnCreate()函数中调用工具条类的Create()或CreateEx()成员函数创建该工具条,并调用LoadToolBar()成员函数将工具条对象和前面创建的工具条资源连接在一起。5455if(!m_wndToolBar.Create(this,WS_VISIBLE|CBRS_TOP)|| !m_wndToolBar.LoadToolBar(IDR_TOOLBAR)) //引入资源IDR_TOOLBAR{ TRACE0("Failedtocreatetoolbar\n"); return-1;//failtocreate}标志简单描述CBRS_TOP将工具条放在窗口顶部CBRS_BOTTOM将工具条放到窗口底部CBRS_ALIGN_ANY工具条放在窗口的任意位置CBRS_NOALIGN防止控制条在其父窗口改变大小时被复位CBRS_FLOAT_FLOAT工具条在主窗口中可以浮动CBRS_TOOLTIPS鼠标光标在按钮上暂停时,显示工具提示CBRS_FLYBY鼠标光标在按钮上暂停时,显示命令描述CBRS_SIZE_DYNAMIC工具条的大小可变CBRS_SIZE_FIXED工具条的大小不可变CBRS_HIDE_INPLACE隐藏工具条调用Create()函数时可以设定工具条的风格563.对工具条进行操作

若在上述代码中使用了CBRS_TOOLTIPS工具条风格,那么当鼠标光标在工具栏按钮上暂停时,会显示预先设置好的工具命令描述提示。设置方法如图所示。

当鼠标光标在按钮上暂停时,显示工具提示和命令描述,并设定工具条的大小是可变的。

m_wndToolBar.SetBarStyle(CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC);风格意义CBRS_ALIGN_TOP工具条可在客户区顶端停靠CBRS_ALIGN_BOTTOM工具条可在客户区底端停靠CBRS_ALIGN_LEFT工具条可在客户区左端停靠CBRS_ALIGN_RIGHT工具条可在客户区右端停靠CBRS_ALIGN_ANY工具条可在客户区任意位置停靠工具条的其他风格:下面这段代码是实现工具条移动的常用代码: m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY);58【例】在前面例题基础上,工具条中增加四个按钮,分别对应菜单的“显示”、“红色”、“绿色”和“蓝色”菜单项。该工具条可以在窗口中任意位置停靠,当鼠标停留在工具条按钮上时,将显示该按钮的功能。如何设置工具按钮的ID?59

在ResourceView中加入工具条资源IDR_TOOLBAR_NEW绘制四个按钮并设置相应ID(修改成跟前面的ID一致)。 在MainFrm.h中添加如下代码,声明一个CMFCToolBar变量//特性public:

CMFCToolBarm_wndToolBarNew;

在MainFrm.cpp文件的

IntCMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)

函数中添加如下代码:if(!m_wndToolBarNew.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP| CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC)|| !m_wndToolBarNew.LoadToolBar(IDR_TOOLBAR_NEW)) { TRACE0("Failedtocreatetoolbar\n"); return-1; //failtocreate}

在框架窗口类的OnCreate()中调用工具条类的Create()或CreateEx()建该工具条并用LoadToolBar()将工具条对象和前面创建的工具条资源连接在一起。60

为了使新增的工具条可以在窗口中自由停靠,在OnCreate函数中,还要增加如下代码:m_wndToolBarNew.EnableDocking(CBRS_ALIGN_ANY); //工具条可以在父窗口内任何一边停靠EnableDocking(CBRS_ALIGN_ANY); //父窗口允许子工具条窗口在任何一边停靠DockPane(&m_wndToolBarNew); //父窗口内按照前面指定的风格停靠该工具条617.6字符串资源的使用

字符串资源最主要的用途就是用于程序的多语言版本。如果要想动态切换界面语言,使用字符串资源是很好的选择。 在MFC中,可以通过CString类的LoadString方法来从资源载入字符串。 具体操作是打开ResourceView

StringTable,在表中的空白高亮处双击,在弹出的StringProperties对话框中的ID编辑框中输入IDS_STRING_HELLO,Caption编辑框中输入“HelloVC++!”。62

在My7_2View.cpp文件的构造函数中,将原来的

m_strShow="HelloWorld!";

改为:m_strShow.LoadString(IDS_STRING_HELLO);

这样我们的程序的输出就变为“HelloVC++!”了使用字符串资源的好处就是不需要在整个程序中去寻找某个字符串,如果某些字符串可能在将来会发生变更,那么最好将它写在字符串资源中。637.7对话框资源的创建及其应用

在上例的基础上编写一个对话框用于接收用户输入,然后用这个输入来替换主程序原来显示的字符串IDD_DIALOG_NEWIDC_EDIT_INPUTCStringm_strInput64在创建完对话框资源之后,需要生成一个相关的对话框类。在新建的对话框中单击鼠标右键,选择“添加类”,在“类名”中填入“CInputDlg”即可。可以使用DDX技术来接受用户的输入,DDX全称是对话框数据交换(DialogDataeXchange),用来在控件变量和数据变量之间交换数据,简单来说,就是MFC通过DDX技术在控件和一个数据类型之间建立一种绑定关系,如果你改变了控件中的内容,那么数据变量随之改变,如果你改变了数据变量的内容,控件中的内容也会随之改变。65将IDC_EDIT_INPUT控件与一个CString类型的变量绑定,建立一种映射关系。添加一个CString类型的对象m_strInput,这样就为IDC_EDIT_INPUT添加了一个DDX变量。67下面来分析一下上面的操作使MFC在幕后作了些什么。

在InputDlg.h文件中,MFC加入了如下斜体字的代码:classCInputDlg:publicCDialogEx{ DECLARE_DYNAMIC(CInputDlg)

public: CInputDlg(CWnd*pParent=NULL);//标准构造函数 virtual~CInputDlg();

//对话框数据 enum{IDD=IDD_DIALOG_NEW};

protected: virtualvoidDoDataExchange(CDataExchange*pDX);//DDX/DDV支持

DECLARE_MESSAGE_MAP()public: CStringm_strInput;};68

可以看到,MFC在CInputDlg中加入了m_strInput这个变量。另外IDD=IDD_DIALOG_NEW一句将该类与对话框资源绑定了。

在InputDlg.cpp文件的构造函数中,MFC加入了如下斜体字的代码:CInputDlg::CInputDlg(CWnd*pParent/*=NULL*/):CDialogEx(CInputDlg::IDD,pParent),m_strInput(_T(“”))//进行了对m_strInput的初始化。{}

在InputDlg.cpp文件的DoDataExchange函数中,MFC加入了如下斜体字的代码:voidCInputDlg::DoDataExchange(CDataExchange*pDX){ CDialogEx::DoDataExchange(pDX);

DDX_Text(pDX,IDC_EDIT_INPUT,m_strInput);}

在函数DDX_Text调用中,完成了控件与变量之间的数据交换。现在为止,CInputDlg已经是一个完整的类了,可以在别的类中调用该类了。6970下面要在CMy72View中使用新创建的对话框首先为“操作”菜单增加菜单项“修改字符串”,其ID为ID_OPER_STRING;为CMy72View中增加COMMAND消息响应函数OnOperString;然后在My7_2View.cpp文件include部分最后加入:#include"CInputDlg.h"

在OnOperString中加入如下代码:voidCMy72View::OnOperString(){//TODO:在此添加命令处理程序代码

CInputDlgdlgInput; //声明对话框变量if(dlgInput.DoModal()==IDOK)//若点击OK按钮{ m_strShow=dlgInput.m_strInput; //更改字符串 Invalidate(); //强制重绘}}717.8位图资源的创建及其应用

标准控件比较单调,若能通过一些精美的图片来点缀,就活泼了,这个问题,可以选择位图资源来实现。

位图是一种数字化的图形表示形式,基本数据结构是像素,一个像素表示一个离散点的颜色值。常见位图有2色、4色、16色、256色、16位、24位。保存在文件中的位图可以看作是设备无关的,文件本身的数据用来描述位图的内容。

72在上例的基础上显示两幅图片,一幅是256色(IDB_BITMAP_256),另一幅是24位真彩(IDB_BITMAP_24bit),两幅图片都是通过资源来显示添加bmp资源并导入两张图73在CMy72View.cpp的OnDraw函数中加入如下代码:CDCdcMemory; //定义内存缓冲DCdcMemory.CreateCompatibleDC(pDC); //创建内存DCCBitmapbmp1; //定义一个位图对象bmp1 bmp1.LoadBitmap(IDB_BITMAP_256); //加载位图BITMAPbmpInfo1; //定义位图结构变量,包含位图的尺寸变量bmp1.GetBitmap(&bmpInfo1);//获得位图的尺寸并填充参数bmpInfo1dcMemory.SelectObject(&bmp1);//选择位图到内存缓冲设备中pDC->BitBlt(100,150,bmpInfo1.bmWidth,bmpInfo1.bmHeight,&dcMemory,0,0,SRCCOPY);//绘制到屏幕CBitmapbmp2; //定义另一个位图对象bmp2 bmp2.LoadBitmap(IDB_BITMAP_24bit); //加载位图BITMAPbmpInfo2; //定义另一个位图结构变量bmp2.GetBitmap(&bmpInfo2);//获得位图尺寸并填充参数bmpInfo2dcMemory.SelectObject(&bmp2); //选择位图到内存缓冲设备中pDC->BitBlt(100,400,bmpInfo2.bmWidth,bmpInfo2.bmHeight,&dcMemory,0,0,SRCCOPY);//绘制到屏幕7.9MDI编程实例74

多文档应用程序(MDI)和单文档应用程序的主要不同在于:它支持多个文档、甚至多个文档类型。

由于MDI程序可以打开多个文档的特性,所以比起SDI应用程序来要处理很多琐碎的事情,例如切换视图、更新菜单等。但是由于MFC在MDI应用程序中自动加入了很多程序代码来处理这些事情,所以利用应用程序向导生成MDI应用程序框架后,剩下的编程就和SDI程序非常类似了。75【例7-3】创建一个多文档的应用程序,程序运行后,可以打开两种类型的文档。其中,MyMDI是主窗口的标题,MyMdi1是系统默认生成的文档,在此窗口中可以输入文字。MyMdi2是另一个用户添加的文档类型,在此文档中,用户通过选择“画图种类”,然后在视图窗口中,拖动鼠标就可以画出一个图形。由于两种文档的操作对象和操作方式不同,程序运行时的界面与菜单就不同。761创建MDI工程文件,名字为7_3在MFC应用程序向导中,为CMy73View类设置基类为CEditView(原来缺省的为CView类)在应用程序向导的“文档模板属性”页的设置中,在“文件扩展名”一项中输入“TSU”,完成后的应用程序的文件将使用“.TSU”为扩展名,过滤器域显示为“7_3Files(*.TSU)”772创建第二种文档和视图类为项目7_3添加“类”,鼠标右击7_3工程,在弹出的菜单中选择“添加”->“新建项”选择新建项后弹出如图所示的界面值的注意的是,在添加类的操作过程中,如果不是选择“新建项”,而是“类”,那么所创建的不是基于MFC的类,而是C++的类,其界面看似也能正确操作,但情况是不一样的。这里操作容易混淆78在“类名”处输入CMy7_3Doc2,在“基类”处选择CDocument类,说明所创建的是基于MFC的文档类。单击“确定”按钮,则应用程序中增加了CDocument的派生类CMy7_3Doc2。用同种方法在应用程序中增加CView的派生类CMy7_3View2793创建资源在Resource.h文件中,手工加入下列代码:#defineIDR_My73TYPE2 132也可如图操作,在弹出的“资源符号”对话框中单击“新建”按钮,然后输入相应内容即可。80这样就定义了第二类文档的文档、视图和框架窗口共同的资源ID,以后定义的菜单、文档模板等资源均可以使用这个ID。打开“资源视图”中的StringTable,会看到IDR_M73TYPE的资源模板内容如下:

IDR_My73TYPE130\nMy73\n7_3\n\n\nMy73.Document\n7_3.Document文档模板的资源文档模板字符串的格式是:nIDResource \n<WindowTitle>\n<DocName>\n<FileNewName.FilterName>\n<FilterExt>\n<RegFileTypeID>\n<RegFileTypeName>81对于第一个文档,应用程序向导直接产生了一个文档模板,现在还必须按照上面这种格式,手工加入第二个资源模板字符串。具体的方法是在StringTable中加入如下内容:IDR_My73TYPE2132\nMy73_2\n73_2\n7_3_2Files(*.TSU)\n.TSU\nMy73_2.Document\n7_3_2.Document3菜单、对话框资源在资源视图选项卡,将菜单资源IDR_My73TYPE复制一份,ID为IDR_My73TYPE2。在新创建的菜单如右图所示IDCAPTION直线ID_LINE椭圆ID_ELLIPSE矩形ID_RECTANGLE824代码编辑(1)创建文档模板类因本程序支持多文档,应在InitInstance()函数中定义新的文档模板的对象,打开7_3.cpp文件,输入如下斜体加粗的代码:BOOLCMy73App::InitInstance(){……//主窗口已初始化,因此显示它并对其进行更新pMainFrame->ShowWindow(m_nCmdShow);pMainFrame->UpdateWindow();CMultiDocTemplate*pDocTemplate2;pDocTemplate2=newCMultiDocTemplate(IDR_My73TYPE2,RUNTIME_CLASS(CMy7_3Doc2),RUNTIME_CLASS(CChildFrame),//自定义MDI子框架RUNTIME_CLASS(CMy7_3View2));//然后使用CWinApp::AddDocTemplate()方法将新模板添加到应用程序的文档模板列表中AddDocTemplate(pDocTemplate2);returnTRUE;}83为使CMy7_3Doc2类和CMy7_3View2类在CMy73App类中成为可识别的类,必须在7_3.cpp文件中加入CMy7_3Doc2类和CMy7_3View2类的说明头文件CMy7_3Doc2.h和CMy7_3View2.h#include"CMy7_3Doc2.h" //加入头文件#include"CMy7_3View2.h"84(2)扩展CMy7_3Doc2类添加成员变量在类CMy7_3Doc2中增加CPtrArray类型的成员变量m_data,CPtrArray是一个集合类,是一个支持指针数组的类,而指针数组存放的是指针,指针可以指向不同的对象。本程序定义的m_data用于保存多图形对象信息。

然后在应用程序的“类视图”中添加一个C++的类CDrawData

CDrawData用于保存每一图形的信息,在CDrawData的框架中添加如下内容,用以保存绘图的起始点和终点坐标:classCDrawData{public:POINTbegin,end;//图形的起点和终点坐标inttype; //绘制图形的样式};85为了能在CMy7_3Doc2中使用该类,还需在CMy7_3Doc2.h中加入头文件CDrawData.h#include"CDrawData.h"

在类CMy7_3Doc2中添加一个用于保存当前图形的类型的整型变量m_drawType,并在构造函数中缺省初始化为0;在CMy7_3View2.cpp中加入:#include"CMy7_3Doc2.h"#include"CDrawData.h"添加菜单处理函数在类CMy7_3Doc2中,为了加入消息响应成员函数voidOnChangeDrawType(UINTnID);那么在文件CMy7_3Doc2.h中添加如下代码:voidOnChangeDrawType(UINTnID);在CMy7_3Doc2.cpp的消息映射部分添加选择图形类型的消息映射,代码如下:BEGIN_MESSAGE_MAP(CMy7_3Doc2,CDocument)ON_COMMAND_RANGE(ID_LINE,ID_RECTANGLE,CMy7_3Doc2::OnChangeDrawType)END_MESSAGE_MAP()

这个是基于ID范围的消息响应,我们在前一个例子的颜色处理响应中曾经用到,不过这个是需要手动添加的。

OnChangeDrawType的代码如下:voidCMy7_3Doc2::OnChangeDrawType(UINTnID){m_drawType=nID-ID_LINE;}87文档串行化

为了把对视图中显示文本的修改保存到磁盘文件中,并在需要时可以打开所保存的磁盘文件读取文档,必须重载CMy7_3Doc2类的Serialize函数来完成串行化,在CMy7_3Doc2.cpp中增加如下代码。重载后的Serialize()函数的代码如下:voidCMy7_3Doc2::Serialize(CArchive&ar){if(ar.IsStoring()){intsize=m_data.GetCount();//获取此数组中的元素个数ar<<size;//将size值写到ar中inti;for(i=0;i<size;i++)//这里是绘制矩形{CDrawData*data=(CDrawData*)m_data.GetAt(i);//返回给定索引位置处的值ar<<data->begin.x;//把绘制图形的起止点坐标写入arar<<data->begin.y;ar<<data->end.x;ar<<data->end.y;ar<<data->type;//把绘制图形的样式也记录到ar中}}88else{intsize;ar>>size;inti;m_data.RemoveAll();//从此数组中移除所有元素for(i=0;i<size;i++){CDrawData*data=newCDrawData;//定义一个DrawData的指针,并分配内

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论