vc编程技术600个大型项目源码二_第1页
vc编程技术600个大型项目源码二_第2页
vc编程技术600个大型项目源码二_第3页
vc编程技术600个大型项目源码二_第4页
vc编程技术600个大型项目源码二_第5页
已阅读5页,还剩125页未读 继续免费阅读

下载本文档

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

文档简介

1、用 MFC 如何高效地绘图(加入日期:2002-1-6)【收藏文章】【对此文评论】【保存文章至硬盘】【打印文章】【字体:大 中 小】显示图形如何避免闪烁,如何提高显示效率是问得比较多的而且多数人认为 MFC 的绘图函数效率很低,总是想寻求其它的解决方案。MFC 的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,。只要使用得当,再加上一些技巧,用 MFC 可以得到效率很高的绘图程序。就我长期(呵呵当然也只有 2 年多)使用 MFC 绘图的经验谈谈一些观点。1、显示的图形为什么会闪烁?我们的绘图过程大多放在 OnDraw 或者 OnPaint 函数中,OnDraw 在进行屏OnPaint

2、进行调用的。当窗口由于任何幕显示需要重绘时,总是先用背景色将显示区清除,然后才调用 OnPaint,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。如果将背景刷设置成 NULL,这样无论怎样重绘图形都闪了。当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的, 其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。例如在 OnDraw(CDC *pDC)中这样写: pDC->MoveTo(0,0);pDC->Lin

3、eTo(100,100);这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。比如:清楚屏幕时间为 1s 绘图时间也是为 1s,这样在 10s 内的连续重画中就要闪烁 5 次;如果清楚屏幕时间为 1s 不变,而绘图时间为 9s,这样 10s 内的连续重画只会闪烁一次。这个也可以试验,在 OnDraw(CDC *pDC)中这样写:for(int i=0;i<100000;i+)pDC->MoveTo(0,i); pDC->Line

4、To(1000,i);呵呵,程序有点,但是能说明。说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉害一些,但是闪烁频率要低。那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间 的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。2、如何避免闪烁在知道图形显示闪烁的之后,对症下药就好办了。首先当然是去掉 MFC提供的背景绘制过程了。实现的* 可以在窗口形成时给窗口的* 也可以在形成以

5、后修改背景很多,类的背景刷付 NULLstatic CBrush brush(RGB(255,0,0);SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);* 要简单也可以重载 OnEraseBkgnd(CDC* pDC)直接返回 TRUE这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。怎么办?这就要用到双缓存的了。双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中绘制好,然后再的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个

6、过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差大的背景色进行清除都闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就闪。3、如何实现双缓冲首先给出实现的程序,然后再解释,同样是在 OnDraw(CDC *pDC)中:CDC MemDC; /首先定义一个显示对象CBitmap MemBitmap;/定义一个位图对象/随后建立与屏幕显示兼容的内存显示MemDC.CreateCompatibleDC(NULL);/这时还不能绘图,因为没有地方画 _/下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可

7、以用窗口的大小MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);/将位图选入到内存显示中/只有选入了位图的内存显示才有地方绘图,画到指定的位图上CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);/先用背景色将位图清除干净,这里我用的是白色作为背景/你也可以用应该用的颜色MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255);/绘图MemDC.MoveTo(); MemDC.LineTo();/将内存中的图拷贝到屏幕上进行显示pDC

8、->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);/绘图完成后的MemBitmap.DeleteObject(); MemDC.DeleteDC();上面的注释应该很详尽了,废话就不多说了。4、如何提高绘图的效率我主要做的是电力系统的图形的 CAD,在一个窗口中往往要显示成千上万个电。如果真要在一次重绘过程重画这么力多,而每个又是由点、线、圆等基本图形,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将绘图过程了。受。怎么办?只有再研究研究 MFC 的实际上,在 OnDra

