现在的位置: 首页 > 综合 > 正文

详解MFC框架窗口、文档和视图

2013年01月19日 ⁄ 综合 ⁄ 共 29108字 ⁄ 字号 评论关闭

 

尽管窗口、文档和视图是MFC的基础,但可能也是最不容易理解的部分,因为其概念比传统编程所需要的Windows函数更强一些,因此,须在本章做进一步详细讨论框架窗口、文档和视图的方法和技巧。

6.1框架窗口

   分两类:一是应用程序主窗口,另一类是文档窗口。

6.1.1主窗口和文档窗口

主窗口(或称主框架窗口)是应用程序直接放在桌面(DeskTop)上的那个窗

口,每个应用程序只能有一个窗口,主窗口的标题栏上往往显示应用程序的名称。

主窗口类的源文件名是MainFrm.hMainFrm.cpp,其类名是CMainFrame

单文档SDI程序主窗口类是从CFrameWnd派生来的。

多文档MDI程序主窗口类是从CMDIFrameWnd派生的。

如果应用程序中还有工具栏(CToolBar)状态栏(CStatusBar),那么在CMainFrame类还含有表示工具栏和状态栏的成员变量m_wndToolBar和m_wndStatusBar,并在CMainFrame的OnCreate函数中进行初始化。

文档窗口对于SDI程序来说,它和主窗口是一致的,即主窗口就是文档窗口;对于MDI程序,文档窗口是主窗口的子窗口。见书244页图6.1所示。文档窗口一般都有相应的可见边框,他的客户区(初了窗口标题栏、边框外的区域)是由相应的视图来构成的,可以说视图是文档窗口内的子窗口。文档窗口时刻跟踪当前处于活动状态的视图的变化,并将用户或系统产生的命令消息传递给当前活动视图。而主窗口负责管理各个用户交互对象(包括菜单、工具栏、状态栏以及加速键)并根据用户操作相应地创建或更新文档窗口及其视图。在MDI应用程序中,MFC 
AppWizard创建的文档子窗口类的源代码文件是ChildFrm.h和ChildFrm.cpp,其类名是CChildFrame,它是从CMDIChildWnd派生的。

6.1.2窗口风格的设置

窗口的风格决定了窗口的外观及功能,用户通过风格的设置增加或减少窗口中所包含的功能,这些功能一般都是由系统内部定义的,不需要用户去编程实现。   

窗口风格可以通过MFC AppWizard来设置,也可以在主窗口或文档窗口类的

PreCreateWindow函数中修改CREATESTRUCT结构,或是可以调用CWnd类的

成员函数ModifyStyle和ModifyStyleEx来更改。

1、窗口风格

通常以WS_为前缀和扩展以WS_EX_为前缀两种形式;这两种形式的窗口风格可在函数CWnd::Create(只能指定窗口的一般风格)或CWnd::CreateEx(可同时支持以上两种风格),对于控件和对话框这样的窗口来说,它们的窗口风格可直接通过其属性对话框来设置。常见的一般窗口风格如下所示:(书245页表6.1)

WS_BORDER         窗口含有边框

WS_CAPTION        窗口含有标题栏(它意味着还具有WS_BORDER风格)

                     但它不能和WS_DLGFRAME组合

WS_CHILD           创建子窗口,它不能和WS_POPUP组合

WS_CLIPCHILDREN   在父窗口范围内裁剪子窗口,它通常在父窗口创建时指定

WS_CLIPSIBLINGS    裁剪相邻子窗口,也就是说,具有此风格的子窗口和其他

子窗口重叠的部分被裁剪,它只和WS_CHILD组合

WS_DISABLED        窗口最初时是禁用的

WS_DLGFRAME       窗口含有双边框,但没有标题

WS_GROUP           此风格被控件组中第1个控件窗口指定。用户可在控件组

的第1个和最后1个控件中用方向键来选择

WS_HSCROLL          窗口最初时处于最大化

WS_MAXIMIZEBOX     在窗口的标题栏上含有”最大化”按钮

WS_MINIMIZE          窗口最初处于最小化,他只和WS_OVERLAPPED组和

WS_MINIMIZEBOX      在窗口的标题栏上含有”最小化”按钮

WS_OVERLAPPED       创建覆盖窗口,一个覆盖窗口通常有一个标题和边框

WS_OVERLAPPEDWINDOW   创建一含有WS_OVERLAPPED、WS_CAPTION、

WS_SYSMENU、WS_THICKFRAME、

WS_MINIMIZEBOX和WS_MAXIMIZEBOX

风格的覆盖窗口

WS_POPUP              创建一弹出窗口,它不能和WS_CHILD组合,只能用

                        CreateWx函数指定

WS_POPUPWINDOW  创建一含有WS_BORDER、WS_POPUP和WS_SYSMENU 

                   风格的弹出窗口。当WS_CAPTION和WS_POPUPWINDOW

                   风格组合时才能使系统菜单可见。

WS_SYSMENU      窗口的标题栏上含有系统菜单框,它仅用于含有标题的窗口

WS_TABSTOP       用户可以用于TAB键 选择控件组中的下一个控件

WS_THICKFRAME  窗口含有边框,并可调整窗口的大小

WS_VISIBLE        窗口最初是可见的

WS_VSCROLL       窗口含有垂直滚动条

除了这些风格外,框架窗口还有以下3个自己的风格。他们都可以在PreCreateWindow重载函数中指定。

(1)FWS_ADDTOTITLE风格:指定一个文档名添加到框架窗口标题中,如书244页图6.1的“Ex_MDI---Ex_MDI1”的Ex_MDI1是文档名。若单文档应用程序,默认的文档名是”无标题”。

(2)FWS_PREFIXTITLE风格:使得框架窗口标题中的文档名显示在应用程序名之前。例如,若未指定该风格时的窗口标题为”Ex_MDI_Ex_MDI1”,当指定该风格后就变成了”Ex_MDI1_Ex_MDI”。

(3)FWS_SNAPTOBARS风格:用于调整窗口的大小,使它刚好包含了框架窗口中的控制栏(如工具栏)

2、用MFC AppWizard设置

MFC AppWizard有一个Advanced(高级)按钮(在创建单或多文档程序时的第4步中),允许用户指定有关SDI和MDI框架窗口的属性。见书246页图6.2所示为”Advanced Options”对话框的Window Styles页面,其中的含义如下:(246页)

(但在该对话框中,用户只能设定少数几种窗口风格)

Use split window(应用拆分窗口)   选中时,将程序的文档窗口创建成”切分”*(或称

拆分)窗口

Thick frame(厚框)             选中时,设置窗口风格WS_THICKFRAME

Minimize box(最小化框符)        选中时,设置窗口风格WS_MINIMIZEBOX

Maximize box(最大化框符)        选中时,设置窗口风格WS_MAXIMIZEBOX

System menu(系统菜单)           选中时,设置窗口风格WS_SYSMENU

Minimized(最小化)               选中时,设置窗口风格WS_MINIMIZE

Maximized(最大化)               选中时,设置窗口风格WS_MAXIMIZE

