版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
4.1主窗口和文档窗口
主窗口(或称主框架窗口)类CMainFrame可以应用在SDI(单文档)和MDI(多文档)结构中。在SDI框架结构中,CMainFrame派生于类CFrameWnd,形成单个文档及其视图的边框。框架窗口即是应用程序的主框架窗口,也是当前文档对应的视图的边框。在MDI框架结构中,CMainFrame派生于类CMDIFrameWnd,它是所有MDI文档窗口的容器,并与它们共享菜单条,MDI框架窗口是出现在桌面中的顶层窗口。每个应用程序只能有一个主框架窗口,主窗口的标题栏上往往显示应用程序的名称。主窗口类的源文件名分别是MainFrm.h和MainFrm.cpp,其类名是CMainFrame。下一页返回4.1主窗口和文档窗口
如果应用程序中还有工具栏(CToolBar)、状态栏(CStatusBar),那么在CMainFrame类还含有表示工具栏和状态栏的成员变量m_wndToolBar和m_wndStatusBar,并在CMainFrame的OnCreate函数中进行初始化。文档窗口对于SDI程序来说,它和主窗口是一致的,即主窗口就是文档窗口,如图4.1所示。对于MDI程序,文档窗口是主窗口的子窗口,如图4.2所示。上一页下一页返回4.1主窗口和文档窗口
文档窗口一般都有相应的可见边框,它的客户区(除了窗口标题栏、边框外的区域)是由相应的视图来构成的,可以说视图是文档窗口内的子窗口。文档窗口时刻跟踪当前处于活动状态的视图的变化,并将用户或系统产生的命令消息传递给当前活动视图。而主窗口负责管理各个用户交互对象(包括菜单、工具栏、状态栏以及加速键),并根据用户操作,相应地创建或更新文档窗口及其视图。在MDI应用程序中,MFCAppWizard创建的文档子窗口类的源代码文件是ChildFrm.h和ChildFrm.cpp,其类名是CChildFrame,它是从CMDIChildWnd派生的。上一页下一页返回4.1主窗口和文档窗口
窗口的风格决定了窗口的外观及功能,用户通过风格的设置增加或减少窗口中所包含的功能,这些功能一般都是由系统内部定义的,不需要用户去编程实现。窗口风格可以通过MFCAppWizard来设置,也可以在主窗口或文档窗口类的PreCreateWindow函数中修改CREATESTRUCT结构,或是可以调用CWnd类的成员函数ModifyStyle和ModifyStyleEx来更改。1.窗口风格窗口的风格,通常有两种形式:一般形式以WS_为前缀;扩展形式以WS_EX_为前缀。这两种形式的窗口风格可在函数CWnd::Create(只能指定窗口的一般风格)或CWnd::CreateEx(可同时支持以上两种风格),对于控件和对话框这样的窗口来说,它们的窗口风格可直接通过其属性对话框来设置。常见的一般窗口风格如表4.1所示。上一页下一页返回4.1主窗口和文档窗口2.修改CREATESTRUCT结构在窗口创建之前,系统自动调用PreCreateWindow虚函数。在MFCAppWizard创建SDI/MDI应用程序结构时,MFC已为主窗口或文档窗口类自动重载了该虚函数。可以在此函数中通过修改其参数CREATESTRUCT结构来设置窗口的绝大多数风格。
CREATESTRUCT结构定义了创建窗口所用的初始参数,其定义如下:上一页下一页返回4.1主窗口和文档窗口typedefstructtagCREATESTRUCT{LPVOIDlpCreateParams; //创建窗口的基本参数HANDLEhInstance; //拥有将创建的窗口的模块实例句柄HMENUhMenu; //新窗口的菜单句柄HWNDhwndParent; //新窗口的父窗口句柄intcy; //新窗口的高度intcx; //新窗口的宽度inty; //新窗口的左上角Y坐标intx; //新窗口的左上角X坐标LONGstyle; //新窗口的风格LPCSTRlpszName; //新窗口的名称LPCSTRlpszClass; //新窗口的窗口类名DWORDdwExStyle; //新窗口的扩展参数}CREATESTRUCT;上一页下一页返回4.1主窗口和文档窗口[例4.1]创建一个“更改风格”的应用程序在SDI程序中,框架窗口默认的风格是WS_OVERLAPPEDWINDOW和FWS_ADDTOTITLE(如表4.1所示)的组合,更改其风格。(1)创建一个单文档的应用程序,名为:更改风格。(2)在主框架程序MainFrm.cpp中找到PreCreateWindow虚函数,并添加代码:上一页下一页返回4.1主窗口和文档窗口BOOLCMainFrame::PreCreateWindow(CREATESTRUCT&cs){………cs.style&=~WS_MAXIMIZEBOX;//按位取反,使新窗口不带有“最大化”按钮cs.cy=::GetSystemMetrics(SM_CYSCREEN)/3;//将窗口大小设置为1/3屏幕大小并居中cs.cx=::GetSystemMetrics(SM_CYSCREEN)/3;//cx和cy表示长和宽cs.y=((cs.cy*3)-cs.cy)/2;//放置的坐标点ycs.x=((cs.cx*3)-cs.cx)/2;//放置的坐标点x//调用基类的PreCreateWindow(cs)函数returnCFrameWnd::PreCreateWindow(cs);}上一页下一页返回4.1主窗口和文档窗口
(3)编译运行,见出现的窗口缩小为屏幕1/3大小,并且不含“最大化”按钮。说明:①PreCreateWindow(CREATESTRUCT&cs):修改窗口的默认风格。框架在即将创建窗口前调用PreCreateWindow。通过修改传递给PreCreateWindow的结构CREATESTRUCT,应用程序可以更改用于创建窗口的属性。例如,为了确保窗口不使用标题,使用按位操作:cs.style&=~WS_CAPTION;。上一页下一页返回4.1主窗口和文档窗口②cs.style&=~WS_MAXIMIZEBOX中的“~”是按位取反运算符,它将WS_MAXIMIZEBOX的值按位取反后,再和cs.style值按位“与”,其结果是将cs.style值中WS_MAXIMIZEBOX标志位清零。③::(双冒号)是作用域符号,其后面的函数是指全局函数。④GetSystemMetrics:表示得到系统自设一些标准的固定数值以及显示器分辨率之类的系统当前设置。SM_CYSCREEN,SM_CYSCREEN是以像素为单位计算的屏幕尺寸。上一页下一页返回4.1主窗口和文档窗口3.用MFCAppWizard设置窗口风格在创建单文档或多文档应用程序过程的第4步中,有一个MFCAppWizard“高级”按钮AdvancedOptions,允许用户指定有关SDI和MDI框架窗口属性,如图4.3所示,单击AdvancedOptions按钮,在弹出的对话框中打开WindowStyles选项卡,如图4.4所示,该页面的各个选项含义如表4.2所示。上一页下一页返回4.1主窗口和文档窗口4.使用ModifyStyle和ModifyStyleEx函数
CWnd类中的成员函数ModifyStyle和ModifyStyleEx也可用来更改窗口的风格,其中ModifyStyleEx还可更改窗口的扩展风格。这两个函数具有相同的参数:
BOOLModifyStyle(DWORDdwRemove,DWORDdwAdd,UINTnFlags=0);BOOLModifyStyleEx(DWORDdwRemove,DWORDdwAdd,UINTnFlags=0);参数:
dwRemove用来指定需要删除的风格;
dwAdd用来指定需要增加的风格;上一页下一页返回4.1主窗口和文档窗口nFlags表示SetWindowPos的标志,0(默认)表示更改风格的同时不调用SetWindowPos(该函数改变一个子窗口、弹出式窗口、顶层窗口的尺寸、位置、Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口)。由于框架窗口在创建时不能直接设定其扩展风格,因此只能通过调用ModifyStyle函数来进行。上一页下一页返回4.1主窗口和文档窗口[例4.2]在多文档(MDI)应用程序中增加水平和垂直滚动条(1)用MFCAppWizard创建一个多文档应用程序,名为:多文档。(2)用ClassWizard为子文档窗口类CChildFrame添加OnCreateClient处理消息。步骤为:选择View→ClassWizard命令,分别选择CChildFrame和OnCreateClient,依次单击AddFunction和EditCode按钮。上一页下一页返回4.1主窗口和文档窗口//子文档窗口函数,改变子文档窗口风格BOOLCChildFrame::OnCreateClient(LPCREATESTRUCTlpcs,CCreateContext*pContext){ ModifyStyle(0,WS_VSCROLL,0);//垂直滚动轴
ModifyStyle(0,WS_HSCROLL,0);//水平滚动轴
returnCMDIChildWnd::OnCreateClient(lpcs,pContext);}(3)编译并运行,结果如图4.7所示。上一页下一页返回4.1主窗口和文档窗口说明:如果在单文档程序中增加水平和垂直滚动条,则要在主框架CMainFrame.cpp的OnCreate函数中添加下列代码。intCMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct){………ModifyStyle(0,WS_VSCROLL,0);//垂直滚动轴
ModifyStyle(0,WS_HSCROLL,0);//水平滚动轴
return0;}上一页返回4.2改变窗口的状态4.2.1用ShowWindow改变窗口的显示状态当应用程序运行时,Windows会自动调用应用程序框架内部的WinMain函数,并自动查找该应用程序类的全局变量theApp,然后自动调用用户应用程序类的虚函数InitInstance,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。如例4.1的应用程序“更改风格.cpp”文件中的InitInstance()函数的最后两条代码,就是系统默认的窗口显示;可以对此加以修改,按需要显示应用程序的窗口,下面涂有灰色的代码就是被修改的(窗口被显示后就立即最大化了)。下一页返回4.2改变窗口的状态BOOLCMyApp::InitInstance(){………
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);//激活窗口并使之最大化
m_pMainWnd->UpdateWindow();//更新窗口
returnTRUE;}说明:①m_pMainWnd是主框架窗口指针变量。②ShowWindow()是CWnd类的成员函数,用于按指定的参数显示窗口,该参数的值如表4.3所示。上一页下一页返回4.2改变窗口的状态③UpdateWindow():Windows的消息循环是这样的:当调用GetMessage的时候,如果当前没有任何消息可处理,那么它会看看窗口会不会需要重画,如果需要,就发一个WM_PAINT消息。也就是说,WM_PAINT的优先级是很低的。如果当前需要立即重画,就必须调用UpdateWindow这个函数直接给窗口函数发出WM_PAINT,不经过GetMessage,且返回。一般情况下,不用UpdateWindow,GetMessage过一会儿也会给出这个消息。问题是,有些情况下,窗口不停的接收到别的消息,这个时候,如果不用UpdateWindow,那么可能就不能及时地重画了。上一页下一页返回4.2改变窗口的状态通过指定ShowWindow函数的参数值可以修改窗口的显示状态,如:在例4.2应用程序的“多文档.cpp”文件的InitInstance()函数中,将最后两条代码改写为:BOOLCMyApp::InitInstance(){ ………m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);//激活窗口并使之最大化
m_pMainWnd->UpdateWindow();returnTRUE;}上一页下一页返回4.2改变窗口的状态
这样就将窗口的初始状态设置为“最大化”了。由于用户应用程序类继承了基类CWinApp的特性,因此也可在用户应用程序类CWinApp中使用公有型(public)DWORD类型的成员变量m_nCmdShow,通过对其进行赋值,同样能达到效果。上一页下一页返回4.2改变窗口的状态例如:在例4.2应用程序的“多文档.cpp”文件的InitInstance()函数中,将最后两条代码改写为如下3条代码:BOOLCMyApp::InitInstance(){ ………m_nCmdShow=SW_SHOWMAXIMIZED;//激活窗口并使之最大化pMainFrame->ShowWindow(m_nCmdShow);pMainFrame->UpdateWindow();returnTRUE;}上一页下一页返回4.2改变窗口的状态程序运行后,窗口的初始状态也设置为“最大化”了。说明:单文档程序亦如此,事先也要在应用程序头文件public下定义:DWORDm_nCmdShow;BOOLCMyApp::InitInstance(){ m_nCmdShow=SW_SHOWMAXIMIZED;//激活窗口并使之最大化
m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); returnTRUE;}上一页下一页返回4.2改变窗口的状态4.2.2用SetWindowPos或MoveWindow改变窗口的大小和位置
CWnd中的SetWindowPos是一个非常有用的函数,它不仅可以改变窗口的大小、位置,而且还可以改变所有窗口在堆栈排列的次序(Z次序),这个次序是根据它们在屏幕出现的先后来确定的。其函数原型为:上一页下一页返回4.2改变窗口的状态BOOLSetWindowPos(constCWnd*pWndInsertAfter,intx,inty,intcx,intcy,UINTnFlags);参数:
pWndInsertAfter 表示窗口对象指针,它可以预定义下列窗口对象的地址:
wndBottom 将窗口放置在Z次序中的底层;
wndTop 将窗口放置在Z次序中的顶层;
wndTopMost 设置最顶层窗口;
wndNoTopMost 将窗口放置在所有最顶层的后面,若此窗口不是最顶窗口,则此标志无效。
intx、inty分别表示窗口新的左上角x、y坐标。
intcx、intcy分别表示窗口的宽度和高度。
nFlags表示窗口新的大小和位置方式,如表4.4所示。上一页下一页返回4.2改变窗口的状态
函数CWnd::MoveWindow也可用来改变窗口的大小和位置,与SetWindowPos函数不同的是,用户必须在MoveWindow函数中指定窗口的大小,其函数原型为:
voidMoveWindow(intx,inty,intnWidth,intnHeight,BOOLbRepaint=TRUE);voidMoveWindow(LPCRECTlpRect,BOOLbRepaint=TRUE);参数:x、y表示窗口新的左上角坐标;nWidth、nHeight表示窗口新的宽度和高度;bRepaint用于指定窗口是否重绘;lpRect表示窗口新的大小和位置。上一页下一页返回4.2改变窗口的状态
[例4.3]用上面的MoveWindow()函数,把主窗口移到屏幕的(200,100)处(1)创建一个单文档的应用程序名为:窗口位置。(2)打开应用程序“窗口设置.cpp”,找到BOOLCMyApp::InitInstance()函数,并添加代码:上一页下一页返回4.2改变窗口的状态BOOLCMyApp::InitInstance(){ ………//不改变当前的窗口大小和窗口Z次序m_pMainWnd->SetWindowPos(NULL,100,100,0,0,SWP_NOSIZE|SWP_NOZORDER);CRectrcWindow;m_pMainWnd->GetWindowRect(rcWindow);//改变窗口位置m_pMainWnd->MoveWindow(200,100,rcWindow.Width(),rcWindow.Height(),TRUE);returnTRUE;}上一页下一页返回4.2改变窗口的状态这样,当程序运行后,出现的窗口就是在屏幕坐标的(200,100)处。当然,改变窗口的大小和位置的CWnd成员函数还不止以上两个,例如:若用下面两条代码替换上面的代码,则实现窗口居中和主框架窗口置于屏幕中央:BOOLCMyApp::InitInstance(){ ………AfxGetMainWnd()->CenterWindow(CWnd::GetDesktopWindow());//窗口置屏幕中央AfxGetMainWnd()->CenterWindow();//将主框架窗口居中
returnTRUE;}上一页返回4.3文档串行化
串行化在面向对象程序设计领域中的基本概念是指对象可以被持续,即当程序退出时,数据可以被保存在磁盘中,而当程序重新运行时,又可以从磁盘中读取恢复。对象的这种保存和恢复的过程就称为“串行化”。在MFC中,对象串行化的成员函数称为Serialize()成员函数。这个函数提供了将类的数据进行存盘和读取的功能。该函数在MFCAppWizard自动生成,置于文档类CDocument的执行文件CMyDoc.cpp中,文档的串行化就在该函数中进行。在MFC中,串行化过程是顺序的,即与文档相关的所有对象只能在某个单独的文件中进行顺序的读或写,而并不能进行随机的访问。下一页返回4.3文档串行化4.3.1文档串行化过程先举一个简单的例子,看看如何用Serialize函数实现读取文档中的数据。[例4.4]使用Serialize函数,读取文档中的数据(1)创建一个单文档应用程序,名为:串行化。(2)在文档类的头文件Doc.h中的public下加字符类型的数组变量:charm_ch[300];(3)在文档类即Doc.cpp中找到Serialize(CArchive&ar)函数并添加代码:上一页下一页返回4.3文档串行化voidCMyDoc::Serialize(CArchive&ar){if(ar.IsStoring())//判断当前读写状态{//TODO:addstoringcodehere}else{for(inti=0;i<sizeof(m_ch);i++)//文件内容输入到数组中
ar>>m_ch[i];CStringstr;上一页下一页返回4.3文档串行化//以文件字符串格式将数组内容写入“str”str.Format(_T("%s"),m_ch);//显示输出“str”字符串文本
AfxMessageBox(str);}}(4)编译运行,选择“文件”→“打开”命令,就弹出一个对话框,如图4.8所示。选中某个文件后,再单击对话框上的“打开”按钮,便显示出该文件前300个字符,如图4.9所示。上一页下一页返回4.3文档串行化说明:
1)CArchive(归档):使用缓冲区作为临时数据存储地,读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储介质。好比火车货场装运零散的货物被收集,当总量到达火车运量的时候,由火车装运走。
2)CArchive(归档)对象“&ar”:要么存储数据(即写入数据或将数据串行化),要么加载数据(即读取数据或将数据反串行化),但决不能同时进行。CArchive对象的寿命只限于将对象写入文件或从文件读取对象的一次传递。因此,需要两个连续创建的CArchive对象将数据序列化到文件,然后从文件反串行化数据。上一页下一页返回4.3文档串行化3)CArchive构造函数的原型如下:
CArchive(CFile*pFile,UINTnMode,intnBufSize=4096,void*lpBuf=NULL);参数:
pFile用于指定与之关联的文件指针;
nBufSize表示内部文件的缓冲区大小,默认值为4096字节;
lpBuf表示自定义的缓冲区指针,若为NULL,则表示缓冲区在堆内存中,当对象清除时,缓冲区内存也被释放,若指明用户缓冲区,对象消除时,缓冲区内存不会释放;上一页下一页返回4.3文档串行化nMode用于指定文档是用于存入还是读取,它可以是CArchive::load(读取数据)、CArchive::store(存入数据)或CArchive::bNoFlushOnDelete(当析构函数被调用时,避免文档自动调用Flush。若设置这个标志,则必须在析构函数被调用之前调用Close,否则文件数据将被破坏)。也可将一个CArchive对象与CFile类指针相关联,如下(ar是CArchive对象):
ConstCFile*fp=ar.GetFile();MFCAppWizard应用程序向导在生成应用程序框架时,只创建了文档派生类串行化函数Serialize的框架,如例4.4中的:voidCMyDoc::Serialize(CArchive&ar)函数体。上一页下一页返回4.3文档串行化
要想实现数据的保存和回复,即串行化,必须在Serialize()函数中加入读和写的代码(如例4.4加入的代码),并且所有需要保存的变量都应该在文档类中声明。
Serialize函数有一个指向类CArchive的指针参数ar,CArchive是C++iostream库的类,它写入和读出的数据都是二进制代码,而不是规格化数据。CArchive类对象使用操作符插入(<<)和提取(>>)执行读写操作。这种方式与cin和cout的输入输出流相似,只是这里处理的是对象,而cin和cout处理的是ASCII字符串。
C++主要通过文件句柄来实现磁盘输入和输出,一个文件句柄与一个磁盘文件相关联。而MFC中物理文件的读写操作由CFile类及其派生类来完成,CArchive类对象为读写CFile类对象中的可串行化数据提供了一种安全的缓冲机制。上一页下一页返回4.3文档串行化
当文档数据被存储时,函数Serialize通过调用文档数据对象的Serialize函数,从每一个串行化的对象中读取数据并把它存储到与包含在ar中的成员变量(类型为CFile*)相关联的文件。当文档数据被载入时,函数Serialize从与包含在ar中的成员变量相关联的那个文件中读取数据,然后通过调用文档类数据对象的函数Serialize把它们还原。串行化过程中所创建的磁盘文件称为串行化的归档文件,它存储了应用程序中每一个数据对象的一份复制,并且包括每一个对象的类型信息和版本信息。上一页下一页返回4.3文档串行化
串行化这种保存数据的方式适合大多数的情况,特别是那些数据量不大且要一次性全部读入或写出的情况。当用户选择文件菜单中的New、Open、Save和Saveas等菜单命令时,都会调用文档派生类的成员函数Serialize,实现与序列化有关的操作。下面再通过一个简单例子,介绍怎样用Serialize函数实现保存文档中的数据。上一页下一页返回4.3文档串行化[例4.5]使用Serialize函数,保存文档中的数据(1)创建一个单文档应用程序,名为:保存数据,或用上个程序:串行化。(2)在CMyDoc.h中加一个成员变量charm_ch[300],若用“串行化”程序,此步不用操作。(3)在CMyDoc.cpp中找到:voidCMyDoc::Serialize(CArchive&ar)函数并添加代码:
voidCMyDoc::Serialize(CArchive&ar)//CArchive的对象是ar(引用)上一页下一页返回4.3文档串行化{if(ar.IsStoring())//判断当前读写状态
{for(inti=0;i<sizeof(m_ch);i++)ar<<m_ch[i];//将数组内容保存的“ar”}else//下面代码是上个程序“串行化”所加的代码
{for(inti=0;i<sizeof(m_ch);i++)ar>>m_ch[i];//文件“ar”内容输入到数组中
CStringstr;str.Format(_T("%s"),m_ch);//以文件字符串格式将数组内容写入“str”AfxMessageBox(str);//显示输出“str”字符串文本
}}上一页下一页返回4.3文档串行化
上述代码的结果是当保存的文件名成功指定后,该文件保存m_ch中的300字符。若文件中有其他数据,则数据被自动清除。若想看看数据保存与否,必须再加例4.4中else语句的代码,之后按以下步骤操作即可。打开某文件夹里的一个文件(*.*),见到用对话框形式显示的这个文件前的300个字符,单击“确定”按钮后这个对话框消失,再选择“另存为”命令,文件名写:“小山”,然后再打开“小山”就见到刚才保存的文件的300个字符。如果打开没有内容,先打开一个别的文件,关闭后再打开“小山”,就能看到另存为到“小山”文件里的300个字符了。上一页下一页返回4.3文档串行化说明:1)当MFCAppWizard创建应用程序时,它会自动将“文件”菜单中的“打开”命令(ID号为ID_FILE_OPEN)映射到CWinApp的OnFileOpen成员函数。这一结果可以从应用类(CMyApp.cpp)的消息入口处得到验证(涂灰色的一条语句):BEGIN_MESSAGE_MAP(CMyApp,CWinApp)//{{AFX_MSG_MAP(CMyApp)………ON_COMMAND(ID_FILE_NEW,CWinApp::OnFileNew)ON_COMMAND(ID_FILE_OPEN,CWinApp::OnFileOpen)//StandardprintsetupcommandON_COMMAND(ID_FILE_PRINT_SETUP,CWinApp::OnFilePrintSetup)END_MESSAGE_MAP()上一页下一页返回4.3文档串行化
除了使用“文件”→“打开”命令外,用户也可以选择最近使用过的文件列表来打开相应的文档。在应用程序的运行过程中,系统会记录下4个(默认)最近使用过的文件,并将文件名保存在Windows的注册表中。当每次启动应用程序时,应用程序都会在“文件”菜单中显示最近使用过的文件名称。
2)当MFCAppWizard创建应用程序时,它会自动将“文件”菜单中的“保存”命令与文档类CDocument的OnFileSave函数在内部关联起来,但用户在程序框架中看不到相应的代码。OnFileSave函数还会进一步完成下列工作:①弹出通用文件“保存”对话框,让用户提供一个文件名;②调用文档对象的CDocument::OnSaveDocument虚函数,接着又自动调用Serialize函数,将CArchive对象的内容保存在文档中。文档存盘的必要操作都是由系统自动完成的。上一页下一页返回4.3文档串行化4.3.2文档串行化操作从上述的单文档串行化过程可以看出,打开和保存文档时,系统会自动调用Serialize函数。事实上,MFCAppWizard在创建文档应用程序框架时,已在文档中重载了Serialize函数,通过在该函数中添加代码可达到实现数据串行化的目的。
Serialize函数的参数ar是一个CArchive类引用变量。通过判断ar.IsStoring的结果是“真”还是“假”就可决定向文档写或读数据。上一页下一页返回4.3文档串行化CArchive(归档)类提供对文件数据进行缓存,同时还保存一个内部标记,用于标识文档是存入(写盘)还是载入(读盘)。每次只能有一个活动的存档与ar相连。通过CArchive类可以简化文件操作,提供“<<”和“>>”运算符,用于向文件写入简单的数据类型以及从文件中读取它们,CArchive支持的数据类型如表4.5所示。上一页下一页返回4.3文档串行化
除了“<<”和“>>”运算符外,CArchive类还提供成员函数ReadString和WriteString用于从一个文件对象中读、写一行文本,它们的原型如下:
BOOLReadString(CString&rString);LPTSTRReadString(LPTSTRlpsz,UINTnMax);voidWriteString(LPCTSTRlpsz);参数:lpsz用于指定读或写的文本内容;
nMax用于指定可以读出的最大字符个数。注意:当向一个文件写一行字符串时,字符"\0"和"\n"都不会写到文件中。上一页下一页返回4.3文档串行化CArchive类没有基类,它提供了串行化对象从文件中读写的类型安全缓冲机制,可以把CArchive对象想象成一种二进制流,就像输入/输出流一样可以顺序高效的处理二进制对象数据。使用CArchive对象之前,必须先创建一个CFile对象,同时保证CArchive对象的读写标志设置和文件打开方式相一致。CArchive类的成员函数如表4.6所示。上一页下一页返回4.3文档串行化
文件数据的串行化归根到底是类的串行化,所以实现串行化的主要工作就是设计串行化的类,MFC应用程序实现类的串行化必须满足以下4个条件:(1)类必须直接或间接从CObject类派生而来;(2)在自定义类中重载序列化成员函数Serialize;(3)类必须定义一个不带参数的构造函数,从磁盘文件载入文档时调用该构造函数来创建一个可序列化的对象,使用从文件中读出来的数据填充对象的成员变量;(4)在类的头文件中使用DECLARE_SERIAL宏,在类的实现文件中使用IMPLEMENT_SERIAL宏。上一页下一页返回4.3文档串行化说明:
1)DECLARE_SERIAL宏是指序列化特性,它是一个完全自动化的存储机制,它可以将一个对象数组(可能含有A,B,C类的对象)存储进去,而且能够根据存储的情况准确的载入进来,它最主要的用途是一种智能存储;
2)IMPLEMENT_SERIAL宏能够进行文件读写动作,前提是拥有动态生成的能力,所以,MFC设计了两个宏——DECLARE_SERIAL
和IMPLEMENT_SERIAL。下面就按照这4个条件,来自行设计一个串行化程序。上一页下一页返回4.3文档串行化[例4.6]编写一个绘制图形的程序当按下鼠标左键并移动时,开始绘图,并能将绘制好的图形保存在磁盘上(注意:此程序一定要保存好,例4.14要用到这个程序)。(1)创建一个SDI单文档的应用程序,名为:绘制图形。(2)定义一个新类,类名为:CLine,基类为:CObject,步骤为:上一页下一页返回4.3文档串行化
选择Insert→NewClass命令,弹出一个NewClass对话框,在Classtype下拉列表框中选择GenericClass,在Name处写类名:CLine,单击Baseclass[es]栏里面的蓝色横条,在DerivedFrom的下面出现有光标闪动的空白格,在其中写:CObject,而右边在As的下面就出现了public,单击OK按钮,然后单击“确定”按钮,新类CLine就建好了。(3)在Line.h(新建类)头文件中,加入代码如下:上一页下一页返回4.3文档串行化classCLine:publicCObject{public:CLine();virtual~CLine();CPointm_Start;//线段的起点CPointm_End;//线段的终点CLine(CPointStart,CPointEnd);//构造函数,设置线段的开始和结束位置voidDrawLine(CDC*Pdc);//绘制线段函数};上一页下一页返回4.3文档串行化(4)在实现文件Line.cpp中加入以下代码。1)在构造函数中将线段的起点和终点赋初值:CLine::CLine(){m_Start=m_End=0;}2)在Line.cpp文件末位写入在头文件中定义的函数及其代码(全用手写):CLine::CLine(CPointStart,CPointEnd)//构造函数,设置线段的开始和结束位置(全用手写){m_Start=Start;m_End=End;}voidCLine::DrawLine(CDC*pDC)//绘制线段函数(全用手写){pDC->MoveTo(m_Start);//移到起点m_StartpDC->LineTo(m_End);//在m_Start和m_End之间画一条线}上一页下一页返回4.3文档串行化(5)在视图类的头文件CMyView.h中定义两个公有变量。public:CPointm_ptEnd;//终点
CPointm_ptStart;//始点在视图类CMyView.cpp的构造函数中初始化这两个公有变量,分别记录线段起点和终点。CMyView::CMyView(){ m_ptEnd=0;//终点
m_ptStart=0;//始点}上一页下一页返回4.3文档串行化(6)在视图类(View)中分别添加WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP消息映射函数OnLButtonDown、OnMouseMove和OnLButtonUp。
1)当按下鼠标左键时,需要记住鼠标按下位置,因此在OnLButtonDown函数中加入代码:voidCMyView::OnLButtonDown(UINTnFlags,CPointpoint){ SetCapture();//捕捉鼠标
m_ptStart=m_ptEnd=point;//得到起点和终点的坐标
CView::OnLButtonDown(nFlags,point);}上一页下一页返回4.3文档串行化说明:①SetCapture表示函数将随后的鼠标信息捕捉当前视图,直到调用函数ReleaseCapture;②ReleaseCapture表示函数从当前线程中的窗口释放鼠标捕获,并恢复通常的鼠标输入处理。捕获鼠标的窗口接收所有的鼠标输入(无论光标的位置在哪里),除非按鼠标键时,光标热点在另一个线程的窗口中。
2)当移动鼠标时,需要用蓝色虚线画出线段轮廓,因此在函数OnMouseMove中加入代码:上一页下一页返回4.3文档串行化voidCMyView::OnMouseMove(UINTnFlags,CPointpoint){ if(GetCapture()!=this)return;//若捕捉鼠标信息的窗口不是当前视图则返回
CClientDCdc(this); CPennewPen(1,1,RGB(0,0,255));//创建画笔
CPen*oldPen=dc.SelectObject(&newPen); intnOldMode=dc.SetROP2(R2_NOTXORPEN);//设置绘图模式
dc.MoveTo(m_ptStart); dc.LineTo(m_ptEnd); m_ptEnd=point; dc.MoveTo(m_ptStart); dc.LineTo(m_ptEnd); dc.SelectObject(oldPen);//恢复原先的画笔
dc.SetROP2(nOldMode);//恢复原先的绘图模式
CView::OnMouseMove(nFlags,point);}上一页下一页返回4.3文档串行化说明:函数GetCapture获取捕捉鼠标信息的窗口,若窗口为当前视图,则进行操作,否则返回,在鼠标移动过程中,用蓝色的虚线表示鼠标的移动轨迹。为保证只显示新轨迹,要重画鼠标移动前的轨迹;如果构造CClientDC对象,则设备环境的映射区域限于客户区域,不能在客户区域外绘图,原点(0,0)在客户区左上角,如果创建CWindowDC对象,则设备环境的映射区域为整个窗口(包括标题栏、状态栏、窗口边框等),原点(0,0)在整个窗口的左上角。注意:①视图窗口没有非客户区域;②视图窗口覆盖在框架窗口之上。上一页下一页返回4.3文档串行化3)当放开鼠标左键时,需要记住鼠标放开时的位置,在OnLButtonUP函数中添加下列代码:voidCMyView::OnLButtonUp(UINTnFlags,CPointpoint){ if(GetCapture()!=this)return;//若捕捉鼠标信息的窗口不是当前视图则返回
CClientDCdc(this); m_ptEnd=point; dc.MoveTo(m_ptStart); dc.LineTo(m_ptEnd); ReleaseCapture(); CView::OnLButtonUp(nFlags,point);}上一页下一页返回4.3文档串行化
编译运行,就可以根据鼠标的移动轨迹画出一条线段,注意,按下左键鼠标移动画的是蓝色虚线,放开左键,线就成实线并变黑色了,如图4.10所示。这时,画的线段并不能保存;也就是退出程序后,画的线段就无法再找到了。下面开始作保存线段的工作,也就是实现文档数据的串行化。上一页下一页返回4.3文档串行化(7)文档数据串行化,需添加以下代码。1)在CLine类的头文件中加入下列代码:protected://保护模式virtualvoidSerialize(CArchive&ar);DECLARE_SERIAL(CLine)在Line.cpp文件中的构造函数之前,添加下列代码:IMPLEMENT_SERIAL(CLine,CObject,1)上一页下一页返回4.3文档串行化2)在Line.cpp文件末尾加入头文件中定义的Serialize函数,并添加下列代码:voidCLine::Serialize(CArchive&ar)//(全用手写){ if(ar.IsStoring()) {ar<<m_Start<<m_End;} else {ar>>m_Start>>m_End;}}上一页下一页返回4.3文档串行化3)在文档类头文件CMyDoc.h中加入下列代码:
protected:CObListm_LineList;//一系列线段的链表
public:CObject*GetLineList();//得到当前的线段链表voidDeleteContents();//删除文档内容,当文档被关闭、打开或新建时,应用程序会自动调用//该函数上一页下一页返回4.3文档串行化4)在文档的实现文件CMyDoc.cpp末尾加入如上定义的函数和其代码:CObject*CMyDoc::GetLineList()//(全用手写){return(&m_LineList);}voidCMyDoc::DeleteContents(){ while(m_LineList.GetHeadPosition()) deletem_LineList.RemoveHead(); CDocument::DeleteContents();}上一页下一页返回4.3文档串行化(8)在文档类CMyDoc.cpp的Serialize函数中最后面加入下列代码:voidCMyDoc::Serialize(CArchive&ar){ if(ar.IsStoring()) {//TODO:addstoringcodehere } else {//TODO:addloadingcodehere } m_LineList.Serialize(ar);}上一页下一页返回4.3文档串行化(9)在视图类CMyView.cpp实现文件中的OnDraw函数中加入下列代码:voidCMyView::OnDraw(CDC*pDC){ CMyDoc*pDoc=GetDocument(); ASSERT_VALID(pDoc); CObList*pList=(CObList*)pDoc->GetLineList(); POSITIONpos=pList->GetHeadPosition(); while(pos!=NULL) { CLine*pLine=(CLine*)(pList->GetNext(pos)); pLine->DrawLine(pDC); }}上一页下一页返回4.3文档串行化注意:要在CMyView.cpp的前面加入CLine类包含#include“Line.h”。(10)修改视图类的OnLButtonUp函数,代码如下:voidCMyView::OnLButtonUp(UINTnFlags,CPointpoint){ if(GetCapture()!=this)return;//若捕捉鼠标信息的窗口不是当前视图则返回
CClientDCdc(this); m_ptEnd=point; dc.MoveTo(m_ptStart); dc.LineTo(m_ptEnd);上一页下一页返回4.3文档串行化if(m_ptStart!=m_ptEnd) { CLine*pLine=newCLine(m_ptStart,m_ptEnd); CMyDoc*pDoc=GetDocument();//获取与视图类相关联的文档
CObList*pList=(CObList*)pDoc->GetLineList();//获取线段的链表
pList->AddTail(pLine);//将新的线段加入链表
pDoc->UpdateAllViews(this,NULL,NULL);//更新视图
pDoc->SetModifiedFlag(TRUE);//设置文档修改标志
} ReleaseCapture(); CView::OnLButtonUp(nFlags,point);}上一页下一页返回4.3文档串行化
(11)设置文档类型:此处的文档类型是指应用程序处理的文档存盘后的文件的类型或扩展名。前面完成了文档的串行化,现在可以设置文档类型了,这里的文件扩展名为:.Line。在应用程序的资源页面中,单击列表中的StringTable,打开字符串表资源,双击其中的StringTable,打开字符串资源。在字符串资源中找到ID号“ID_MAINFRAME”,双击该项,弹出字符串属性窗口,将其Caption中的内容改写成如图4.11所示,即下面的内容:上一页下一页返回4.3文档串行化
(12)编译运行后,在文档窗口中绘制好图形,选择“保存”命令,可将绘制的图形保存在已命名的文件(扩展名为.Line)中,退出后再重新运行程序,选择“打开”命令,找到已命名的文件,在弹出的对话框中单击它,再单击“打开”按钮,则刚才画好的图就显示在文档窗口中了,如图4.12和4.13所示。上一页下一页返回4.3文档串行化4.3.3文档模板字串资源在MFCAppWizard创建的应用程序资源中,许多资源标识符都是IDR_MAINFRAME,意味着这些具有同名标识符的资源将被框架自动加载到应用程序中。其中,StringTable(字符串)资源列表中也有一个IDR_MAINFRAME项,它是用于标识文档类型、标题等内容的,称为“文档模板字符串资源”。其内容为(以上面的单文档应用程序“绘制图形”为例):绘制图形\n\n绘制图形\n绘制图形Files[*.Line]\n.Line\n绘制图形.Document\n绘制图形Document。上一页下一页返回4.3文档串行化
可以看出,IDR_MAINFRAME所标识的字符串被分成了一些以“\n”结尾的子串,这些子串共有7段,每段都有特定的用途,其含义如表4.7所示。文档模板字串资源内容既可直接通过字串资源编辑器进行修改,也可在文档应用程序创建向导的第4步中,通过AdvancedOptions对话框中的DocumentTemplateStrings选项卡来指定,如图4.14所示(以上面的单文档应用程序“绘制图形”为例),图中的数字标号表示该项的含义与表4.7中对应串号的含义相同。上一页返回4.4使用简单数组集合类
上述文档的读、写是通过变量来存取文档数据的,实际上还可以使用MFC提供的集合类来进行操作。这样不仅可以有利于优化数据结构,简化数据的串行化,而且保证数据类型的安全性。MFC提供的集合类可分为3类:①链表集合类(List);②数组集合类(Array);③映射集合类(Map)。这里只讨论简单数组集合类,它们的构造原型是:下一页返回4.4使用简单数组集合类CByteArrayCByteArray()4125036//DWORD数组集合类CDWordArrayCDWordArray()CObArrayCObArray()//对象数组集合类CPtrArrayCPtrArray()//指针数组集合类CStringArrayCStringArray()//字符串数组集合类CUIntArrayCUIntArray()//UINT数组集合类CWordArrayCWordArray()//WORD数组集合类下面代码说明了简单数组集合类的两种构造方法:CObArrayarray;//使用默认的内存块大小//使用堆内存中的默认的内存块大小CObArray*pArray=newCObArray;上一页下一页返回4.4使用简单数组集合类4.4.1应用实例下面用数组集合类来作一个实际的例子。首先通过对话框来输入一个学生记录,记录包括学生的姓名、学号和3门成绩。然后将记录内容保存到一个对象数组集合类对象中,最后通过文档序列化将记录保存到一个文件中。当添加记录或打开一个记录时,还会将数据显示在文档窗口中。上一页下一页返回4.4使用简单数组集合类[例4.7]使用数组集合类(1)创建一个单文档的应用程序名为:学生记录。(2)向应用程序添加一个对话框资源,ID为IDD_DIALOG,Caption标题改为:学生信息,将OK和Cancel按钮标题分别改为“确定”和“取消”,并为该对话框资源创建一个对话框类,类名为:CaddDlg。(3)参看图4.15所示的控件布局,用编辑器为对话框添加如表4.8所示的一些控件。上一页下一页返回4.4使用简单数组集合类
(4)打开ClassWizard的MemberVariables选项卡,在Classname中选择CAddDlg,选择所需的控件ID标识符,双击或单击AddVariables按钮。依次按表4.9所示的控件增加成员变量。(5)添加一个新类CStudent类并使该类可序列化。在例4.6中,加入一个新的可序列化的类,是选择Insert(插入)的方法进行的。在此例中为了简化类文件,创建新的可串行化的类Student的声明代码和实现代码是直接添加在CMyDoc.h和CMyDoc.cpp文件中。上一页下一页返回4.4使用简单数组集合类说明:一个可序列化的类必须是CObject的一个派生类,且在类的声明文件中包含DECLARE_SERIAL宏调用,而在类的实现文件中包含IMPLEMENT_SERIAL宏调用,这个宏有三个参数:前两个参数分别表示类名和基类名,第三个参数表示应用程序的版本号。最后还需要重载Serialize函数,使该类的数据成员进行相串行列化操作。由于使用ClassWizard无法添加一个CObject派生类,因此必须手动添加CObject的派生类Student,并且将DECLARE_SERIAL(CStudent)和IMPLEMENT_SERIAL(CStudent,CObject,1)宏分别加到Student.h和Student.cpp中。上一页下一页返回4.4使用简单数组集合类CObject是大多数MFC类的根类或基类。CObject类有很多有用的特性:对运行时类信息的支持,对动态创建的支持,对串行化的支持,对象诊断输出等。MFC从CObject派生出许多类,具备其中的一个或者多个特性。程序员也可以从CObject类派生出自己的类,利用CObject类的这些特性。
1)在CMyDoc.h文件的classCMyDoc:publicCDocument前面添加下列代码(全用手写):上一页下一页返回4.4使用简单数组集合类classCStudent:publicCObject{ CStringstrName; //姓名
CStringstrID; //学号
floatfeng,fmat,fcom; //3门课程
floatfAverage; //平均成绩
DECLARE_SERIAL(CStudent) //宏调用(派生类本身)public: CStudent(){};//声明构造函数与类同名
CStudent(CStringname,CStringid,floatf1,floatf2,floatf3);//声明重载 //构造函数
voidSerialize(CArchive&ar);//声明序列化函数
voidDisplay(inty,CDC*pDC);//声明在坐标为(0,y)处显示数据的函数};上一页下一页返回4.4使用简单数组集合类2)在CMyDoc.cpp文件末尾,添加上面定义的重载构造函数的实现代码(全用手写):CStudent::CStudent(CStringname,CStringid,floatf1,floatf2,floatf3)//重载构
//造函数{ strName=name;//姓名
strID=id;//学号
feng=f1;//英语
fmat=f2;//数学
fcom=f3;//计算机
fAverage=(float)((f1+f2+f3)/3.0);//平均成绩}上一页下一页返回4.4使用简单数组集合类3)在CMyDoc.cpp末尾加上面定义的显示数据函数(全用手写):voidCStudent::Display(inty,CDC*pDC)//在坐标为(0,y)处显示数据{ CStringstr; str.Format("%s%s%f%f%f%f",strName,strID,feng, fmat,fcom,fAverage);//打印姓名、学号、3门成绩、平均成绩
pDC->TextOut(0,y,str);}IMPLEMENT_SERIAL(CStudent,CObject,1)//其参数为:类名,基类名,应用程序版本号上一页下一页返回4.4使用简单数组集合类4)在CMyDoc.cpp末尾加上面定义的Serialize(CArchive&ar)序列化函数(全用手写):voidCStudent::Serialize(CArchive&ar)//使该类的数据成员进行相关序列化操作{ if(ar.IsStoring())//判断当前读写状态(真或假),决定向文档写或读数据
ar<<strName<<strID<<feng<<fmat<<fcom<<fAverage;//向文件写入
else ar>>strName>>strID>>feng>>fmat>>fcom>>fAverage;//从文件读取}上一页下一页返回4.4使用简单数组集合类(6)添加并处理菜单项,包括以下几步。1)在菜单资源的主菜单中增加顶层菜单项:学生信息(&S),在该顶层菜单中增加子菜单,名为:添加学生记录,ID为:ID_STUREC_ADD。2)在CMyDoc.h文件的public下添加:CObArraym_stuObArray;//对象数组集合类CObArray的对象在CMyDoc.cpp文件的前面,增加包含CAddDlg的头文件:#include"AddDlg.h"上一页下一页返回4.4使用简单数组集合类3)用ClassWizard为CMyDoc类添加菜单ID_STUREC_ADD的COMMAND消息映射,并在映射函数中添加下列代码:voidCMyDoc::OnSturecAdd(){CAddDlgdlg;if(IDOK==dlg.DoModal()){//添加记录
CStudent*pStudent=newCStudent(dlg.m_strName, dlg.m_strID,dlg.m_feng,dlg.m_fmat,dlg.m_fcom);m_stuObArray.Add(pStudent);//对象数组集合类对象,将数据加到数组中
SetModifiedFlag();//设置文档更改标志。表示Doc类已经修改了
//比如说需要保存的时候,可以判断是否修改,没修改,就不需要保存等等
UpdateAllViews(NULL);//更新视图
}}上一页下一页返回4.4使用简单数组集合类说明:UpdateAllViews()函数是CDocument的成员函数,因为视图窗口显示的一般是文档数据的可视化表示,所以当文档中的数据被修改后,需要通知所有与之相关的视图,重新绘制画面。(7)完善代码,包括以下几步。1)在CMyDoc类中添加成员函数和变量(打开ClassView页面,右击添加):CStudent*GetStudentAt(intnIndex);//判索引号用intGetAllRecNum(void);//获得索引号上一页下一页返回4.4使用简单数组集合类2)在CMyDoc.cpp中,添加如上声明的GetStudentAt函数实现代码
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026三年级诗词启智润心渗透课件
- 2026年《澳大利亚》测试题及答案
- 2026年html基本标签测试题及答案
- 2026年护理实践能力测试题及答案
- 2026年电工初级测试题及答案
- 2026年ps 图层测试题及答案
- 2026年处理公文测试题及答案
- 2026年机械加工精度测试题及答案
- 2026年合作框架修改建议函5篇
- 建筑工地安全教育培训课程设计指南
- wst 885-2026 临床检验结果互认的基本技术条件及质量指标课件
- 中水管道施工安全措施方案
- 《无人机系统概论》期末考试试卷及答案
- 2026年重庆市中考物理试卷(含答案及解析 )
- 切花玫瑰采后分级包装标准
- StarterUnit1SectionA课件人教版七年级英语上册
- 2025年江西省公安厅招聘警务辅助人员笔试真题(附答案)
- 重症医学科护理文书书写规范
- 2026年上海市高三语文二模作文题目审题立意解析(二)含素材
- 护理带教中的冲突管理技巧
- 果园绿肥种植实施方案
评论
0/150
提交评论