9、w(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在 OnDraw 中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为 MFC 本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是 pDC 中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多, 有了裁剪区后显示的就只是应该显示的部分,大大提

10、高了显示效率。但是这个裁剪区是 MFC 设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用 pDC->GetClipBox()得到裁剪区,然后在绘图时你的图形是否在这个区内,如果在就画,不在就不画。如果你的绘图过程不复杂,这样做可能对你的绘图效率有提高让动态创建的 ActiveX 控件响应 Windows 消息(加入日期:2001-10-20)【收藏文章】【对此文评论】【保存文章至硬盘】【打印文章】【字体:大 中 小】当我们通过 CWnd:CreateControl()动态创建 ActiveX 控件时,Windows 消

11、息并被给我们的由 CWnd 派生得控件类。例如,即使我们为 WM_KILLFOCUS 消息创建了句柄,这个句柄也是被调用的。(不信你试试 :)引起这个的时,CWnd:CreateControl()并使我控件的 HWND 子类化。知道了,要解决就不难了。我们只要在控件创建之后使它子类化就行了。通过一下步骤可以实现:1.当然是添加控件啦,先生成控件的 C+包容类。2.再要创建控件的类中成员变量,此成员变量是包容类的指象。3.在包容类的件中添加:/AFX_MSG(CMyClass)/AFX_MSG DECLARE_MESSAGE_MAP()CMyClass 是实际的类名。4.在包容类的 CPP 文件

12、的#include 段下面添加:BEGIN_MESSAGE_MAP(CMyClass, CWnd)/AFX_MSG_MAP(CMyClass)/AFX_MSG_MAP END_MESSAGE_MAP()5.删除工程目录下的.clw 文件,在工程中打开 ClassWizard,先选 yes,在选 AddAll。6.现在你就可以使用 ClassWizard 为控件包容类添加消息和处理函数了。完成这些修改后,你必须创建控件并且使它子类化,下面是个例子:void CSDIApp2View:OnInitialUpdate()CView:OnInitialUpdate();m_pFlexGrid = ne

13、w CMSFlexGrid;CRect rect; GetClientRect(&rect);m_pFlexGrid->Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, IDC_FLEXGRID); HWND hWnd = m_pFlexGrid->Detach();m_pFlexGrid->SubclassWindow(hWnd);利用键盘钩子开发按键发音程序(加入日期:2002-1-5)【收藏文章】【对此文评论】【保存文章至硬盘】【打印文章】【字体:大 中 小】一、前言一日,看见我妈正在用电脑练习打字,频频低头看键盘,

14、:要是键盘能发音的话,不就可以方便她养成"盲打"的好习惯吗?光想不做可不行,开始行动(您可千万别急着去拿工具箱啊_).,按键能发音,其关键就是让程序能够知道当前键盘上是哪个键被按下,并相应的程序当然不在话下,那么其它程序当前按下哪个键如何得知呢?利用键盘钩子便可以很好地解决。本文的全部源代码 大小:552K二、挂钩(HOOK)的基本原理WINDOWS 调用挂接的回调函数时首先会调用位于函数链首的函数,我们只要将的回调函数置于链首,该回调函数就会首先被调用。那么如何将我们的回调函数置于函数链的链首呢?函数 SetWindowsHookEx()实现的就是该功能。我们首先来看一下

15、 SetWindowsHookEx 函数的原型:HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, HINSTANCE hMod,DWORD dwThreadId);第一个参数:指定钩子的类型,有 WH_MOUSE、WH_KEYBOARD 等十多种(具体参见 MS DN)第二个参数:标识钩子函数的地址第三个参数:钩子函数所在模块的句柄;第四个参数:钩子相关函数的 ID 用以指定想让钩子去钩哪个线程,为 0 时则消息。整个系统的,挂钩函数应该放在动态库 DLL 中。另外需要注意的是为了捕获所有三、具体实现理论的话就不多说了,运行 VC+6.0,新建

16、一个 MFC AppWizard(dll)工程,命名为 Hook, 使用默认的创建 DLL 类型的选项,也就是使用共享 MFC DLL,点击完成后开始编写代码:(1)在 Hook.h 中定义全局函数BOOL installhook(); /钩子安装函数LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lPara m);/挂钩函数(2)在 Hook.cpp 文件的#endif 下添加定义全局变量 Hook 的代码: static HHOOK hkb=NULL;HINSTANCE hins; /钩子函数所在模块的句柄(3)

17、添加代码BOOL installhook()hkb=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hins,0); return TRUE;第一个参数指定钩子的类型,因为我们只用到键盘操作所以设定为 WH_KEYBOARD;第二个参数将钩子函数的地址指定为 KeyboardProc,当钩子钩到任何消息后便调用这个函数,即当不的哪个窗口有键盘输入马上会引起 KeyboardProc 的动作;第三个参数是钩子函数所在模块的句柄;最后一个参数是钩子相关函数的 ID 用以指定想让钩子去钩哪个线程,为 0 时则整个系统的消息;现在,就开始定义当键