3、修改CREATESTRUCT结构

在窗口创建之前,系统自动调用PreCreateWindow虚函数。在MFC AppWizard

创建SDI/MDI应用程序结构时,MFC已为主窗口或文档窗口类自动重载了该虚函数。我们可以在此函数中通过修改CREATESTRUCT结构来设置窗口的绝大多数风格。

例:在SDI程序中,框架窗口默认的风格是WS_OVERLAPPEDWINDOW和FWS_ADDTOTITLE的组合,更改其风格。

(1)建一个单文档的应用程序

(2)在主框架程序MainFrm.cpp中找到PreCreateWindow虚函数,并加代码:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

cs.style&=~WS_MAXIMIZEBOX;//新窗口不带有[最大化]按钮

cs.cy=::GetSystemMetrics(SM_CYSCREEN)/3;

  cs.cx=::GetSystemMetrics(SM_CYSCREEN)/3;//将窗口大小设置为1/3屏幕大

//小,并居中。

cs.y=((cs.cy*3)-cs.cy)/2;

cs.x=((cs.cx*3)-cs.cx)/2;

//调用基类的PreCreateWindow(cs)函数

   return CFrameWnd::PreCreateWindow(cs);

//     if( !CFrameWnd::PreCreateWindow(cs) )

//            return FALSE;

       // TODO: Modify the Window class or styles here by modifying

       //  the CREATESTRUCT cs

//     return TRUE;

}

(3)编译运行,见出现的窗口是原来的1/3。

(4)建一个多文档MDI应用程序(先运行一下试一试窗口最大化按钮)

(5)在子文档窗口ChildFrm程序中找到PreCreateWindow虚函数,并加代码:

(创建不含有[最大化]按钮的子窗口)

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)

{

       // TODO: Modify the Window class or styles here by modifying

       //  the CREATESTRUCT cs

    cs.style&=~WS_MAXIMIZEBOX;//创建不含有[最大化]按钮的子窗口

       return CFrameWnd::PreCreateWindow(cs);

//     if( !CMDIChildWnd::PreCreateWindow(cs) )

//            return FALSE;

//     return TRUE;

}

结果是:不含有[最大化]按钮的子窗口

代码中,前面有“::”作用域符号的函数是指全局函数。

代码“cs.style &=~WS_MAXIMIZEBOX;”中的“~”是按位取“反”运算符,它将WS_MAXIMIZEBOX的值按位取反后,再和cs.style值按位“与”,其结果是将cs.style值中的WS_MAXIMIZEBOX标志位清零。

4、使用ModifyStyle和ModifyStyleEx

CWnd类中的成员函数ModifyStyle和ModifyStyleEx也可用来更改窗口的风格,其中ModifyStyleEx还可更改窗口的扩展风格。这两个函数具有相同的参数:

BOOL ModifyXXXX(DWORD dwRemove,DWORD dwAdd,UINT nFlags=0);

参数:dwRemove用来指定需要删除的风格

      dwAdd    用来指定需要增加的风格

      nFlags    表示SetWindowPos的标志,0(默认)表示更改风格的同时不

                调用SetWindowPos函数

由于框架窗口在创建时不能直接设定其扩展风格,因此只能通过调用ModifyStyle函数来进行。

例:在多文档(MDI)的子文档窗口增加水平和垂直滚动条(书247页例,248

页图6.3)

(1)用MFC AppWizard创建一个多文档应用程序(或用上个多文档应用程序),

名为:“多文档”

(2)用ClassWizard为子文档窗口类CChildFrame添加OnCreateClient处理消息:

ViewàClassWizardàCChildFrameàOnCreateClientàAdd FunctionàEdit Code

并加如下代码:

BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs,,CCreateContext* pContext)

{

      // TODO: Add your specialized code here and/or call the base class

      ModifyStyle(0,WS_VSCROLL,0);//垂直滚动轴

    ModifyStyle(0,WS_HSCROLL,0);//水平滚动轴

      return CMDIChildWnd::OnCreateClient(lpcs, pContext);

}

(3)编译并运行(书上只有一个滚动轴)

6.1.3窗口状态的改变

    MFC AppWizard为每个窗口设置了相应的大小和位置,但总有的默认窗口

不另人满意,这时就需要进行适当的调整。

1、用ShowWindow改变窗口的显示状态

   当应用程序运行时,Windows会自动调用应用程序框架内部的WinMain函数,并自动查找该应用程序类的全局变量theApp,然后自动调用用户应用程序类的虚函数InitInstance,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作,如一个单文档应用程序的应用程序文件中的程序名.cpp中的InitInstance()函数中的代码:

      m_pMainWnd->ShowWindow(SW_SHOW);

      m_pMainWnd->UpdateWindow();

参数:SW_SHOW是用当前的大小和位置激活并显示窗口。

      m_pMainWnd是主框架窗口指针变量,ShowWindow是CWnd类的成员函

数,用于按指定的参数显示窗口,该参数的值如下:

SW_HIDE      隐藏此窗口并将激活状态移交给其他窗口

SW_MINIMIZE 将窗口最小化并激活系统中的顶层窗口

SW_RESTORE  激活并显示窗口。若窗口是最小或最大状态时,则恢复到原来的

大小和位置

SW_SHOW     用当前的大小和位置激活并显示窗口

SW_SHOWMAXIMIZED 激活窗口并使之最大化

SW_SHOWMINIMIZED  激活窗口并使之最小化

SW_SHOWMINNOACTIVE窗口显示成为一个图标并保留其激活状态(即原来是

激活的,仍然是激活)

SW_SHOWNA   用当前状态显示窗口

SW_SHOWNOACTIVATE 用最近的大小和位置状态显示窗口并保留其激活状态

SW_SHOWNORMAL 激活并显示窗口   

通过指定ShowWindow函数的参数值可以改变窗口显示状态,例如:将窗口的初始状态设置为”最小化”,可以这样写:

m_pMainWnd->ShowWindow(SW_SHOWMINIMIZED);

m_pMainWnd->UpdateWindow();

由于用户应用程序类继承了基类CWinApp的特性,因此也可在用户应用程序类

CWinApp中使用公有型(public)成员变量m_nCmdShow,通过对其进行赋值,同样能达到效果。

例如:在上个“多文档”项目的应用程序的InitInstance()函数中这样写:

m_nCmdShow=SW_SHOWMAXIMIZED;//激活窗口并使之最大化,书是最小

pMainFrame->ShowWindow(m_nCmdShow);

pMainFrame->UpdateWindow();

     return TRUE;

2、用SetWindowPos或MoveWindow改变窗口的大小和位置

CWnd中的SetWindowPos是一个非常有用的函数,它不仅可以改变窗口的大小、位置,而且还可以改变所有窗口在堆栈排列的次序(Z次序),这个次序是根据它们在屏幕出现的先后来确定的。

BOOL SetWindowPos(const CWnd *pWndInsertAfter,int x,int y,int cx,int cy,UINT

nFlags);

