【OOP】实验8 MFC
作者:3sbase | 发布时间:2016/3/19

一、实验类型

    设计型

二、实验目的与要求

1、实验目的

    学习VC++ MFC开发环境

2、实验要求

了解VC++ MFC架构;掌握架构中各个类的作用;能够编写简单的Windows程序。

三、上机准备

1、实验室安装VC++6.0软件。

2、学习VC++相关教程。

四、实验内容

编写单文档应用程序,在视图区显示一串字符,并保存于文档中。

1、根据向导创建一个单文档应用程序,并且显示一串字符“Hello,World!”。

首先启动VC++编程环境。

选择File菜单下的New,进入Project标签页下,选择工程类型为MFC AppWizard(exe)。在Project name框中输入一个项目名test。在Location框中输入准备存放项目文件的路径。在Platforms框中选中Win32。单击[OK]

进入MFC AppWizard-Step1构造应用程序框架。

VC++6.0给我们提供了三种基本的应用程序框架:

(1)Single document单文档(SDI

指明应用程序的主界面以单文档界面形式出现。也就是指应用程序一次只能打开一个文件。

(2)Multiple document多文档(MDI

指明应用程序的主界面以多文档界面形式出现。也就是指应用程序可以同时打开多个文件,并进行处理。

(3)Dialog based对话框(DIALOG

指明应用程序的主界面以对话框界面形式出现。这种程序框架适合于涉及文档少而交互操作多的场合。

在这里,我们选择SDI

AppWizard-Step2AppWizard-Step5的步骤比较简单请参考运行提示,全部点击[Next]按钮,当进行到MFC AppWizard-Step6时,系统将为你生成了4个类CTestView,CTestApp,CMainFrameCTestDoc。点击[Finishi]按钮,弹出[新建工程信息]对话框,点击[OK]

运行程序。

2、分析这4个类中的重要元素。

¨         WinMain()函数

Windows总是要求每个应用程序要有WinMain函数,而在此看不到这个函数,是因为它隐藏在应用程序框架内部了。

¨         CTestApp

CTestApp的对象就代表一个应用程序。该程序定义了一个单独的全局CTestApp类的对象theAppCWinApp基类决定了theApp的大部分行为。

启动应用程序

当用户运行该应用程序时,Windows会自动调用应用程序框架内部的WinMain函数,WinMain函数会去查找该应用程序的全局构造对象,该对象是由CWinApp所派生出的类的对象。注意,在VC++中,全局对象在主程序被运行之前就已经构造好了。

¨         CTestApp::InitInstance成员函数

WinMain发现了该应用程序对象时,它会自动调用虚拟InitInstance成员函数,该函数会进一步调用相应得函数来完成主窗口的构造和显示工作。我们必须在派生出的应用程序类中重载InitInstance函数,因为CWinApp基类根本无法知道你具体需要什么样的主框架窗口。

¨         CTestApp::Run成员函数

Run函数被隐藏在基类中,它用来负责传递应用程序的消息给相应得窗口,从而维护应用程序的运转。WinMain在调用InitInstance之后将紧接着调用Run

¨         CMainFrame

CMainFrame的对象代表着应用程序的主框架窗口。当构造函数调用基类CFrameWnd

Create成员函数时,Windows将创建具体的窗口结构,同时应用程序框架会将所创建的窗口结构连接到C++对象中。为了显示所创建的窗口,必须调用基类中ShowWindowUpdateWindow成员函数。

关闭应用程序

用户可以通过关闭主框架窗口来终止应用程序。这一举动将会引起一系列事件的发生,首先CMainFrame对象将被删除,然后退出Run,进而退出函数WinMain,最后在删除CTestApp对象。

至此,我们只是对MFC生成的应用程序的框架有了一定的了解,可还不知道如何才能显示“Hello,World”,下面让我们看一个系统自动生成的类:

class CTestView : public CView

{

protected: // create from serialization only

      CTestView();

      DECLARE_DYNCREATE(CTestView)

// Attributes

public:

      CTestDoc* GetDocument();

// Operations

public:

 

   // Overrides

      // ClassWizard generated virtual function overrides

      //{{AFX_VIRTUAL(CTestView)

      public:

      virtual void OnDraw(CDC* pDC);  // overridden to draw this view

      virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

      protected:

      virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);

      virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);

      virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

      //}}AFX_VIRTUAL

// Implementation

public:

      virtual ~CTestView();

      virtual void AssertValid() const;

      virtual void Dump(CDumpContext& dc) const;

protected:

      DECLARE_MESSAGE_MAP()

};

该类被称为视图类,对应框架窗口的客户区,主要作用是在客户区上显示各种数据和接受各种用户消息(如按下鼠标左键,移动鼠标等消息)。在这个类中有一个虚成员函数OnDraw(CDC *pDC),可以实现输出字符串的功能。因此,我们只要扩充这个函数的代码即可实现输出“Hello,World”的功能。下面先看一下缺省(由系统自动生成的)OnDraw函数已具备了哪些功能:

void CTestView::OnDraw(CDC* pDC)

{

      CTestDoc* pDoc = GetDocument();

      ASSERT_VALID(pDoc);

      // TODO: add draw code for native data here

}

该函数中只有两条语句。第一条是获得文档类的指针,第二条是一个特殊的VC++测试,它使用ASSERT_VALID宏进行测试(宏是一套预先编写好的C++指令),如果pDoc无效,则发出警告并停止应用程序的进行。我们只需在//TODOadd draw code for native data here行下面增加如下代码即可:

pDC->TextOut(100,100,”Hello World!”);

可能有的读者一看就知道是在坐标为(100,100)的位置输出字符串”Hello World!”。其中,pDC是有MFC类库中CDC类定义的设备描述表指针。设备描述表是Windows程序设计中的一个重要概念。由于Windows程序设计不允许程序员直接操纵硬件设备,而要求“程序与设备无关”,为此,Windows提供了一个称为设备描述表的的数据结构给程序员,程序员可以通过对这个设备描述表的操作来进行绘图,再由Windows对设备描述表进行管理,与真正的物理设备关联。简单地说,设备描述表的数据结构中封装了图形化的“属性”和图形化的工具(GDI函数)。

从整体看,该应用程序的大部分功能都包含在类库的基类CWinAppCFrameWnd中。在编写过程中,我们遵循了一些简单的结构规则,并且扩充了OnDraw函数。可见VC++允许我们“借用”许多代码而用不着去复制,这就好比我们和应用程序框架是非常不错的合作伙伴一样,应用程序框架为我们提供了大致的结构,而我们只需将应用程序具体化。

可见,应用程序框架不但定义了应用程序的结构,而且它所包含的内容还不仅仅是C++的基类。WinMain以及其他一些用来支持消息处理、诊断、DLL等诸如此类的元素都包含在应用程序框架中。对MFC了解得越深,越能得心应手地运用它,得到事半功倍的效果。

3VC++的文档—视图模型

上面的例子用到了应用程序对象和框架窗口对象,而大多数的MFC库应用程序都要比它复杂得多。这些复杂的应用程序除了需要包含应用程序和框架类外,一般还要包含两个分别用来代表“文档”和“视图”的类,这种“文档—视图”结构是应用框架的核心。它是在Smalltalk环境下的Mode/View/Controler类的结构上发展出来的。是利用MFC进行Windows程序设计的一种模型。

简而言之,文档—视图结构将数据从用户对数据的观察中分离了出来,这么做的一个最明显的好处就是允许对同一数据可以有多种视图。而多个视图又可以共用同一个数据。在MFC库应用程序中,文档和视图是由VC++的类的实例来描述的。

文档类代码通常和File OpenFile Save菜单项相关联,而派生文档类一般用来完成对文档对象数据的实际读写工作。视图类通常表示一个包含于框架窗口中的窗口(即客户区),而派生视图类则常用来和文档类相联系,负责应用程序的显示和打印机I/O。派生视图类及其类共同处理Windows的消息,而MFC库则协调着文档、视图、框架窗口以及应用程序对象之间的相互作用关系,这种协调大多是通过虚函数来实现的。

采用文档—视图模式另一个非常方便之处是,在文档类和视图类之间可以方便地获得对方的指针(用来得到对方的数据在视图中显示、在文档类中保存)。

在视图类中获得文档类的指针代码如下:

CTestDoc* pDoc=GetDocument();

在文档类中获得视图类的指针代码如下:

POSITON Position=GetFirstViewPosition();

CView* pVView=GetNextView(Position);

CTestView* pView;

while(pVView!=NULL)

{

  if(pVView->IsKindOf(RUNTIME_CLASS(CTestView))) {break;}

  pVView=GetNextView(Position);

 }

pView=(CTestView*)pVView;

在这里,我们做个实验。

CTestDoc类中定义一个CString类型的变量strHello

public:

    CString strHello;

CTestDoc类的构造函数中,对这个公共变量进行赋值。

strHello=”欢迎您!”;

CTestView中的OnDraw函数中调用CTestDoc类对象中的strHello。在CTestView中添加如下语句:

pDC->TextOut(100,100,pDoc->strHello);

该语句的作用就是将文档类的数据成员strHello在客户区显示出来。pDocCTestDoc类的对象的指针。在OnDraw函数中,用CTestDoc* pDoc = GetDocument();语句实现pDoc指针变量的初始化。

4、利用串行化存取文档

   CArchive类用于以持久的二进制形式来存储一个复杂的对象数据。用户可以把对象的内容存储到存储区中,也可以从存储区中读取内容重新创建对象。这个过程称为“串行化”。与CFile文件对象及fstream文件流等方式比较起来,这种方式更简洁。

CArchive提供的主要操作:

A IsStoringIsLoad函数

这两个函数在CArchive类中的定义如下:

BOOL IsStoring();

BOOL IsLoading();

这两个函数用来判断正在进行的读取还是存储操作。当从存储区中进行读取操作时,IsStoring函数返回FALSEIsLoading函数返回TRUE;而在进行存储操作时,IsStoring函数返回TRUEIsLoading函数返回FALSE

B “<<””>>”操作

重载抽取”>>”和插入”<<”操作符是方便的文档编程接口。

ar<<strHello; //保存

ar>>strHello;  //读取

除了能够对各种数据原型进行操作外,更主要的是这两个操作符能够对各个CObject派生的对象,可以直接用操作符进行操作,利用串行化存储对象,同学们可以看看相关的介绍,这次实验就不讲了。

在这里,我们将strHello保存到文件中,并能够读取。修改CTestDoc的成员函数Serialize如下。运行程序,保存文件,就将strHello的内容保存到了一个文件中。以后打开该文件,就会看到strHello的内容。

void CTestDoc::Serialize(CArchive& ar)

      if (ar.IsStoring())

      {

             ar<<strHello;

      }

      else

      {

             ar>>strHello;

      }

}

五、实验步骤

1VC++MFC应用程序的建立。

2、在文档类中定义变量CString strHello,并在构造函数中赋值为“欢迎您!

3、在视图类中显示strHello

4、基于Serialize函数将strHello保存于文件中。

六、实验报告

1VC++MFC应用程序的建立。

2、以自己的话,说明VC++ MFC四个类的作用,写入实验报告。

3、说明实验中修改后的OnDraw函数中各个语句的作用。

4、说明Serialize函数的作用,并解释各条语句的含义。