18、盘上的键按下时程序要做什么了KeyboardProc 动作:LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)if(DWORD)lParam&0x40000000) && (HC_ACTION=nCode)switch(wParam) /键盘按键标识case case case case.case case case case.'1':sndPlaySound("1.wav",SND_ASYNC);break; /当数字键 1 被按下'2&#

19、39;:sndPlaySound("2.wav",SND_ASYNC);break; '3':sndPlaySound("3.wav",SND_ASYNC);break;'4':sndPlaySound("4.wav",SND_ASYNC);break;'A':sndPlaySound("a.wav",SND_ASYNC);break; /当字母键 A 被按下'B':sndPlaySound("b.wav",SND_ASYNC);b

20、reak; 'C':sndPlaySound("c.wav",SND_ASYNC);break;'D':sndPlaySound("d.wav",SND_ASYNC);break;LRESULT RetVal = CallNextHookEx( hkb, nCode, wParam, lParam ); return RetVal;上面的代码中我们用声音做为按键被按下后的动作,API 函数 sndPlaySound 的第一个参数定义参数定义前声音的文件的绝对路径(比如要C 盘下的 a.wav,就定义成"C:a.w

21、av");第二模式,SND_ASYNC 模式可以及时地正在文件,立刻停止当有阻塞感了.,这样在我们连续击键时就转去新(4)添加输出标识在 Hook.def 的末尾添加installhook KeyboardProc短短的四步,键盘钩子的制作算是完成了,编译生成后的 DLL 文件就可以序来调用了.的用别的程如何调用 DLL 呢?那就简单了.再用 VC+6.0 新建一个 MFC AppWizard(exe)工在程,命名为 KeySound,点击"确定"后选择程序类型为框,直接点击确定即可.在 KeySoundDlg.cpp 文件中的 OnInitDialog()初始化

22、函数的 CDialog:OnInitDialog();下面添加:/程序反复驻留内存,也为了防止有两个程序同时DLL 而发生错误.CreateMutex(NULL, FALSE, "KeySound");if(GetLastError()=ERROR_ALREADY_EXISTS) OnOK();/DLLstatic HINSTANCE hinstDLL;typedef BOOL (CALLBACK *inshook)(); inshook instkbhook; if(hinstDLL=LoadLibrary(LPCTSTR)"Hook.dll")ins

23、tkbhook=(inshook)GetProcAddress(hinstDLL,"installhook"); instkbhook();elseMessageBox("当前目录找不到 Hook.dll 文件,程序初始化失败"); OnOK();将编译生成后的 KeySound.exe 和 Hook.dll 放在同一目录下,定义文件,运行 KeySound.exe 后打开记事本或写字板,体验一下系统为您即时快速地朗读您按下的每一个键的吧-有一点必须说明,标准键盘有 101 个键,您想让多少键音,就必须在上面的 KeyboardProc 动作里定义多少个

24、键,常用的 10 个数字键和 26 个英文字母只要相应的'A'对应 A 键,'1'对应 1 键就可以,但如果您希望能让给您带来太大的,的键都有各种特色音乐的话,很可能会遇到一些键盘编码上的麻烦,比如 ESC 键就不能简单的用'ESC'来搞定了,得用 VK_ESCAPE,又比如 Alt 键得用 VK_来定义,没有个键盘编码表的话会令人告诉您键盘按键名称的:相当头疼,这里我一种让为一个工程添加 PreTranslateMessage,添加如下代码:char KeyName50; ZeroMemory(KeyName,50);if(pMsg ->

25、 message = WM_KEYDOWN)GetKeyNameText(pMsg->lParam,KeyName,50); MessageBox(KeyName);那么当程序窗口显示在面前时按下某个键,就会弹出一个消息显示该键的名称,然后用''包起来就可以了,比如逗号,就是','和'.',简单吧:)到此就全部完成了按键发音程序的编写,通过改音文件的名称而不用改动程序本身就可以达到更换按键声音的目的了,只是有个遗憾,声音文件在硬盘中的位置不能变更,从 C盘换移动 D 盘程序就不能了,怎么样才能灵活的声音文件呢?可以用 API 函数 Ge如下