参数:pWndInsertAfter表示窗口对象指针,它可以预定义下列窗口对象的地址:

      wndBottom将窗口放置在Z次序中的底层

      wndTop将窗口放置在Z次序中的顶层

      wndTopMost设置最顶层窗口

      wndNoTopMost将窗口放置在所有最顶层的后面,若此窗口不是最顶窗

口,则此标志无效。

     x和y表示窗口新的左上角坐标,cx和cy分别表示窗口的宽度和高度,

     nFlags表示窗口新的大小和位置方式,如下说明(书250页表6.4)

常用nFlags值及其含义:

SWP_HIDEWINDOW隐藏窗口

SWP_NOACTIVATE 不激活窗口。如该标志没有被指定,则依赖pWndInsertAfter参数

SWP_NOMOVE        不改变当前的窗口位置(忽略x和y参数)

SWP_NOOWNERZORDER不改变父窗口的Z次序

SWP_NOREDRAW      不重新绘制窗口

SWP_NOSIZE           不改变当前的窗口大小(忽略cx和cy参数)

SWP_NOZORDER       不改变当前的窗口Z次序(忽略pWndInsertAfter参数)

SWP_SHOWWINDOW    显示窗口

函数CWnd::MoveWindow也可用来改变窗口的大小和位置,与SetWindowPos函数不同的是,用户必须在MoveWindow函数中指定窗口的大小。

void MoveWindow(int x,int y,int nWidth,int nHeight,BOOL bRepaint=TRUE);

void MoveWindow(LPCRECT lpRect,BOOL bRepaint=TRUE);

参数:x,y表示窗口新的左上角坐标

nWidth,nHeight表示窗口新的宽度和高度

bRepaint用于指定窗口是否重绘

lpRect表示窗口新的大小和位置

例:用上面两个函数,把主窗口移到屏幕的(100,100)处。

(1)建一个单文档的应用程序

(2)打开:文件名.cpp应用程序,找到BOOL CMyApp::InitInstance()函数,并添下列代码:

     m_pMainWnd->SetWindowPos(NULL,100,100,0,0,SWP_NOSIZE|

              SWP_NOZORDER);//不改变当前的窗口大小和窗口Z次序,

        CRect rcWindow; //见上面说明(书250页表6.4)

        m_pMainWnd->GetWindowRect(rcWindow);

        m_pMainWnd->MoveWindow(100,100,rcWindow.Width(),

              rcWindow.Height(),TRUE);//改变窗口位置

//AfxGetMainWnd()->CenterWindow();//将主框架窗口居中.

//AfxGetMainWnd()->CenterWindow(CWnd::GetDesktopWindow());

       //将窗口置于屏幕中央

       //return TRUE;

   当然,改变窗口的大小和位置的CWnd成员函数还不止以上两个,例如:

CenterWindow(CWND::GetDesktopWindow())函数是使窗口居于父窗口(屏幕)中央。

AfxGetMainWnd()->CenterWindow();//将主框架窗口居中。见上面已加上的两个函数。

6. 2文档模板

   用MFC AppWizard创建的单文档(SDI)或多文档(MDI)应用程序均包含应用程序类、文档类、视图类和框架窗口类,这些类是通过文档模板有机地联系在一起的。

6.2.1文档模板类

   文档应用程序框架结构是在程序运行一开始构造的。

1、在单文档应用程序的应用程序类InitInstance()函数中,可以看到这样的代码:

BOOL CMyApp::InitInstance()

{

CSingleDocTemplate *pDocTemplate;

pDocTemplate = new CSingleDocTemplate

(

IDR_MAINFRAME,           //资源ID

RUNTIME_CLASS(CMyDoc),   //(以中文名字命名的项目名的项目)文档类

RUNTIME_CLASS(CMainFrame),// 主框架窗口类

RUNTIME_CLASS(CMyView)  //(以中文名字命名的项目名的项目)视图类

);

AddDocTemplate(pDocTemplate);

…..

return TRUE;

}

代码中

pDocTemplate      是类CSingleDocTemplate的指针对象。

CSingleDocTemplate是一个单文档模板类,他的构造函数中有4个参数,分别表示

菜单和加速键等的资源ID号以及3个由宏RUNTIME_CLASS

指定的运行时类对象。

AddDocTemplate   是类CWinApp的一个成员函数,当调用了该函数后,就建立

了应用程序类、文档类、视图类和主框架类之间的相互联系。

 

2、在多文档应用程序的应用程序类InitInstance()函数中,同样可以看到这样的代码:

   BOOL CMyApp::InitInstance()

{  CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate

(

 IDR_MYTYPE,    //(以中文名字命名的项目名的项目)资源ID号

 RUNTIME_CLASS(CMyDoc),//(以中文名字命名的项目名的项目)文档类

 RUNTIME_CLASS(CChildFrame),// MDI文档窗口类

 RUNTIME_CLASS(CMyView)//(以中文名字命名的项目名的项目)视图类

);

AddDocTemplate(pDocTemplate);

// create main MDI Frame window创建主框架窗口

CMainFrame* pMainFrame = new CMainFrame;

if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

        return FALSE;

m_pMainWnd = pMainFrame;

……

Return TRUE;

}

由于多文档模板是用于建立资源、文档类、视图类和子框架窗口(文档窗口)类之间的联系的,因而多文档的主框架窗口需要额外的代码来创建。代码中:

LoadFrame()是CFrameWnd类成员函数,用于加载与主框架窗口相关的菜单、加速键、图标等资源。

说明:多文档主框架窗口的创建应在多文档模板创建后进行,以便MFC程序框架将多文档模板和多文档主框架窗口建立联系。

   应用程序类对象在模板被创建之前就已经存在,但此时文档、视图及框架对象

还没有被创建。程序运行后,程序框架会动态地创建这些对象。这一动态创建的

过程包含了对C++语言的非常复杂的运用。

   通过在用户文档类、视图类以及主框架类的定义(.h)及实现(.cpp)过程中

使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,MFC库可以动

态地创建相应的类对象。

   这种称为运行时类的动态创建机制,简化了程序员编程工作,拓展了“类”的

功能,是MFC程序框架结构的重要特性。

6.2.2 文档模板字串资源 

   在MFC AppWizard创建的应用程序资源中,许多资源标识符都是IDR_MAINFRAME

意味着这些具有同名标识的资源将被框架自动加载到应用程序中。其中String Table

(字符串)资源列表中也有一个IDR_MAINFRAME项,他是用于标识文档类型、标题等内容的,称为“文档模板字串资源”。其内容如下(以单文档“模板”为例)

模板\n\nMy\n\n\nMy.Document\nMy Document

可以看出,IDR_MAINFRAME所标识的字符串被分成了一些以“\n”结尾的子串,这些子串共有7段,每段都有特定的用途,如:(252页表6.5)

  文档模板字符串的含义:

IDR_MAINFRAME的子串  串号   用途

模板\n                     0   应用程序窗口标题即窗口标题栏:无标题--模板

\n                         1   文档根名。对多文档应用程序来说,若在文档

窗口标题上显示“Sheet1”,则其中的Sheet就

是文档根名,若该子串为空,则文档名为默认

的“无标题”

My\n                      2    新建文档的类型名。若有多个文档类型,则这

个名称将出现在“新建”对话框中

\n                         3   通用对话框的文件过滤器正文

\n                         4   通用对话框的文件扩展名

My.Document\n             5    在注册表中登记的文档类型标识

My Document               6   在注册表中登记的文档类型名称

但对于MDI(多文档)来说,上述的字串分别由IDR_MAINFRAME和IDR_MYTYPE

(项目名为:中文名:“字串资源“)2项组成;其中,IDR_MAINFRAME表示窗口标题,而IDR_MYTYPE表示后6项内容。它们的内容如下:

IDR_MAINFRAME:  My(中文项目名)

IDR_MYTYPE:      \nMy\n\n\nMy.Document\nMy Document

   实际上,文档模板字串资源内容既可直接通过字串资源编辑器进行修改,也可以在文档应用程序中创建向导的第4步中,通过“Advanced Options”对话框中的

“Document Template String”页面来指定,如书235页图6.4所示(名字是Ex_SDI)。

图中的数字表示该项的含义与上表中对应串号的含义相同。

6.2.3使用多个文档类型

   在MFC AppWizard创建的应用程序中,通常只有一种文档类型。但有时,用户也需要另一种文档类型。例如Visual C++6.0本身既要处理文本文件,也要处理MFC资源,至少要有2种文档类型。

   多种文档类型的应用程序中一般有多个文档模板、多个文档类以及与之紧密相联的多个视图类。

   当用户选择“文件”菜单的“新建”命令时,应用框架将弹出含有文档类型列表的对话框,允许用户自己选择所需的文档类型。下面是实现在多文档应用应用程序中使用多种文档类型。

例:使用多个文档

(1)用MFC AppWizard创建一个多文档应用程序(Step1第一步就单击[Finish]

即可)

(2)(3)“多个文档类型实例”

(4)\nPicture\nMyDenmo图片\n图片文件(*.bmp)\n.bmp\nMyDemo.Document\n

     My Document

(5)在“字符串属性”对话框中将ID设为IDR_OTHERTYPE=130,标题内容设

为:\nTxt\nMyDemo 文本\n 文本文件(*.txt,*.cpp,*.h)\n.txt;*.cpp,*.h\n

MyDemo.Document\n MyDocument

(6)COtherDoc

(7)COtherDoc

(8)pDocTemplate=new CMultiDocTemplate(

                     IDR_OTHERTYPE,

                     RUNTIME_CLASS(COtherDoc),//指定新的文档类

                  RUNTIME_CLASS(CChildFrame),

                     RUNTIME_CLASS(COtherView));//指定新的视图类

       AddDocTemplate(pDocTemplate);

(9) #include "OtherDoc.h"

#include "OtherView.h"

(10)运行结果见书

说明:A、如果在该例程序中再添加图标和菜单资源,并使资源标识设为IDR_OTHERTYPE

        则当创建“MyDemo文本”文档类型后,程序会自动使用新的图标和菜单资源。

      B、单文档应用程序也可以有多个文档类型,它的实现方法与多文档类似,也是通过添

加文档模板来实现的,只不过每次只能在文档窗口(视图)中显示一个文档。

6.3文档序列化

   用户处理的数据往往需要存盘作为永久备份。将文档类中的数据成员变量的值保存在磁盘文件中,或者将存储的文档文件中的数据读取到相应的成员变量中,这个过程称为序列化(Serialize)。

6.3.1文档序列化过程

    在使用MFC程序结构进行文档序列化之前,我们先了解对文档不同操作的具体程序运行过程。

1创建空文档(256页有说明)

应用程序类的InitInstance函数在调用了AddDocTemplate函数之后,会通

过CWinApp::ProcessShellCommand间接调用CWinApp的另一个非常有用的成员函数OnFileNew,并依次完成文档各个步骤的创建。…..

2)打开文档

当MFC AppWizard创建应用程序时,它会自动将“文件(File)”选单中的“打开(Open)”命令(ID号为ID_FILE_OPEN)映射到CWinApp的OnFileOpen

成员函数。这一结果可以从应用类(.cpp)的消息入口处得到验证:见书257页

除了使用“文件(File)”à“打开(Open)”选项外,用户也可以选择最近使用过的文件列表来打开相应的文档。在应用程序的运行过程中,系统会记录下4个(默认)最近使用过的文件,并将文件名保存在Windows的注册表中。当每次启动应用程序时,应用程序都会在“文件(File)”选单中显示最近使用过的文件名称。

需要说明的是:MFC为我们重载了Serialize函数,不必使用CFile类就可以完成相应的文档操作。(写磁盘和串口访问这样这样的任务)

例:使用Serialize函数,读取文档中的数据(附加)

(1)打开前面的“多文档”应用程序或重新建一个单或多文档应用程序。

(2)加成员变量

工作区ClassViewà右键对准CMyDocà单击àAdd Member Variableà右键

   单击à出现对话框àVariable Type处写:char àVariable Name处写:m_ch[300]

   在文档类中加了一个成员变量。

(3)在文档类即多文档Doc.cpp中找到:void CMyDoc::Serialize(CArchive& ar)

     函数并加代码:

 void CMyDoc::Serialize(CArchive& ar)//CArchive的对象是ar(引用)

{

       if (ar.IsStoring())

       {

              // TODO: add storing code here

       }

       else

       {

              // TODO: add loading code here

              for(int i=0;i<sizeof(m_ch);i++)

ar>>m_ch[i];

              CString str;

              str.Format(_T("%s"),m_ch);

              AfxMessageBox(str);

       }

}

这样,当我们通过选单的“打开(Open)”成功打开(用*.*)一个文件时,将弹出一个对话框,显示出该文件的前300个字符的内容。

3)保存文档(257页)

当MFC AppWizard创建应用程序时,它会自动将“文件(File)”选单中的“保存(Save)”命令与文档类CDocument的OnFileSave函数在内部关联起来,但用户在程序框架中看不到相应的代码。OnFileSave函数还会进一步完成下列工作。

(1)    弹出通用文件“保存”对话框,让用户提供一个文件名

(2)    调用文档对象的CDocument::OnSaveDocument虚函数,接着又自动调用Serialize函数,将CArchive对象的内容保存在文档中。

说明:

A、只有在保存文档之前还没有存过盘(即没有文件名)或读取的文档是“只读”

的,OnFileSave函数才会弹出通用“保存”对话框,否则只执行第2步。

B、“文件(File)”菜单中还有一个“另存为(Save  As)”命令,它是与文档类

CDocument的OnFileSaveAs函数相关联。不管文档有没有保存过,OnFileSaveAs

都会执行上述2个步骤。

C、上述文档存盘的必要操作都是由系统自动完成的。

除了系统自动将文档存盘外,用户可以用ClassWizard来重载CDocument::OnSaveDocument函数,并可在Serialize()函数体的ar.IsStorinr()为“真”的条件语句处添加代码,从而在文档中保存用户自己的数据。

例:使用Serialize函数,保存文档中的数据(附加)

(1)打开前面的“多文档”应用程序或重新建一个单或多文档应用程序。