26、:tModuleFileName 来得到程序所在的目录,具体实现(1)在 Hook.h 的 public:下面添加:BOOL InitInstance(); /初始化函数(2)在 Hook.cpp 的#endif 下添加定义全局变量的代码:char szBuf256; char *p;CString msg;(3)在 Hook.cpp 中适当位置添加:BOOL CHookApp:InitInstance ()GetModuleFileName(AfxGetInstanceHandle( ),szBuf,sizeof(szBuf); p = szBuf;while(strchr(p,'&

27、#39;)p = strchr(p,''); p+;*p = '0' msg=szBuf; return TRUE;(4)新建一个文件夹并命名为 Sound;(5)改音文件物理位置定义方式case '1':sndPlaySound(msg+"sound1.wav",SND_ASYNC);break;msg 是得到程序当前所在目录,加上后面的代码就是指当前目录下的 Sound 目录里的1.wav 文件,这样就将声音文件的绝对路径改成了灵活的相对路径.您只要把 KeySound.ex e,Hook.dll 和 Sound 文件夹放

28、在同一个文件夹下,以后只要搬动整个文件夹就能实现声音文件的任意移动了。调试时需要注意:将 Hook.dll、Sound 目录放在 KeySound.exe 的执行目录下。假如编译链接的时候出现 unresolved external symbol imp sndPlaySoundA8 这样的信息,请在 Project Settings 中加入 Winmm.lib 。对象的持久性和文件 I/O(加入日期:2001-10-14)【收藏文章】【对此文评论】【保存文章至硬盘】【打印文章】【字体:大 中 小】内容提要理解对象的持久性* File Demo 应用程序* 创建一个持久类* 直接读写文件* 创

29、建的 CArchive 对象* 理解文章正文对象的持久性程序需要完成的一项最重要的事情是,当数据以某种方式改变之后,程序能够保存用户的数据。没有保存被编辑过的数据的功能,用户利用应用程序所做的工作只能在应用程序执行时,在用户离开应用就会消失。这不是一种好的工作方式!在很多情况下,尤,Visual C提供许多代码用于保存和加载数据。其在使用 AppWizard 来创建应用,在一些情况下特别是当你创建户文件的更新。对象类型时,必须做一部分额外的工作来保存用当读者正在编写一个应用,要处理大量的不同类型对象。你的数据对象有些可以像整数和字符一样简单。其他对象可以是诸如 CString 类的字符串或你的

30、定制类所创建的对象。当在需要保存和加载文档的应用使用对象时,需要用某种方式保存和加载这些对象的状态,这样才能重新创建它们,如同上一次任务结束时那样。一个对象所具有的保存和加载其状态的能力称为“持久性”。几乎所有的 MFC 类都是持久的,这是因为它们是直接或间接由 MFC 的 CObject 类派生的,该类提供了保存和加载对象状态的基本功能。File Demo 应用程序你在使用 Visual C的 AppWizard 来创建,将得到一个可以使用文档和视图类来组织、编辑和显示数据的应用程序。由 CDocument 类派生的文档对象,负责在会话期间保存应用程序的数据和在不同时期保存和加载数据,使得文

31、档从一个会话过程到另一个会话过得以保持。创建一个 File Demo 应用程序,演示保存和加载由 CDocument 派生的对象的数据的基本技术。File Demo 的文档是包含一个短消息的单一字符串。有三个菜单项与该应用程序相关。当程序初次运行时,消息自动设置字符串为 Default Message。用户可以选择 EditChange Message 来改变这条消息。FileSave 菜单项可以存储文档,FileOpen 菜单项则从磁盘加载文档。 文档类的回顾任何编写过程序的人都保存和打开过文件,从用户的观点看这就是对象的持久性。以学到持久性工作的原理。可当使用 Visual C的 AppW

32、izard 创建载其状态。这些步骤是:,必须完成以下几步使得文档能够保存和加1)定义保存文档数据的成员变量。2)在文档类的 OnNewDocument( )函数中初始化该成员变量。3)在视图类的成员函数 OnDraw( )中显示当前文档。4)在视图类中提供成员函数以使得用户能够编辑文档。5)在文档类的成员函数 Serialize( )中增加保存和加载该文档的数据所需要的代码。当应用程序处理多文档时,你需要做一点额外的工作来保证使用、修改或保存了正确的文档。所幸的是大部分工作都由 MFC 和 AppWizard 完成了。 创建 File Demo 应用程序要想创建 File Demo 应用程序,