(2)如果是重建的应用程序,也按前面第二步在文档类CMyDoc中加一个成员变量char m_ch[300]。

(3)在文档类即多文档Doc.cpp中找到:void CMyDoc::Serialize(CArchive& ar)

     函数并加代码:

void CMyDoc::Serialize(CArchive& ar)// CArchive的对象是ar(引用)

{

       if (ar.IsStoring())

       {

              // TODO: add storing code here

              for(int i=0;i<sizeof(m_ch);i++)

                     ar<<m_ch[i];

       }

       else

       {

              // TODO: add loading code here

              for(int i=0;i<sizeof(m_ch);i++) ar>>m_ch[i];

              CString str;

              str.Format(_T("%s"),m_ch);

              AfxMessageBox(str);

       }

}

上述代码的结果是当保存的文件名成功指定后,该文件保存m_ch中的300字符。若文件中有其它数据,则数据被自动清除。

(可用Word随便建个文件名“小山”,先打开一个文件,成功后,再用保存,将刚才打开的文件另存为“小山”,退出再运行之后打开小山,就是刚才打开的文件内容)

4、关闭文档

当用户试图关闭文档(或退出应用程序)时,应用程序会根据用户对文档的修改与否来进一步完成下列任务。

(1)若文档内容已被修改,则弹出一个“消息”对话框,询问用户是否需要将文档保存。当用户选择“是”,则应用程序执行OnFileSave过程。

(2)调用CDocument::OnCloseDocument虚函数,关闭所有与该文档相关联的文档窗口及相应的试图,调用文档类CDocument的DeleteContents清除文档数据。

MFC应用程序通过CDocument的protected类型成员变量m_bModified的逻辑值来判断用户是否对文档进行修改,如果m_bModified为“真”,则表示文档被修改。对于用户来说,可以通过CDocument的SetModifiedFlag成员函数来设置或通过IsModified成员函数来访问m_bModified的逻辑值。当文档创建、从磁盘中读出以及文档存盘时,文档的这个标记就被置为FLASE(假);而当文档数据被修改时,用户必须使用SetModifiedFlag函数将该标记置为TRUE。这样,当用户关闭文档时,应用程序才会显示询问消息对话框。(多文档与单文档类似)。

6.3.2文档序列化操作

   打开和保存文档,系统都会自动调用Serialize函数。而MFC AppWizard在创建文档应用程序框架时已在文档类中重载了Serialize函数,通过在该函数中加代码可达到实现数据序列化的目的。例如:上面的“打开”“保存”都在Serialize里加了代码。

void CMyDoc::Serialize(CArchive& ar)

{

       if (ar.IsStoring())

       {

              // TODO: add storing code here

       }

       else

       {

              // TODO: add loading code here

       }

}

    代码中,Serialize函数的参数ar是一个CArchive类引用变量。通过判断ar.IsStoring

的结果是“真”还是“假”就可决定向文档写或读数据。

   CArchive(归档)类提供对文件数据进行缓存,同时还保存一个内部标记,用于标识文档是存入(写盘)还是载入(读盘)。每次只能有一个活动的存档与ar相连。通过CArchive类可以简化文件操作,提供“<<”和“>>”运算符,用于向文件写入简单的数据类型以及从文件中读取它们。下面是CArchive支持的常用数据类型:

类   型          描    述            类   型           描    述

BYTE          8位无符号整型         WORD          16位无符号整型

LONG         32位带符号整型         DWORD        32位无符号整型

float            单精度浮点             double          双精度浮点

int              带符号整型             short           带符号短整型

char             字符型                 unsigned        无符号整型

除了“<<”和“>>”运算符外,CArchive类还提供成员函数ReadString和

WriteString用于从一个文件对象中读、写一行文本,它们的原型如下:

BOOL  ReadString(CString &rString);

LPTSTR ReadString(LPTSTR lpsz,UINT nMax);

void WriteString(LPCTSTR lpsz);

其中,lpsz用于指定读或写的文本内容,nMax用于指定可以读出的最大字符个数。

注意:当向一个文件写一行字符串时,字符’\0’和’\n’都不会写到文件中。         

CArchive(归档)类:CArchive类没有基类,它提供了串行化对象从文件中读

写的类型安全缓冲机制,可以把CArchive对象想象成一种二进制流,就象输入/输出流一样可以顺序高效的处理二进制对象数据。使用CArchive对象之前,必须先创建一个CFile对象,同时保证CArchive对象的读写标志设置和文件打开方式相一致。对于一个CArchive对象,可以进行存储操作,也可以读取,但不能2者同时进行。

(1)CArchive类操作符”<<”和”>>”

    CArchive对象的引用,类似于流的输入/输出(cin/cout),可以使用一系列的

操作符以简化程序,例如:

int x;        

CString y;    

……

ar<<x<<y;

ar是CArchive类的对象,见Serialize(CArchive &ar)函数

2、CArchive类的成员函数:

Read()、Write()  读写指定字节数的缓冲区内容

ReadString()、WriteString()读写一行文本

ReadObject()、WriteObject()调用一个对象的Serialize函数来读或写

ReadClass()、WriteClass()读写一个CRuntimeClass指明对象

IsLoading()、IsStoring()判断当前读写状态

3、Serialize()函数 

在”MFC AppWizard”自动生成的程序框架中,文件的串行化操作都是由CDocument派生类成员函数Serialize()完成的。它的结构如下:

void CMySdiDoc::Serialize(CArchive &ar)

{  if(ar.IsStoring())

    {

       //TODO:….

}

else

{

   //TODO……

 }

}

在这个成员函数中,使用CArchive对象完成具体的操作。参数中传递进来的CArchive引用对象(ar),是由MFC程序框架根据用户输入需要执行串行化操作时创建的,它包含了所需要的文件的信息,使用它可以对各种CArchive类支持的数据格式进行读写、调用其它CObject派生类对象的串行化函数等。

   “MFC AppWizard”自动生成的代码已经完成的工作是:用户选择”打开”、”保存”或”另存为”等命令时,程序框架创建这个文件的CFile对象,将它关联到新创建的CArchive对象上,并设置CArchive对象的”Store”或”Load”标志,用这个对象来调用CDocument派生类的Serialize()成员函数。在Serialize()函数完成读写操作返回后,自动删除Serialize()函数和CFile对象。祥见最后的例题《学生档案管理程序》

例:一个简单的文档序列化示例(259页)

(1)(2)

(3)

char m_chArchive[100];//读、写数据时使用

CString m_strArchive; //读、写数据时使用

BOOL m_bIsMyDoc; //用于判断文档

(4)m_bIsMyDoc=FALSE;

(5)

     strcpy(m_chArchive,"&这是一个用于测试文档的内容!");

        m_strArchive="这是一行文本!";

        m_bIsMyDoc=TRUE;

(6)

     if(m_bIsMyDoc)//是自己的文档

              {

                     for(int i=0;i<sizeof(m_chArchive);i++)

                            ar<<m_chArchive[i];

                     ar.WriteString(m_strArchive);

              }

              else

           AfxMessageBox("数据无法保存!");

}

else  //此条原程序上有的

{

   ar>>m_chArchive[0];//读取文档首字符

              if(m_chArchive[0]=='&')//是自己的文档

              {

                     for(int i=1;i<sizeof(m_chArchive);i++)

                            ar>>m_chArchive[i];

                     ar.ReadString(m_strArchive);

                     CString str;

                     str.Format("%s%s",m_chArchive,m_strArchive);

                     AfxMessageBox(str);

                     m_bIsMyDoc=TRUE;

              }

              else//不是自己的文档

              {

                     m_bIsMyDoc=FALSE;

                     AfxMessageBox("打开的文档无效!");

              }

  }//原有

}//原有

(7)不做,与第二步重复了

(8)结果见260页图6.12

6.3.3使用简单数据组集合类

   上述文档的读、写是通过变量来存取文档数据的,实际上还可以使用MFC提供的集合类来进行操作。这样不仅可以有利于优化数据结构,简化数据的序列化,而且保证数据类型的安全性。

MFC提供的集合类可分为3类:

(1)链表集合类(List)   (2)数组集合类(Array)    (3)映射集合类(Map)

在这讨论的是简单数组类,它包括:

CObArray(对象数组集合类)

CByteArray(BYTE数组集合类)(8位无符号整型)

CDWordArray(DWORD数组集合类)(32位无符号整型)

CPtrArray(指针数组集合类)

CStringArray(字符串数组集合类)

CUIntArray(UINT数组集合类)(Windows所用的数据类型,unsigned int  32位无符号整数)

CWordArray(WORD数组集合类)(16位无符号整型)

简单数组集合类是一个大小动态可变的数组,数组中的元素可用下标运算符“[]”

来访问(从0开始),设置或获取元素数据。若要设置超过数组当前个数的元素的值,可以指定是否使数组自动扩展。当数组不需扩展时,访问数组集合类的速度与访问标准C++中的数组的速度同样快,以下的基本操作对所有的简单数组集合类都适用。

1、简单数组集合类的构造及元素的添加

   对简单数组集合类构造的方法都是一样的,均是使用各自的构造函数,它们的原型如下:

CByteArray       CByteArray()            //  BYTE数组集合类

CDWordArray     CDWordArray()          //  DWORD数组集合类

CObArray        CObArray()              //  对象数组集合类 

CPtrArray        CPtrArray()              //  指针数组集合类

CStringArray     CStringArray()            //  字符串数组集合类

CUIntArray      CUIntArray()             //  UINT数组集合类

CWordArray      CWordArray()            //  WORD数组集合类

下面的代码说明了简单数组集合类的2种构造方法:

CObArray  array;                    //使用默认的内存块大小

CObArray *pArray=new CObArray;     //使用堆内存中的默认的内存块大小

为了有效使用内存,在使用简单数组集合类之前最好调用成员函数SetSize设置此数组的大小,与其对应的函数是GetSize,用于返回数组的大小。原型如下:

void SetSize(int nNewSize,int nGrowBy=-1);

int GetSize()const;

其中,参数nNewSize用于指定新的元素的数目(必须大小或等于0)。nGrowBy表示当数组需要扩展时允许可添加的最少元素数目,默认时为自动扩展。

向简单数组集合类添加一个元素,可使用成员函数Add和Append,它们的原型如下:

int Add(CObject *newElement);

int Append(const CObArray&src);

其中,Add函数是向数组的末尾添加一个新元素,且数组自动增1。如果调用的函数SetSize的参数nGrowBy的值大于1,那么扩展内存将被分配。此函数返回被添加的元素序号,元素序号就是数组下标。参数newElement表示要添加的相应类型的数组元素。而Append函数是向数组的末尾添加由src指定的另一个数组的内容。函数返回加入的第1个元素的序号。

2、访问简单数组集合类的元素

   在MFC中,一个简单数组集合类元素的访问既可以使用GetAt函数,也可以使用“[]”运算符,例如:

//CObArray::operator[]示例

CObArray array;  //CObArray是对象数组集合类

CAge *pa;       //CAge是一个用户类

array.Add(new CAge(21));//添加一个元素

array.Add(new CAge(40));//再添加一个元素

pa=(CAge*)array[0];//获取元素0

Array[0]=new CAge(30);//替换元素0

//CObArray::GetAt示例

CObArray array;

array.Add(new CAge(21));//元素0

array.Add(new CAge(40));//元素1

3、删除简单数组集合类的元素

(1)使用函数GetSize和整数下标值访问简单数组集合类中的元素

(2)若对象元素是在堆内存中创建的,则使用delete操作符删除每一个对象元素

(3)调用函数RrmoveAll删除简单数组集合类中的所有元素

例如:以下代码是一个CObArray的删除示例:

CObArray array;

CAge *pa1;

CAge *pa2;

array.Add(pa1=new CAge(21));

array.Add(pa2=new CAge(40));

ASSERT(array.GetSize()==2);

for(int i=0;i<array.GetSize();i++)

delete array.GetAt(i);

array.RemoveAll();

  函数RemoveAll是删除数组中的所有元素,而函数RemoveAt(int nIndex,int nCount=1)

则表示要删除数组中从序号为nIndex元素开始的,数目为nCount的元素。

6.3.4文档序列化示例(262页)

  见书263页图6.13示例:首先通过对话框来输入一个学生记录,记录包括学生的姓名、学号和3门成绩。然后将记录内容保存到一个对象数组集合类对象中,最后通过文档序列化将记录保存到一个文件中。当添加记录或打开一个记录时,还会将数据显示在文档窗口(视图)中。

1、添加用于学生记录输入的对话框

(1)创建一个单文档的应用程序名为:学生记录

(2)(3)(4)(5)

2、添加一个CStudent类并使该类可序列化(在CMyDoc.h和CMyDoc.cpp中)

//一个可序列化的类必须是CObject的一个派生类,且在类声明中需要包含:

//264页说明

//在CMyDoc.h中加:

#endif // _MSC_VER > 1000

class CStudent:public CObject

{

       CString strName;  //姓名

       CString strID;    //学号

       float fScore1,fScore2,fScore3;//3门课程

       float fAverage;//平均成绩

       DECLARE_SERIAL(CStudent)  //宏调用,派生类本身

public:

       CStudent(){};//构造函数 与类同名

       CStudent(CString name,CString id,float f1,float f2,float f3);//构造函数

       void Serialize(CArchive &ar);//声明序列化函数

       void Display(int y,CDC *pDC);//在坐标为(0,y)处显示数据

};

//在CMyDoc.cpp中加:

CStudent::CStudent(CString name,CString id,float f1,float f2,float f3)//构造函数

{

       strName=name;

       strID=id;

       fScore1=f1;

       fScore2=f2;

       fScore3=f3;

       fAverage=(float)((f1+f2+f3)/3.0);

}

void CStudent::Display(int y,CDC *pDC) //在坐标为(0,y)处显示数据

{

       CString str;

       str.Format("%s %s %f %f %f %f",strName,strID,fScore1,

              fScore2,fScore3,fAverage);//打印姓名、学号、三门成绩、平均成绩

       pDC->TextOut(0,y,str);

}