33、首先利用 AppWizard 创建一个 SDI 应用程序。因为要使 AppWizard 中的所有选项都保持为其默认值,所以可以在第 1 步选择了 SDI 并使 Document/View 被选中之后就Finish 按钮,以加快速度。在 ClassView 中双击 CfileDemoDoc 编辑文档类的件。在 Attributes 部分添加一个 CString 成员变量,变量名为 m_message,使得 Attributes 部分如下所示:/ Attributespublic:CString m_message;在此例中,的文档只不过是单一字符串对象。通常,的文档要更加复杂。,用单字符串演示文

34、档持久性的基本已经足够。对于 MFC 程序员来说,在文档中使用公有变量而不用带有公共函数的私有变量是很常见的。这使得编写文档变量的视图类的代码简单了一些。不过,这会使以后加强这个程序的功能时要做的工作增加。像所有的文档数据一样,这个变量必须要初始化。这个工作在 OnNewDocument( )函数中进行。在 ClassView 中扩展 CFileDemoDoc,双击 OnNewDocument( )编辑它。在其中添加一行初始化串的代码,如程序1 所示。在此之前要把 TODO 注释删除掉。程序1 初始化文档的数据BOOL CFileDemoDoc:OnNewDocument()if (!CDoc

35、ument:OnNewDocument()return FALSE;m_message = 褼 efault Message*;return TRUE;经过对文档类的数据成员 m_message 进行初始化,应用程序可以在视图窗口中显示该数据。你只须编辑视图类的 OnDraw( )函数(参见程序2)。在 ClassView 中扩展 CFileDemoView,并双击 OnDraw( )函数编辑它。在这里也只要删除 TODO 注释,添加一行代码。程序2 显示文档的数据void CFileDemoView:OnDraw(CDC* pDC)CFileDoc* pDoc = GetDocument()

36、;ASSERT_VALID(pDoc);pDC->TextOut(20, 20, pDoc->m_message);现在创建 File Demo 应用程序,确保没有错误,运行这个程序。缺省的消息就显示在屏幕中。现在,增加程序的功能程序员通过修改串值来编辑应用程序的文档。理论上说应用程序应该显示一个框,让用户输入字符串。在这儿,我们利用 EditChange Message 菜单项来自动赋给消息串一个不同的值。Resource 选项卡切换到 ResourceView,扩展 resources,扩展s,双击 IDR_M列表末尾的空AINFRAME,编辑它。菜单中的 Edit 项,下拉出

37、一个子菜单列表。输入 Change &Message,向菜单中添加了另外一项。选择 ViewClassWizard 使得这个菜单项与代码相关联。先使 ID_EDIT_CHANGEMESSAGE 被中,再从右上角的下拉框中选择 CFileDemoView 。在左下角的框中COMMAND,然后Add Function 按钮。框的 OK 按钮,接受所推荐的函数名称OnEditChangeMessage( )。Edit Code,在编辑器中打开这个新函数,按程序辑它。3 编程序3 修改文档的数据void CFileDemoView:OnEditChangemessage()CTime now

38、= CTime:GetCurrentTime();CString changetime = now.Format(褻 hangedat %B %d %H:%M:%S*);GetDocument()->m_message = changetime;GetDocument()->SetModifiedFlag();Invalidate();此函数响应应用程序的 EditMessage 命令,利用当前的日期和时间创建一个串,并将它传送给文档数据成员。对文档类函数SetModifiedFlag( )的调用通知对象其内容已经改变。在用户可能修改数据的地方调用 SetModifiedFlag(

39、 ),就会使程序在用户试图不保存修改过的数据而时发出警告。最后,代码调用 Invalidate( ),强迫屏幕重绘。提示:如果 m_message 是文档类的私有成员变量,就应该用公共的 SetMessage( )函数来调用 SetModifiedFlag( )函数。这样每次 m-message 改变时,都会调用 SetModifiedFlag()函数。这是编正的面向对象的程序的好处。文档类的 Serialize( )函数处理保存和加载文档的数据。程序成的 Serialize( )的空壳。4 显示了由 AppWizard 生程序4 FILEVIEW.CPP文档类的 Serialize()函数v

40、oid CFileDoc:Serialize(CArchive& ar)if (ar.IsStoring()/ TODO: addstoring code hereelse/ TODO: addloading code here由于Cstring 类(m_message 是其的一个对象)定义了>>和<<运算符用于向文档传递字符串,因此保存和加载文档类的数据就是一件简单的事情。在提醒增加保存代码的注释的地方增加以下行:ar<<m_message;同样,在加载代码的地方增加增加以下行:ar>>m_message;运算符将 CSTring m_

41、message 传送给文档运算符把文档赋给 m_message。只要所有的文档成员变量都是如整数或字符那样的简单数据类型,利用象 CString 这样已经定义了此运算符的 MFC 类,保存和加载数据将非常简单,此运算符是为下列这些简单数据类型定义的:*BYTE*WORD*int*LONG*DWORD*float*double创建这个应用程序 FileDemo,并运行它。选择 EditMessage 命令,就可以看到屏幕上出现了新的串,选择 FileSave 并输入一个易记的文件名。再次修改消息,选择 FileNew,用户会得到保存当前修改过的数据的警告。选择 FileOpen 浏览文件,或者在

42、 File 菜单的底部找到刚刚击它也可以打开这个文件。可以看到 File的文件的文件Demo 的确可以保存和重新载入一个串。File Demo 用命令修改了串值用户无意丢失未保存的数据了创建一个持久类但是如果读者已经创建了的定制类用于保存文档的元素该怎么办呢?如何使得此类的一个对象成为持久呢?假设读者现在想改进 File Demo 应用程序使其能够在一个称为 CMessages 的定制类中包含程序的数据。此时该成员变量称为 m_messages,它是 CMessages 的一个实例。此类包含三个 CString 对象,如果要使应用程序正常运行,必须对上述三个对象进行保存和加载。法是分别保存和加

43、载每一个字符串,代码如程序5 所示:保存和加载它们的程序5 保存新类的字符串的注void CFileDoc:Serialize(CArchive& ar)if (ar.IsStoring()ar<<m_messages.m_message1;ar<<m_messages.m_message2;ar<<m_messages.m_message3;elsear>>m_messages.m_message1;ar>>m_messages.m_message2;ar>>m_messages.m_message3;如果在 C

44、Messages 类中定义这三个成员变量为 public,而且知道类是如何实现的,读者就可以编写如5 中所示的代码。之后,如果类以任何其他方式发生变化,此代码也必须改变。把存贮和加载工作交付给 CMessages 类自身去完成更具有面向对象的意义。这需要一些准备,创建一个能串行化的其成员变量的类的基本步骤如下:1)由 CObject 派生此类。2)在类定义部分放置 DECLAR_SERIAL( )宏。3)在类实现部分放置 IMPLEMENT_SERIAL( )宏。4)重载类中的 Serialize( )函数。5)提供一个类的空的缺省构造函数。File Demo 2 应用程序下一个示例应用程序

45、File Demo 2,演示创建一个可由此创建持久对象的类的步骤。它包含了可以修改三个串的 Edit|Change Messages 命令。与 File Demo 程序类似,当用户选择 FileSave 或 FileOpen 时可以保存或加载文档。与创建 File Demo 程序类似,创建一个名为 MultiString 的 SDI 应用程序。添加一个成员变量到文档中,使得 MultiStringDoc.h 的 Attributes 部分如下所示:/Attributespublic:CMessages m_messages;就是编写 CMessage 类。 CMessages 类一览在理解文档

46、类如何实现保存和加载其内容之前,需要了解CMessages 类是如何工作的, 文档类的 m_messages 数据成员是该类的一个对象。在查看此类时,将看到上面提到的创建持久类所需的五个步骤是如何实现的。要想创建 CMessages 类,首先选择 InsertNew Class。修改其类类型为普通类,并命名为 CMessages。在屏幕的下部,选择 CObject 作为基类名,并将 As 列设置为 public,这样将创建两个文件:messages.h 作为件,messages.cpp 作为代码文件。同时还有一些简单的代码被添加到这些文件中(这时候可能会有一条警告信息,找不到 CObject

47、的件,OK 忽略它。因为 CObject 像所有 MFC 文件一样,不需要包含额外的处理就是可用的)。切换到 Multistringdoc.h 中,将下面的代码行添加到类定义部分:#include "Messages.h"这就编辑器知道 CMessages 类的。现在切换回 Messages.h,在其中添加下面的代码行:DECLARE_SERIAL(CMessages)protected:CStringm_message1;CStringm_message2;CStringm_message3;public:void SetMessage(UINT msgNum, CStr