IMPLEMENT_SERIAL(CStudent,CObject,1)//类名,基类名,应用程序版本号

void CStudent::Serialize(CArchive &ar)//使该类的数据成员进行相关序列化操作

{

       if(ar.IsStoring())//判断真或假,决定向文档写或读数据

ar<<strName<<strID<<fScore1<<fScore2<<fScore3<<fAverage;//向文件写入

else

ar>>strName>>strID>>fScore1>>fScore2>>fScore3>>fAverage;//从文件读取

}

3、添加并处理菜单

(1)

(2)

CAddDlg dlg;

       if(IDOK==dlg.DoModal())

       {             //添加记录

              CStudent *pStudent=new CStudent(dlg.m_strName,

              dlg.m_strID,dlg.m_fScore1,dlg.m_fScore2,

              dlg.m_fScore3);

       m_stuObArray.Add(pStudent);//对象数组集合类对象,加到数组中

       SetModifiedFlag();//设置文档更改标志

       UpdateAllViews(NULL);//更新视图

       }

(3)#include "序列化Doc.h"

#include "AddDlg.h"

4、完善代码

(1)在CMyDoc.h中添加下列成员函数和变量(用右键加)

CStudent * GetStudentAt(int nIndex);

       int GetAllRecNum(void);

       CObArray  m_stuObArray;//对象数组集合类CObArray的对象,

(2)在CMyDoc.cpp中,添加函数实现代码

CStudent * CMyDoc::GetStudentAt(int nIndex)

{

       if((nIndex<0)||nIndex>m_stuObArray.GetUpperBound())

              return 0; //超界处理

       return(CStudent *)m_stuObArray.GetAt(nIndex);

}

int CMyDoc::GetAllRecNum()

{

return m_stuObArray.GetSize();//返回数组的大小

}

(3)在CMyDoc.cpp的析构函数里加

CMyDoc::~CMyDoc()

{

       int nIndex=GetAllRecNum();

    while(nIndex--)

              delete m_stuObArray.GetAt(nIndex);

       m_stuObArray.RemoveAll();

}

(4)在Serialize函数中添加下列代码

if(ar.IsStoring())

{m_stuObArray.Serialize(ar);}

else

{m_stuObArray.Serialize(ar);}

这里:m_stuObArray是一个对象数组集合类CObArray的对象,当读取数据、调用Serialize成员函数时,它实际上是调用集合类对象中的元素的Serialize成员函数,并将对象添加到m_stuObArray中。那么它又是怎么知道元素是调用CStudent类的Serialize成员函数呢?这是因为当添加学生成绩记录后,一旦保存到文件中,就会将CStudent类名同时存到文件中,当读取时,就会自动使用CStudent类。这是CObArray序列化的一个内部机制。

(5)在CMyView.cpp的OnDrawe函数中加:

int y=0;

       for(int nIndex=0;nIndex<pDoc->GetAllRecNum();nIndex++)

       {

              pDoc->GetStudentAt(nIndex)->Display(y,pDC);

              y+=16;

       }

(6)打开文档的字串资源IDR_MAINFRAME,将其内容修改为:

文件名\nStudentRec\nEx_Stu\n记录文件(.rec)\n.rec\n

文件名.Document\nEx_Stu Document

(7)运行结果见263页图6.13

6.3.5使用CFile类

   在MFC中,CFile类是一个文件I/O的基类,它直接支持非缓冲、二进制的磁盘文件的输入、输出,也可以使用其派生类处理文本文件(CStdioFile)和内存文件(CMemFile)。CFile类的读、写功能类似于C语言中的fread和fwrite,而CStdioFile类的读、写功能类似于C语言中的fgets和fputs。

  使用CFile类可以打开或关闭一个磁盘文件、向一个文件读或写数据等。

1、文件的打开和关闭

   在MFC中,使用CFile打开一个文件通常使用以下2个步骤:

(1)构造一个不带任何参数的CFile对象

(2)调用成员函数Open并指定文件路径以及文件标志

CFile类的Open函数原型如下:

BOOL Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException *pError= NULL);

其中,lpszFileName用于指定一个要打开的文件路径,该路径可以是相对的、绝对的或是一个网络文件名(UNC)。

      nOpenFlags用于指定文件打开的标志,它的值如下表(或267页表6.9)所示:

      pError用于表示操作失败产生的CFileException指针。

      CFileException是一个与文件操作有关的异常处理类。

函数Open操作成功时返回TRUE,否则为FALSE

CFile类的文件访问方式如下:

CFile::modeCreate表示创建一个新文件,若该文件已存在,则将文件原有内容清除

CFile::modeNoTruncate 与CFile::modeCreate组合。若文件已存在,不会将文件原

有内容清除

CFile::modeRead 打开文件只读

CFile::modeReadWrite打开文件读与写

CFile::modeWrite打开文件只写

CFile::modeNoInherit防止子线程继承该文件

CFile::shareDenyNone共享文件的读和写,若其他线程用相关方式打开过此文件,

则创建失败

CFile::shareDenyRead禁止其他线程读此共享文件,若其他线程用相关方式打开过

此文件,则创建失败

CFile::shareDenyWrite禁止其他线程写此共享文件,若其他线程用相关方式打开过

此文件,则创建失败

CFile::shareExclusive禁止其他线程读、写此共享文件,若其他线程用相关方式打

开过此文件,即使是当前线程也会使创建失败

例如:以下代码将显示如何用读、写方式创建一个新文件:

Char *pszFileName = “c:\\test\\myfile.dat”;

CFile myFile;

CFileException fileException;

if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::modeReadWrite),&fileException)

{

   TRACE(“Cant open file %s,error=%u\n”,pszFileName,fileException.m_cause);

}

代码中:若文件创建打开有任何问题,Open函数将在他的最后一个参数中返回

CFileException(文件异常类)对象,TRACE宏将显示出文件名和表示失败原因的代码。使用AfxThrowFileException函数将获得更详细的有关错误的报告。

文件“关闭”使用Close函数,若该对象是在堆内存中创建的,还需要调用delete来删除它(不是删除物理文件)。

例:使用CFile类,完成从磁盘中读取数据的应用程序(为267页附加举例)

(1)用AppWizard创建一个单文档应用程序

(2)将ID_FILE_OPEN的消息映射加到View中。

ViewàClassWizardàClass name处置CMyView视图类里à在Object IDs

里找到ID_FILE_OPEN点黑àCOMMANDàAdd FunctionàEdit Code

(3)添加代码:

void CMyView::OnFileOpen()

{

       // TODO: Add your command handler code here

       CMyDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       CString FilePathname;

       CString FileName;

       CDC *pDC=GetDC();

       CFile MyFile;//使用不带参数的CFile对象

       CFileDialog dlg(TRUE,_T("TXT"),_T("*.TXT"),OFN_HIDEREADONLY|

              OFN_OVERWRITEPROMPT,_T("文本文件(*.TXT)|*.TXT|"));

              //利用CFileDialog类创建"打开"对话框,设置打开文件的类型

       if(IDOK==dlg.DoModal())

       {

              FilePathname.Format("%s %s","filepath:",dlg.GetPathName());

              FileName.Format("%s %s","Old file name:",dlg.GetFileName());

              MyFile.Open(dlg.GetFileName(),CFile::modeRead);

       }

       pDC->TextOut(0,0,FileName);

       pDC->TextOut(0,20,FilePathname);

       pDC->TextOut(0,40,"文件已被打开");

}

(4)编译运行出现空白对话框

文件à打开à在一个一般的单文档应用程序中找到如:ReadMe文件,点黑(是.TXT类型的)à打开à就见有一些文字说明了。

2、文件的读、写和定位

CFile类支持文件的读、写和定位操作。他们相关函数的原型如下:

UINT Read(void *lpBuf,UINT nCount);

此函数将文件中指定大小的数据读入指定的缓冲区,并返回向缓冲区传输的字节数。这个返回值可能小于nCount,这是因为可能到达了文件的结尾。

void Write(const void *lpBuf,UINT nCount);

 此函数将缓冲区的数据写到文件中。参数lpBuf用于指定要写到文件中的数据缓冲区的指针,nCount表示从数据缓冲区传送的字节数。对于文本文件,每行的换行符也被计算在内。

LONG Seek(LONG lOff,UINT nFrom);

此函数用于定位文件指针的位置,若要使定位的位置是合法的,此函数将返回从文件开始的偏移量。否则,返回值是不定的且激活一个CFileException对象。参数lOff用于指定文件指针移动的字节数,nFrom表示指针移动方式,他可以是CFile::begin(从文件的开始位置)、CFile::current(从文件的当前位置)或CFile::end

(从文件的最后位置,但lOff必须为负值才能在文件中定位,否则将超出文件)等。

文件刚刚打开时,默认的文件指针位置为0,即文件的开始位置。

void SeekToBegin()      将文件指针移到文件开始位置

DWORD SeekToEnd()   将文件指针移到文件结尾位置,并将返回文件大小

上述文件操作与C++的fstream操作相类似。

3、获取文件的有关信息

   CFile类还支持获取文件状态,包括文件是否存在、创建与修改的日期和时间、逻辑大小和路径等。

BOOL GetStatus(CFileStatus &rStatus)const;

static BOOL PASCAL GetStatus(LPCTSTR lpszFileName,CFileStatus &rStatus);

若成功获得指定文件的状态信息,该函数返回TRUE,否则返回FALSE。

参数:lpszFileName用于指定一个文件路径,这个路径可以是相对的或是绝对的,

但不可以是网络文件名

      rStatus用于存放文件状态信息,它是一个CFileStatus结构类型。

该结构具有下列成员:

CTime  m_ctime 文件创建日期和时间

CTime  m_mtime文件最后一次修改日期和时间

CTime  m_atime 文件最后一次访问日期和时间

LONG  m_size  文件大小的字节数(32位带符号整数)

BYTE  m_attribute文件属性(8位无符号整数)

Char   m_szFullName[_MAX_PATH]文件名

   static形式的GetStatus函数将获得指定文件名的文件状态,并将文件名复制至m_szFullName中。该函数仅获取文件状态,并没有真正打开文件,这对于测试一个文件的存在性是非常有用的。例如:

CFile theFile;

char *szFileName=”c::\\test\\myfile.dat”;

BOOL bOpenOK;

CFileStatus status;

if(CFile::GetStatus(szFileName,status))//该文件已存在,直接打开

{ bOpenOK=theFile.Open(szFileName,CFile::modeWrite);}

else

 {bOpenOK=theFile.Open(szFileName,CFile::modeCreate|CFile::modeWrite);}

4、CFile和CArchive(归档,从磁盘中读写,可看成是二进制流)类之间的关联

   CFile theFile;

   theFile.Open(…,CFile::modeWrite);

CArchive archive(&theFile,CArchive::store);

CArchive构造函数的原型如下:

   CArchive(CFile *pFile,UINT nMode,int nBufSize=4096,void *lpBuf=NULL);

参数:pFile用于指定与之关联的文件指针。

      nBufSize表示内部文件的缓冲区大小,默认值为4096字节。

      lpBuf表示自定义的缓冲区指针,若为NULL,则表示缓冲区在堆内存中,

当对象清除时,缓冲区内存也被释放;若指明用户缓冲区,对象消除

时,缓冲区内存不会释放。

      nMode用于指定文档是用于存入还是读取,它可以是CArchive::load(读取

数据)、CArchive::store(存入数据)或CArchive::bNoFlushOnDelete

(当析构函数被调用时,避免文档自动调用Flush.若设置这个标志,

则必须在析构函数被调用之前调用Close,否则文件数据将被破坏)。

也可将一个CArchive对象与CFile类指针相关联,如下(ar是CArchive对象):

Const CFile *fp=ar.GetFile();

6.4视图及视图类

   视图是框架窗口的子窗口,它与文档紧密相联,是用户与文档之间的交互接口。视图不仅可以响应各种类型的输入,例如键盘输入、鼠标输入或拖放输入、菜单、工具栏和滚动条产生的命令输入等,而且能实现文档的打印和打印预览。

6.4.1一般视图类的使用

MFC中的CView类及其他的派生类封装了视图的各种不同的功能,他们为用户实现最新的Windows特性提供了很大的便利。这些视图类如下所示,他们都可以作为文档应用程序中视图类的基类,其设置方法是在MFC AppWizard创建SDI/MDI的第六步中进行基类的选择。

CView的派生类及其功能描述

CScrollView      具有滚动或缩放功能

CFormView      提供可滚动的视图,它由对话框模板创建,并具有和对话框一

样的设计方法

CRecordView     提供表单视图直接与ODBC记录集对象关联;和所有的表单视

图一样,CRecoraView也是基于对话框模板设计的

CDaoRecordView  提供表单视图直接与DAO记录集对象关联,其他同CRecordView

CCtrlView        是CEditView、CListView、CTreeView和CRichEditView的基类,他们提供的文档/视图结构,也适用于Windows98(NT)中的新控件

CEditView        提供包含编辑控件的视图;支持文本的编辑、查找、替换以及滚动功能

CRichEditView    提供包含复合编辑控件的视图;它除了CEditView功能外还支持字体、颜色、图表及OLE对象的嵌入等。

CLisView         提供包含列表控件的视图;他类似于Windows98资源管理器的右侧窗口 

CTreeView       提供包含树状控件的视图;他类似于Windows98资源管理的左侧窗口

1、CEditView类

CEditView类对象是一种视图,像CEdit类一样,他是提供窗口编辑控制功能,可以用于执行简单文本操作,如打印、查找、替换、剪贴板的剪切、复制和粘贴等。由于CEditView类自动封装上述功能的映射函数,因此只要在文档模板中使用CEditView类,那么应用程序的“编辑”菜单和“文件”菜单里的菜单项都可自动激活。

例如:创建一个基于CEditView类的单文档应用程序

(1)建一个单文档基于CEditView的应用程序

(2)编译

抱歉!评论已关闭.