48、ing msg);CString GetMessage(UINT msgNum);void Serialize(CArchive& ar);DECLARE_SERIAL( )宏提供了实现对象持久性所需的附加的函数和成员变量。是类的数据成员,它们是 Cstring 类的三个对象。请注意它们现在是 protected 性质的成员变量。其后是 public 类型的成员函数。SetMessage( )函数的变元为需设置的字符串的索引号和字符串的新值,该函数使得程序能够改变一个数据成员。GetMessage( )是一个实现函数,它使得程序能够检索任何字符串的当前值。它的唯一参量是要检索的字符串的

49、索引号。最后,该类重载 Serialize( )函数,在此所有的数据得以保存和加载,Serialize( )函数是一,每个持久类以不同方式实现。程序6 显示了这些新成员函数的代个持久对象的码,将它添加到 messages.cpp 中。程序6 MESSAGE.CPPCMessage 类的实现文件void CMessages:SetMessage(UINTmsgNum,CStringmsg)switch (msgNum)case 1:m_message1 = msg;break;case 2:m_message2=msg;break;case 3:m_message3=msg;break;SetM

50、odifiedFlag();CString CMessages:GetMessage(UINTmsgNum)switch (msgNum)case 1:returnm_message1;case 2:returnm_message2;case 3:returnm_message3;default:return" "void CMessages:Serialize(CArchive&ar)CObject:Serialize(ar);if (ar.IsStoring()ar << m_message1 << m_message2 <<

51、 m_message3;elsear >> m_message1 >> m_message2 >> m_message3;在 SetMessage( )和 GetMessage( )函数中没有任何技巧,它们精确地完成所给予的任务。不过在 Serialize( )函数中可能会产生一些。首先,注意函数体的第一行调用了基类的 Serialize( )函数。这是一个标准的基类函数的重载函数。在本例中,对 CObject:Serialize( ) 的函数调用没做多少事情,这是因为 CObject 类的 Serialize( )函数为空。尽管如此,调用基类的 Seria

52、lize( )函数是一个需要养成的好习惯,因为你建立的持久类不总是直接由 CObject 类派生的。在调用的此函数基类版本后,Serialize( )用与文档对象相同的方式进行数据的保存和加载。因为必须串行化的数据成员是 CString 对象。程序可以使用>>和<<运算符将字符串写入磁盘。在 messages.cpp 的顶部,include 语句之后,添加下面的代码行:IMPLEMENT_SERIAL(CMessages,CObject,0)IMPLEMENT_SERIAL 宏与 DECLARE_SLRIAL( )组成一对,它提供了使类具有持续能力的函数的实现。此宏的三

53、个变类名、直接基类名和方案号。方案号类似一个版本号。在多数情况下,可使用 0 或 1 作为方案号。 在使用 CMessages 类现在 CMessages 已经被实现了,MultiString 的文档和视图的成员函数也已经可以执行了。首先扩展 CMultiStringDoc,双击 OnNewDocument( )编辑它。将下面的代码添加到TODO 注释处:m_messages.SetMessage(1,"DefaultMessage1");m_messages.SetMessage(2,"DefaultMessage2");m_messages.SetM

54、essage(3,"DefaultMessage3");由于文档类不能直接数据对象的数据成员,它必须通过调用 CMessages 类的 SetMessage( )成员函数来初始化每一个字符串。扩展 CMultiStringView,双击 OnDraw( )函数,按下面的代码行来编辑它:void CMultiStringView:OnDraw(CDC* pDC)CMultiStringDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);pDC->TextOut(20,20,pDoc->m_messages.GetMessage

55、(1);pDC->TextOut(20,40,pDoc->m_messages.GetMessage(2);pDC->TextOut(20,60,pDoc->m_messages.GetMessage(3);与在创建 File Demo 时所作的类似,添加“Change Message”项目到 Edit 菜单中。将它与名为 OnEditChangemessages 的函数相关联。这个函数将会通过调用 CMessages 对象的成员函数来修改数据,如程序7 所示。视图类函数 OnDraw( )同样调用 GetMessage( )成员函数,以CMessages 类的串。程序7 编辑数据串void CMultiStringView:OnEditChangemessages()CMultiStringDoc* pDoc = GetDocument();CTime now = CTime:GetCurrentTime();CString changetime = now.Format(褻 hanged at %B %d %H:%M:%S*);pDoc->m_message

温馨提示

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

评论

0/150

提交评论