程序设计简介_第1页
程序设计简介_第2页
程序设计简介_第3页
程序设计简介_第4页
程序设计简介_第5页
已阅读5页,还剩255页未读 继续免费阅读

下载本文档

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

文档简介

1VisualC++的可视化集成开发环境2Windows消息与命令

3单文档与多文档程序 4对话框与常用组件

5通用对话框大纲:1、VisualC++

的可视化集成开发环境教学目标:掌握VC++IDE,能够在该环境下熟练使用AppWizard生成应用程序框架。了解一个C++应用程序的主要组成以及运行特点,掌握一些必要的相关理论知识。重点、难点:重点:熟悉VC++IDE,掌握AppWizard的使用方法,了解C++程序的主要组成。难点:一些必要的理论知识:MFC库,Win32API等。教学方法:以演示为主,辅以实验教学。教学内容:

集成开发环境,或者说是IDE,是一个集成了源代码编辑器、资源编辑器、程序编译器、程序调试工具等相关工具的集合体。整个环境的构建使得编辑应用程序源代码以及生成一个Windows应用程序等工作变得相对轻松。IDE提供了大量的实用工具来支持可视化编程。这些实用工具包括:项目工作区、AppWizard,ClassWizard,WizardBar等。(一)项目工作区项目工作区是IDE的一个最重要的组成部分。每一个应用程序都是由多个源文件组成的,另外还需要包含编译和1.1

集成开发环境调试时的参数文件和库文件名等,通常把这样一组完整的文件集合称作一个项目(或工程)。在一个项目工作区中可以处理:(1)一个工程和它所包含的文件。(2)一个工程的子工程。(3)多个相互独立的工程。(4)多个相互依赖的工程(可以是不同编程语言编写的)。项目工作区一般位于屏幕的左侧,它包含下面三种视图:(1)文件视图(FileView)显示所创建的工程。展开该文件夹可以查看工程中所包含的文件,一般包括:项目的源文件,头文件,资源文件等等。(2)类视图(ClassView)

显示项目中定义的C++类。展开该文件夹显示当前工程中定义的所有类,展开类可查看类的数据成员和成员函数以及全局变量等。利用ClassView可以方便快速地浏览应用程序所包含的类的定义或类中成员函数的定义。(3)资源视图(ResourceView)显示当前活动项目中所包含的资源。包括对话框、菜单快捷键等。(二)AppWizard(应用程序向导)从本质上讲它是一个代码生成器,它创建一个带有特性(或者定制的)、含有类名及源代码文件名的Windows应用程序的基本框架。这些特性、类名都是通过对话框由用户指定的。AppWizard可以使你迅速开始一个应用程序,在以后的学习中我们要经常和它打交道。(三)ClassWizard(类向导):

ClassWizard使程序员从维护VisualC++类代码这样繁琐复杂的工作中解脱出来。在需要定义一个新类、一个新的虚函数、或者一个新的消息处理函数时,ClassWizard可以编写函数原型、函数体以及进行Windows消息映射等。总之ClassWizard起到帮助程序员编写源代码、管理源代码的作用。(四)资源编辑器单击VisualC++Workspace窗口的ResourceView选项卡时,你可以选择一个资源,以进行编辑。这个主窗口中容纳了一个适合资源类型的资源编辑器。此窗口也可以容纳一个所见及所得的菜单编辑器和一个强大的对话框图形编辑器,并且,它还包含了编辑图标、位图和字符串的工具。在对话框编辑器中,除了标准的Windows控件和新的Windows常用控件之外,还允许你插入ActiveX控件。通常,每一个工程都有一个文本格式的资源脚本(RC)文件,它描述工程的菜单、对话框、字符串和快捷键资源。RC文件也可以有#include语句,以便从其他子目录中引进资源。这些资源包括特定的项目,例如位图(BMP)和图标(ICO)文件,以及所有VisualC++程序公有的资源。1.2Vc++编程中的一些相关的概念(一)MFC库

MFC是MicrosoftFoundationClass的缩写。MFC库是微软公司开发出的功能强大的类库,用来供程序员开发Windows应用程序使用,换句话说,MFC库是可以在应用程序中使用的已经定义好的有关C++类的集合,根据约定MFC库类名以字母C作为开头。MFC库是我们学习的重点,我们所涉及到的是6.0版本的MFC库。根据约定,MFC库类名以字母C作为开头。(二)应用程序框架应用程序框架的一种定义是:提供一般应用程序需要的全部面向对象软件组件的集成集合。这个定义有点晦涩,通过一个具体的实例就很容易理解了。应用程序框架是类库的超集,一般的类库只是一种孤立的类的集合,用来嵌入到任何程序中,但是应用程序框架却定义了程序的结构。(三)Win32API

早期的Windows程序员使用C语言编写Win16API的应用程序。今天,如果要编写32位应用程序,你必须直接或间接地使用新的Win32API。大多数的Win16函数都有对应的Win32函数,但是参数会有变化。在MFC库中的许多类封装了Win32的API函数,因而在实际的MFC编程中,你可以使用全局的Win32API函数,也可以使用MFC库中类的成员函数。(四)关于ActiveX控件

VB自1991年面世以来一直非常流行,它成功的原因要归于其开放性,VB的16位版本支持VisualBasic控件(VBX),它是VB开发者可购买或者自己编写的随时可用的软件组件。在微软MFC开发小组也为MicrosoftVisualC++程序员找到了在他们程序中使用VBX的方法。VBX是建立在16位段式结构的基础上的,不适于32位结构,现在ActiveX控件(以前称为OLE,或者OCX)以MicrosoftCOM(ComponentObjectModel)技术为基础,可以替代VBX。应用程序开发者可以在VB和VisualC++6.0中使用ActiveX,ActiveX是用C++编写的。我们可以不知道ActiveX的编写技术,我们只要学习在VisualC++应用程序中如何使用它们。

ActiveX控件是软件模块,它插入到的C++程序中,与普通的Windows控件插入到程序中的方法一样,至少表面上是这样的。(五)Windows图形设备接口(GDI)许多MS-DOS程序直接写显存和打印机接口。这种技术的缺点是对每一种显示卡和每一种打印机型号,都需要提供驱动程序软件。Windows引入了一个名为图形设备接口(GDI)的抽象层。Windows提供视频和打印机驱动程序,所以,你的程序不必知道系统连接的显卡和打印机的类型。程序不是寻址硬件,而是调用GDI函数,这些函数引用名为设备环境(DC)的数据结构Windows把设备环境结构映射到物理设备,并发出适当的输入/输出指令。图形设备接口几乎与直接视频一样快,并且它允许不同的Windows应用程序来共享显示。1.3MFC类库的框架结构

前面提到,MFC库是微软公司为了用户开发应用程序需要而开发出的功能强大的类库。MFC为程序员实现了所有应用程序的公共部分,它不仅包含了C++基类,还包含了WinMain函数(Windows下的主函数)以及其它一些支持菜单、窗口、对话框、设备上下文等的组件。MFC的核心是以C++形式封装了大部分的WindowsAPI,因此MFC向用户提供了一个面向Windows中结构的简单的C++成员函数的接口。

MFC类库可分为:基础类、宏和全局函数两个主要部分(1)基础类。MFC中的基础类按功能可以分为一下几类:基类、应用程序框架类、命令相关类、文档/视图类、线程类、可视对象类、窗口类、对话框类、属性表类、控制类、菜单类、设备描述表类、绘图对象类、文件类、诊断类、异常类数据库类等等。(2)宏和全局函数。若某个函数或变量不是属于某一个具体的类,那么它就是一个全局函数或者是全局变量。宏和全局函数提供以下功能:数据类型、运行时刻对象类型服务、诊断服务、异常处理、CString格式化及信息框显示、消息映射、应用信息管理对象连接和嵌入(OLE)等。全局函数以“Afx”为前缀。所有全局变量都是以“afx”为前缀,宏不带任何前缀,但是全部是大写。从继承关系来看,又可将MFC中的类分成两大类:大多数的MFC类是从Cobject继承下来的,Cobject类描述了几乎所有的MFC中其它类的一些公共特性。Cobject类为派生类提供的服务主要包括:对象诊断、输出对象内部信息、对象有效性检查、运行时访问类的信息、安全可靠地把通用的Cobject指针转化为派生类的指针,等等。另外一些类则不是从Cobject类继承下来的,这些类包括:字符串类(CString)、日期时间类(CTime)、矩形类(CRect)等。下图是MFC库的框架图1.4

对一个最简单的应用程序框架的分析

下面的代码是MyApp应用程序的头文件和实现文件。类CMyApp和CMyFrame都是从MFC库基类派生出来的://MyApp.h//applicationclassclassCMyApp:publicCWinApp{public:virtualBOOLInitInstance();};//framewindowclassclassCMyFrame:publicCFrameWnd{public:CMyFrame();protected://"afx_msg"indicatesthatthenexttwofunctionsarepartoftheMFClibrarymessagedispatchsystemafx_msgvoidOnLButtonDown(UINTnFlags,CPointpoint);afx_msgvoidOnPaint();DECLARE_MESSAGE_MAP()};下面是MyApp应用程序的实现文件:#include<afxwin.h>//MFClibraryheaderfiledeclaresbaseclasses#include"myapp.h"CMyApptheApp;//theoneandonlyCMyAppobjectBOOLCMyApp::InitInstance(){m_pMainWnd=newCMyFrame();m_pMainWnd->ShowWindow(m_nCmdShow);m_pMainWnd->UpdateWindow();returnTRUE;}BEGIN_MESSAGE_MAP(CMyFrame,CFrameWnd)ON_WM_LBUTTONDOWN()ON_WM_PAINT()END_MESSAGE_MAP()CMyFrame::CMyFrame(){Create(NULL,"MYAPPApplication");}voidCMyFrame::OnLButtonDown(UINTnFlags,CPointpoint){TRACE("EnteringCMyFrame::OnLButtonDown-%lx,%d,%d\n",(long)nFlags,point.x,point.y);//在调试窗口显示}voidCMyFrame::OnPaint(){CPaintDCdc(this);dc.TextOut(0,0,“Hello,world!”);//在客户区输出

}下面给出一些程序元素的分析:WinMain函数:Windows要求应用程序有一个WinMain函数。在这个例子中,我们看不到WinMain函数,因为它被隐藏到应用程序框架内部了。CMyApp类:类CMyApp的一个对象代表一个应用程序。程序定义了一个全局的CMyApp对象,即theApp。CWinApp基类决定theApp的大多数行为。应用程序启动:当用户启动应用程序时,Windows调用应用程序框架内置的WinMain函数,并且,WinMain寻找CWinApp派生出的全局构造的应用程序对象。不要忘记,在C++程序中,全局对象在主程序之前构造。成员函数CMyApp::InitInstance:当WinMain函数找到应用程序对象时,它调用虚成员函数InitInstance,这个成员函数调用所需要的构造和显示应用程序的主框架窗口。我们必须在派生的应用程序类中重载InitInstance,因为CWinApp基类不知道需要什么样的主框架窗口。成员函数CWinApp::run函数run隐藏在基类中,但是它发送应用程序的消息到它的窗口,这样可以保持应用程序的运行。在WinMain调用InitInstance之后,便调用run。CMyFrame类:类CMyFrame的一个对象代表应用程序的主框架窗口。当构造函数调用基类CFrameWnd的成员函数Create时,Windows创建实际窗口结构,应用程序框架把它连接到C++对象。函数ShowWindow和UpdateWindow也是基类的成员函数,必须调用他们来显示来显示窗口。CMyFrame::OnLButtonDown函数:鼠标左键按下映射到CMyFrame的一个成员函数。应用程序执行时,鼠标左键按下时,会调用这个函数,该函数调用MFC库的TRACE宏,以便在调试窗口显示信息。CMyFrame::OnPaint函数:每次重新绘图时(在程序的开始,当用户改变窗口的大小,全部或部分窗口重新显现时),应用程序框架就调用CMyFrame的这个重要的映射成员函数。语句CpaintDC与图形设备接口(GDI)有关。关闭应用程序:用户通过主框架窗口来关闭应用程序。这个操作激发一系列事件的发生,包括CMyFrame对象的析构,从Run中退出、从WinMain中退出CMyApp对象的析构。1.5标识符命名方法

好的表示符命名方法使变量、函数易于记忆,并且程序的可都性大大提高。在VC++中对于变量、函数的命名有自己的一套方法,它是由微软的一个名叫西蒙尼的开发人员提出来的,由于西蒙尼的国籍是匈牙利,所以这种标识符命名法称作匈牙利命名法。匈牙利命名法主要有两条规则:(一)标识符的名字以一个或者多个小写字母开头,这些小写字母用来指定数据类型。下面是常见的数据类型的标准前缀:

前缀数据类型

c字符(char)

s短整型(short)n整数(integer)

sz以‘\0’结尾的字符串

b字节

iint(整数)

fBOOLW字(WORD,无符号短整数)

l长整数(long)

hHANDLE(句柄,无符号int)

m_类的成员变量

fn函数

dw双字(DWORD,无符号长整数)2Windows消息与命令教学目标:了解Windows编程模式中的消息驱动机制,熟练使用ClassWizard进行变量定义,消息映射等。能够编写菜单并进行必要的命令消息映射重点、难点:重点:使用ClassWizard进行消息映射难点:消息的分类以及消息映射使用的模型。教学方法:以演示为主,辅以实验教学。教学内容:2.1消息驱动机制消息是一种报告有关事件发生的通知,类似于DOS下的用户输入。Windows应用程序是由消息驱动的。Windows操作系统允许多个任务同时运行,应用程序的输入输出由Windows统一管理;Windows系统下每一个窗口都维护一个消息队列,操作系统接收和管理所有输入消息、系统消息,并把它们发送给相应窗口的消息队列。应用程序初始化完成后,进入消息循环,维护自己的消息队列,从中取出消息,对其进行处理。编写消息处理函数是Windows编程的主要工作之一。Windows应用程序的消息来源有以下四种:

(1)输入消息:包括键盘和鼠标的输入。这一类消息首先放在系统消息队列中,然后由Windows将它们送入应用程序消息队列中,由应用程序来处理消息。

(2)控制消息:用来与Windows的控制对象,如列表框、按钮、检查框等进行双向通信。当用户在列表框中改动当前选择或改变了检查框的状态时发出此类消息。这类消息一般不经过应用程序消息队列,而是直接发送到控制对象上去。

(3)系统消息:对程序化的事件或系统时钟中断作出反应。一些系统消息,像DDE消息(动态数据交换消息)要通过Windows的系统消息队列,而有的则不通过系统消息队列而直接送入应用程序的消息队列,如创建窗口消息。

(4)用户消息:这是程序员自己定义并在应用程序中主动发出的,一般由应用程序的某一部分内部处理。基于MFC类库的应用程序完成初始化后,调用Run()函数进入消息循环。在CWnd类(所有窗口类的基类)中预定义标准Windows消息的处理函数,处理函数的名称以“On”开始,用户可以根据需要重写这些函数。

在CWnd类中,标准Windows消息的处理函数声明都带有afx_msg关键字,例如消息WM_PAINT的处理函数被声明为:

afx_msgvoidOnPaint();

关键字afx_msg用于把消息处理函数与其它CWnd成员函数分开,这些函数是通过消息映射实现的,依赖于标准的预处理宏。预处理之后,关键字afx_msg的位置就变成了空白。以全局对象theApp为例,其中的消息映射如下:

BEGIN_MESSAGE_MAP(CEx01App,CWinApp) //{{AFX_MSG_MAP(CEx01App)ON_COMMAND(ID_APP_ABOUT,OnAppAbout)//注意:ClassWizard(类向导)将可能添加或删除消息映射宏

//}}AFX_MSG_MAP //标准的基于文档的文件命令

ON_COMMAND(ID_FILE_NEW,CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN,CWinApp::OnFileOpen) //标准的打印设置命令

ON_COMMAND(ID_FILE_PRINT_SETUP,CWinApp::OnFilePrintSetup)END_MESSAGE_MAP()

其中的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP都是预处理宏,用于声明消息映射的开始和结束。而在类中重新修改的消息处理函数声明形式为:

//Generatedmessagemapfunctions protected: //{{AFX_MSG(CEx01Ciew) afx_msgvoidOnPaint(); //}}AFX_MSG DECLARE_MESSAGE_MAP()

最后的DECLARE_MESSAGE_MAP宏,声明在该类中使用消息映射。2.2应用程序菜单

Windows菜单是应用程序命令项列表,菜单项可以是文字或位图。通过选择菜单项使应用程序完成与菜单项相关的命令。下面通过一个例子来介绍如何在应用程序中建立新菜单、如何在菜单中加入新的菜单项、如何为菜单项建立相应的命令处理函数以实现消息映射。首先看看如何在已有的下拉菜单中增加一个菜单项。第一步,编辑菜单项资源,步骤如下:

(1)运行AppWizard创建Ex02,选中“Singledocument”(单文档SDI)单选项后,直接按下Finish按钮。(2)在项目工作区窗口中打开ResourceView(资源视图)。

(3)双击该视图中的Menu图标,展开菜单资源。

(4)双击IDR_MAINFRAME,打开菜单编辑器,如图2.1所示。

(5)可以看到,在“文件”菜单的最下方有一个空白菜单项,用户可以编辑这个菜单项来添加菜单项。也可以单击某个菜单项,然后按键盘上的Insert键,在选定的菜单项前面插入一个新的菜单项。

(6)双击空白菜单项,打开MenuItemProperties对话框,如图2.2所示。图2.1菜单编辑器图2.2MenuItemProperties对话框(7)在Caption文本框中输入新菜单项名,输入的名字是“Demo”。在ID组合框中输入新菜单项的ID。注意,这个ID必须是唯一的,不能和别的ID重名。一般菜单项ID的命令规则是:ID_菜单名_菜单项名。因此将该菜单项命名为ID_FILE_DEMO。

(8)关闭该对话框,新的菜单项已经添加到原有的菜单中。运行这个程序可以看到,在“文件”菜单的最下方有一个“Demo”菜单项,如图2.3所示。注意:这里选SDI类型,一是为了简化程序设计,二是想让读者对比SDI应用程序(如本例的Ex02)和MDI应用程序在外观和使用上的一些区别,对SDI和MDI有一个简单了解。

第二步,进行消息映射。图2.3中Demo菜单项是灰色的,不可用,因为它没有命令处理函数。为此必须要对菜单项进行消息映射,为新的菜单项建立处理函数。操作步骤如下:(1)在View菜单中选择ClassWizard选项或使用加速键Ctrl+W,打开ClassWizard对话框,选中其中的MessageMaps选项卡,如图1.4所示。(2)在ClassName下拉列表框中选择CEx02View。(3)在ObjectIDs列表框中选择菜单项Demo的ID,即ID_FILE_DEMO。(4)在Messages列表框中单击COMMAND,此时AddFunction按钮变亮,表示可以图23添加了Demo菜单项的应用程序图2.4ClassWizard的MessageMaps选项卡

添加处理函数。单击AddFunction按钮或双击COMMAND,弹出AddMemberFunction对话框,如图2.5所示。在Memberfunctionname编辑框中给出了系统推荐使用的函数名是OnFileDemo。通常可以使用这个缺省的名字,如果有特殊需要也可以修改它。单击OK按钮,这时可以在Memberfunctions列表框中看到Demo菜单项的处理函数已经创建,如图1.6所示。也就是说,当用户选择Demo菜单项时,应用程序将调用OnFileDemo函数实现消息映射。在图2.6中单击OK按钮完成消息映射。

图2.5AddMemberFunction对话框图2.6创建Demo的处理函数OnFileDemo

第三步,编写响应程序代码。现在为OnFileDemo函数编写实际的处理程序。比如,当用户选择菜单项Demo时,在窗口中显示“您选择了Demo菜单项。”信息。操作步骤如下:

(1)在文档类CEx02Doc中声明一个CString对象m_String。从上一章可以知道,程序中的文档对象用于存储在视图中的数据,因此这里选择在文档类中创建字符串对象。在项目工作区中选ClassView类视图,双击CEx02Doc,打开类编辑器,如图2.7所示。对CEx02Doc.h头文件中修改CEx02Doc类的定义:

classCEx02Doc:publicCDocument{protected://createfromserializationonly CEx02Doc(); DECLARE_DYNCREATE(CEx02Doc)

CStringm_String; //输入本行内容

……}

程序中的黑体字部分表示需要输入的内容,本书下面章节的片段中均使用此方法。“m_”前缀声明成员变量是VisualC++的标准,读者应该习惯于使用这种变量声明方法。(2)在CEx02Doc的构造函数中初始化m_String。在类视图中单击文档类CEx02Doc前面的“+”,将其展开。双击CEx02Doc()函数,将会在正文窗口内打开代码编辑器。对CEx02Doc()函数编辑如下:CEx02Doc::CEx02Doc(){ //TODO:addone-timeconstructioncodehere m_String=""; //输入本行内容}(3)编辑OnFileDemo()函数,将m_String赋值为“您选择了Demo菜单项。”。在图2.6(若在屏幕上看不到它,按下Ctrl+W加速键)的Memberfunction列表框中单击OnFileDemo,然后按下EditCode按钮或双击OnFileDemo,出现Ex02View.cpp代码编辑窗口,并且光标已经处于需要编辑的函数OnFileDemo中。编辑OnFileDemo()函数:

voidCEx02View::OnFileDemo(){//TODO:Addyourcommandhandlercodehere

CEx02Doc*pDoc=GetDocument();//输入以下四行内容

ASSERT_VALID(pDoc); pDoc->m_String="您选择了Demo菜单项。"; Invalidate();}

由于对象m_String处于文档类CEx02Doc中,要在视图类中对这一对象进行存取,必须先获得一个指向文档对象的指针,这可通过GetDocument函数完成。//ASSERT_VALID()函数则用于确保一定能获取该指针,这是VisualC++的技术,如果不使用这一函数,可能会产生错误信息。Invalidate()函数使客户区内容失效,强迫程序调用OnDraw()函数,重新绘制视图。

(4)用OnDraw()函数重新绘制客户区。视图对象管理程序客户区,对客户区的绘制工作由成员函数OnDraw()完成。几乎所有应用程序的绘制工作都通过这一函数实现,编程时必须修改这个函数。

在项目工作区中单击类视图中CEx02View前面的“+”将其展开,双击OnDraw()函数,打开CEx02View.cpp编辑窗口,光标位于OnDraw()函数中。按下述内容对OnDraw函数进行编辑:voidCEx02View::OnDraw(CDC*pDC){ CEx02Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc); //TODO:adddrawcodefornativedatahere

pDC->TextOut(0,0,pDoc->m_String);//输入本行}

这里使用了CDC类的成员函数TextOut()函数,该函数用于在CDC设备类中显示字符串。它有三个参数,前两个参数用于指示字符串显示的相对位置,第三个参数用于传递要显示的字符串。现在编译运行这个程序。在“文件”菜单中选择Demo菜单项,窗口中显示“您选择了Demo菜单项。”的信息,如图2.8所示。建立新菜单的方法与添加菜单项类似,首先编辑菜单资源,然后为相应的菜单项建立消息映射,编写消息处理函数。在资源编辑器中建立新菜单的一般步骤如下:图2.8Ex02程序运行的结果(1)打开菜单编辑器,可以看到在菜单的最右边有一个空白的菜单项,如图2.9所示。

(2)编辑该空白菜单项(双击它,出现对话框),将其命名为NewMenu,如图2.10所示。可以看到,Pop-up复选框是被选中的,表示本菜单带有弹出式子菜单,因此无须定义本菜单的ID。

(3)关闭MenuItemProperties对话框,NewMenu菜单下方已经自动增加了一个空白的菜单项,如图2.11所示。用户可以通过编辑该空白菜单项,向NewMenu菜单添加菜单项。图2.9菜单编辑器

图2.10NewItemProperties对话框图2.11为NewMenu添加菜单项(4)向NewMenu菜单中添加两个菜单项New1和New2,并将其ID分别命名为ID_NEWMENU_NEW1和ID_NEWMENU_NEW2,如图2.12所示。在Windows中常常可以看到,点击某个菜单项还可以显示下一级子菜单。例如为NewMenu的New2添加子菜单Sub1和Sub2的步骤如下:

(1)在菜单编辑器中双击New2菜单项,打开MenuItemProperties对话框,如图2.13所示。

(2)选中对话框中的Pop-up复选框,此时ID下拉列表框中的ID_NEWMENU_NEW2自动消失。关闭该对话框后,在菜单项New2右边出现一个黑色的三角和一个空的子菜单,如图2.14所示。图2.12新菜单建立完成图2.13MenuItemProperties对话框图2.14New2带有下一级子菜单(3)编辑New2的空白子菜单,添加Sub1和Sub2两个菜单项,如图2.15所示。并将它们的ID分别命名为ID_NEWMENU_NEW2_SUB1和ID_NEWMENU_NEW2_SUB2。无论菜单项在哪一级菜单中,建立它们的命令处理函数的方法都是一样的。因为所有的菜单项都有一个唯一的ID标识,菜单项所处的位置不影响它们的处理函数的编写。图2.15为NewMenu添加子菜单

2.3快捷键和加速键

1.快捷键“文件”菜单中的“F”带有下划线,这表示用户可以通过按下Alt+F打开“文件”菜单,而无须用鼠标选取。F称为“文件”菜单的快捷键。增加快捷键非常简单,只要在给定菜单名或菜单项名中的某个位置多写一个“&”字符,就把该字符后面的那个字符定义成了快捷键。比如把NewMenu中的字母Mp定义为快捷键,只需将菜单名设置为New&Menu即可,如图2.16所示。图2.16增加快捷键2.加速键加速键是用于某个菜单项的控制键,用户可以通过按这些键直接打开相应的菜单选项,而无须打开菜单选取。例如,在Windows中,按Ctrl+V与“编辑”菜单中的“粘贴”选择是相同的。注意,只有菜单项才有加速键。下面是向NewMenu菜单中的New1菜单项增加加速键Ctrl+F1的步骤:

(1)在项目工作区的资源视图中双击Accelerator图标。

(2)双击IDR_MAINFRAME图标,打开加速键编辑器,如图2.17所示。

(3)双击加速键编辑器中的最后一个空白项,打开AccelProperties对话框,如图2.18所示。图2.17加速键编辑器图2.18AccelProperties对话框(4)在ID下拉列表框中选择New1菜单项的ID,即“ID_NEWMENU_NEW1”。

(5)在Key下拉列表框中选择VK_F1,关闭对话框,把Ctrl+F1加入到New1菜单项中。

(6)最后还要将New1的标题改为“New1Ctrl+F1”。

2.4工具栏和状态栏

1.工具栏工具栏显示一组按钮,每个按钮对应一个菜单项。工具栏按钮是方便直观的用户界面,用户可以直接点击某个按钮,其作用和选取相应菜单选项是相同的。可以把任何菜单项连接到工具栏中的按钮。以“文件”菜单中的Demo菜单项为例,将其添加到工具栏的步骤如下:

(1)在项目工作区的资源视图中,双击Toolbar图标。

(2)双击IDR_MAINFRAME图标,打开工具栏编辑器,同时在它的右边出现一组工具栏绘图工具,如图2.19所示。图2.19工具栏编辑器(3)选取工具栏编辑器中工具栏最右边的空白按钮,使用绘图工具在新按钮中画出如图2.19所示的图形。

(4)双击工具栏编辑器中编辑好的新按钮,打开ToolbarButtonProperties对话框,如图2.20所示。

(5)在ID下拉列表框中选择ID_FILE_DEMO,新的按钮已经连接到Demo菜单项上。运行程序,单击新按钮,可以看到窗口中显示出与选择Demo菜单项相同的信息,如图2.21所示。

2.状态栏状态栏用于显示某些提示信息。当用户把鼠标移到某个菜单项或工具栏按钮上时,状态栏中通常会显示一些简单的信息,提示用户该选项可以做哪些操作。图2.20ToolbarProperties对话框图2.21添加了工具栏按钮的应用程序

增加工具栏按钮的状态栏提示,只需在图2.20所示的ToolbarProperties对话框的Prompt文本框中放置相应信息即可。其操作步骤如下:

(1)打开需要增加提示的按钮的ToolbarProperties对话框。这里为Demo按钮增加提示信息,如图2.22所示。

(2)在Prompt文本框中输入这个按钮的说明:“在窗口显示相关的信息\n演示信息”,这意味着当用户将鼠标移到Demo按钮上时,状态栏中将显示“在窗口显示相关信息”,而在鼠标旁边将显示“演示信息”。再次运行程序,看一看当鼠标移到Demo按钮上时,是否会出现如图2.23所示的提示信息。图2.22为工具栏按钮增加提示信息图2.23添加了提示信息的工具栏按钮3单文档与多文档程序教学目标:了解文档/视图的模式,掌握单文档和多文档主要区别。能够开发简单的单文档应用程序,进一步强化应用前面所学的知识。重点、难点:重点:文档对象与视图对象之间的关系。难点:对文档/视图模式的理解。教学方法:以演示为主,辅以实验教学。教学内容:3.1文档—视图结构使用MFCAppWizard创建应用程序,首先要求用户确定应用程序的基本结构:单文档(SDI)、多文档(MDI)或基于对话框的应用程序界面。其中单文档或多文档创建的应用程序是基于文档的应用程序。创建基于文档的应用程序一般需要经过以下几步:①为每一种类型的文档定义一个派生自CDocument类的派生类;②添加用于存储文档数据的成员变量;③编写用于实现读取和修改文档数据的函数成员。

在MFC中,用文档—视图结构可将数据从用户对数据的观察中分离出来。文档用来管理应用程序的数据,视图用于显示文档并管理与用户的交互过程。实际上,视图在用户与文档之间起桥梁作用,如图3.1所示。MFCAppWizard创建的文档是从CDocument类派生的,视图类是从CView类派生的。

CDocument类为应用程序定义的文档类提供基本功能,而CView类为应用程序定义的视图类提供基本功能。视图和文档连接在一起,在文档与用户间起中介作用。视图在屏幕上显示文档数据并把用户输入转换成对文档的操作。

图3.1文档—视图之间的关系

3.2单文档应用程序的建立上一节的例子通过菜单显示了一行字符,是通过菜单完成的,本节直接在应用程序的窗口中显示一些信息,如“VisualC++的单文档应用程序很简单”,按如下步骤进行操作:

(1)利用MFCAppWizard创建单文档的应用程序Ex03。

(2)在项目工作区的类视图中单击CEx03View类前面“+”,展开该类。

(3)双击OnDraw()函数名,打开Ex03View.cpp编辑窗口。

(4)对OnDraw()函数按如下内容编辑:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc);

CStringm_Message="VisualC++的单文档应用程序很简单";

pDC->TextOut(30,30,m_Message);}

现在编译运行程序。“VisualC++的单文档应用程序很简单”显示在程序窗口中,但是,一个好的程序必然少不了与用户之间的交互。在Windows中,用户最常用的输入工具无非是键盘和鼠标,如果要显示由键盘输入的字符串,应该如何处理呢?下面演示如何处理键盘和鼠标输入以及存储和读取文档数据。首先,建立键盘输入存储区,用于存储键盘输入的字符串。程序中的文档对象用于存储在视图中显示的数据,因此下面在文档类中创建键盘输入存储区。当用户击键时,把键入的数据加到一个字符串中,并在视图中显示这个字符串。下面修改CEx03Doc类,增加字符串数据成员m_Message:(1)在类视图中双击CEx03Doc图标,在Ex03Doc.h编辑窗口修改CEx03Doc的类声明:classCEx03Doc:publicCDocument{protected://createfromserializationonly CEx03Doc(); DECLARE_DYNCREATE(CEx03Doc)

CStringm_Message; //在此加入变量声明

……}(2)在文档类CEx03Doc的构造函数中,把这个字符串变量初始化为空字符串。在类视图中单击CEx03Doc图标前的“+”,展开该类。双击CEx03Doc()函数,在Ex03Doc.cpp编辑窗口修改CEx03Doc的构造函数:CEx03Doc::CEx03Doc(){ //TODO:addone-timeconstructioncodehere

m_Message="

"; //对m_Message变量初始化}

现在来处理键盘输入。键盘输入产生WM_CHAR消息,处理键盘输入就必须编写相应的消息处理函数。首先按照下面的步骤使用ClassWizard来完成消息的映射:

(1)按下Ctrl+W打开MFCClassWizard对话框,并选择MessageMaps选项卡。由于需要添加的消息处理函数属于CEx03View类,在Classname下拉列表框中选择CEx03View,如图10.25所示。

(2)在Messages列表框中选择WM_CHAR消息,并双击它(或单击AppFunction按钮),这时可以在Memberfunctions列表框中看到添加了这一OnChar函数。MFCClassWizard对话框的MessageMaps选项卡

现在可以通过OnChar函数来读取键盘输入并将读取的字符存入到m_Message变量中。在图10.25中按下EditCode按钮,编辑OnChar函数如下:voidCEx03View::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags){ CEx03Doc*pDoc=GetDocument();//获取文档对象指针

ASSERT_VALID(pDoc); //确保一定能获取该指针

pDoc->m_Message+=nChar;//将读取的字符加到m_Message中

Invalidate();

//强行调用OnDraw函数,重绘视图

CView::OnChar(nChar,nRepCnt,nFlags);}

最后,对OnDraw函数进行修改,去掉对m_Message的声明(已经在CEx03Doc类中声明过),使之成为:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc); //TODO:adddrawcodefornativedatahere

pDC->TextOut(30,30,pDoc->m_Message);}

编译运行该程序并键入某些字符,相应的字符将在窗口中显示出来。

视图中显示的文档数据通过文档的串行化方法来实现磁盘的存取。所谓文档的串行化,是指在文档打开时,能够自动把文档中的数据转换为文档类所支持的对象;在保存文档时,能够自动将文档类对象转换为文档数据格式。文档串行化方法是由CEx03Doc类中的Serialize函数完成的。在项目工作区的类视图中单击CEx03Doc前面的“+”,展开该类。双击Serialize函数,在代码编辑窗口对其编辑如下:voidCEx03Doc::Serialize(CArchive&ar){ if(ar.IsStoring()) ar<<m_Message; //存入文档

else ar>>m_Message; //读入文档}

现在运行这个程序。在窗口中输入“这是第一次的输入”,再选择“文件”菜单中的“保存”,在“文件名”编辑框中输入“1”。再运行一次,输入“这是第二次的输入”,将其存为“2”,如图所示。在Ex03程序中存储文件

现在可以使用“文件”菜单中的“打开”菜单项分别打开这两个文件,可以看到程序窗口中显示的字符串与存储的字符串完全一致。接下来看看如何处理鼠标输入。按照下面的步骤修改上面的程序,将允许用户通过在客户区的某个位置单击鼠标来确定新文本输入的位置。当用户输入文本时,将会在单击鼠标的位置开始输入;当用户用鼠标选择新位置时,清除字符串对象m_Message中的字符,以便接收新的字符串。由于鼠标左键被按下时,文本显示位置也随之变化,必须首先建立一个新的存储区,用以记录鼠标单击的位置。鼠标位置可以通过鼠标消息处理函数传递的参数获得。所有鼠标消息处理函数都传递两个参数:UINTnFlags,CPointpoint

其中,point参数是CPoint类的一个对象,用于存入鼠标当前位置。可以在程序视图类的头文件中创建一个CPoint类的对象,命名为m_Point,用于记录point参数。编辑Ex03View.h,修改CEx03View类的声明:classCEx03View:publicCView{protected://createfromserializationonly CEx03View(); DECLARE_DYNCREATE(CEx03View) CPointm_Point; //用户加入本行

……}

与m_Message对象一样,需要对m_Point做初始化工作,这一工作在视图类的构造函数CEx03View()中完成:CEx03View::CEx03View(){ //TODO:addconstructioncodehere

m_Point.x=0; //用户加入以下两行// m_Point.y=0;}

现在使用ClassWizard创建WM_LBUTTONDOWN消息(被鼠标左键单击)的处理函数,该函数记录鼠标新位置,并在该位置显示新输入的文本。步骤如下:

(1)按下Ctrl+W加速键,启动ClassWizard对话框,选择MessageMaps选项卡。

(2)在Classname下拉列表框中选择CEx03View,在Messages列表框中选择WM_LBUTTONDOWN消息。

(3)按下AddFunction按钮,这时可以在Memberfunctions列表框中看到WM_LBUTTONDOWN的处理函数已被创建,函数名为OnLButtonDown。编辑OnLButtonDown函数如下:voidCEx03View::OnLButtonDown(UINTnFlags,CPointpoint){ //TODO:Addyourmessagehandlercodehereand/orcalldefault

m_Point=point; //记录鼠标位置

CEx03Doc*pDoc=GetDocument();//定义pDoc使用其指向CEx03Doc对象

ASSERT_VALID(pDoc); pDoc->m_Message.Empty(); //清空字符串

Invalidate(); //强迫重新绘图

CView::OnLButtonDown(nFlags,point);}

新位置已经记录在m_Point中了。改写OnDraw函数,在新位置显示文本:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc); //TODO:adddrawcodefornativedatahere pDC->TextOut(m_Point.x,m_Point.y,pDoc->m_Message);}

运行程序。在客户区的任何位置单击鼠标左键,然后输入文本,新文本将会在该位置出现。程序中读者可能发现一个问题:输入字符或单击鼠标的时候,无法确认当前字符的输入位置。在Windows中,通常使用光标指示用户输入字符时显示的位置。下面介绍如何在程序中建立和使用光标。要建立一个新光标,必须确定光标的大小。通常,光标与当前字符高度相同,宽度为平均字符宽度的1/8。文本字符串大小的相关信息由MFC提供的结构类型TEXTMETRIC记录,只要说明该类型的一个变量,然后使用CDC类中的GetTextMetrics方法对其进行填充即可。TEXTMETRIC类型中包括的成员有:tmHeight(字符高度)、tmAveCharWidth(平均字符宽度)、thMaxCharWidth(最大字符宽度)、thWeight(磅数)、tmItalic(斜体)等等。上面程序建立和使用光标的步骤如下:(1)在视图类中建立指示光标是否已经建立的布尔变量m_Created和光标位置m_Pos:classCEx03View:publicCView{protected://createfromserializationonly CEx03View();DECLARE_DYNCREATE(CEx03View) CPointm_Point;

BOOLm_Created; //光标是否建立// CPointm_Pos; //光标位置//

……}(2)在视图构造函数中将m_Created初始化为False,表示尚未创建;将m_Pos初始化为0:CEx03View::CEx03View(){ //TODO:addconstructioncodehere m_Point.x=0; m_Point.y=0;

m_Created=FALSE; m_Pos=m_Point;}(3)使用GetTextMetrics方法获取当前字符的大小,并用CreateSolidCaret方法实际建立光标,编辑OnDraw函数:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc); //TODO:adddrawcodefornativedatahere

pDC->TextOut(m_Point.x,m_Point.y,pDoc->m_Message); if(!m_Created){TEXTMETRICmetric; pDC->GetTextMetrics(&metric); CreateSolidCaret(metric.tmAveCharWidth/8,metric.tmHeight); }}

创建光标后,使用SetCarePos设置光标位置,并使用ShowCaret在视图中显示光标,还需要将m_Created设置为TRUE,表示光标已被创建。重新修改OnDraw函数:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc); //TODO:adddrawcodefornativedatahere pDC->TextOut(m_Point.x,m_Point.y,pDoc->m_Message); if(!m_Created){ TEXTMETRICmetric; pDC->GetTextMetrics(&metric);CreateSolidCaret(metric.tmAveCharWidth/8,metric.tmHeight);

SetCaretPos(m_Pos); ShowCaret(); m_Created=TRUE; }}

现在编译并运行程序。可以看到光标在窗口的左上角闪动。但在新位置点下鼠标左键时,光标并没有跟着移动。

光标需要指示下一个字符的输入位置。因此,当用户输入字符时,光标应该随之移动而出现在字符串的尾部。可按下面的方法处理:

(1)首先计算字符串结尾的位置。通过使用GetTextExtent函数可以获得字符串的高度和长度。需要注意,这里的长度不是指字符串中字符的个数,而是字符串在视图中以像素为单位计算的长度。

(2)隐藏光标,将光标移动到字符串的尾部并再次显示它。修改OnDraw函数如下:voidCEx03View::OnDraw(CDC*pDC){ CEx03Doc*pDoc=GetDocument(); ASSERT_VALID(pDoc);//TODO:adddrawcodefornativedatahere pDC->TextOut(m_Point.x,m_Point.y,pDoc->m_Message); if(!m_Created){ TEXTMETRICmetric; pDC->GetTextMetrics(&metric); CreateSolidCaret(metric.tmAveCharWidth/8,metric.tmHeight); SetCaretPos(m_Pos); ShowCaret(); m_Created=TRUE; }CSizeStringSize=pDC->GetTextExtent(pDoc->m_Message);//字符串长度

m_Pos.x=m_Point.x+StringSize.cx;

//计算光标位置

m_Pos.y=m_Point.y;HideCaret(); //隐藏光标

SetCaretPos(m_Pos); //移动光标到新位置

ShowCaret(); //显示光标}(3)最后还应该考虑到,在程序窗口失去输入焦点时,需要隐藏光标,当窗口重新获得焦点时再次显示光标。程序窗口失去焦点和获得焦点的消息分别是WM_KILLFOCUS和WM_SETFOCUS。使用ClassWizard分别创建这两个消息的处理函数,并编辑它们如下:

voidCEx03View::OnKillFocus(CWnd*pNewWnd){

CView::OnKillFocus(pNewWnd); HideCaret();}voidCEx03View::OnSetFocus(CWnd*pOldWnd){

CView::OnSetFocus(pOldWnd); ShowCaret();}

再次编译运行这个程序,可以看到闪动的光标指示下一个字符的输入位置。当用户用鼠标在其它位置点击时,所输入的字符串消失,光标又在新位置闪动。

3.3多文档应用程序的建立单文档应用程序与多文档应用程序在外观上是有区别的。可以再次运行Ex01程序,将它与单文档应用程序相比较(如Ex02、Ex03)。多文档应用程序多出一个“窗口”菜单,并且在主窗口中可以同时打开多个子窗口。这些子窗口中的内容可以是不同的。利用MFCAppWizard生成单文档(SDI)应用程序时,只派生文档类、视图类、主窗口类和应用程序类。而生成多文档(MDI)应用程序时,它有一个主窗口和嵌在该窗口中的子窗口,所以具有CChildFrame类,而且每个窗口可以使用不同的文档模板。单文档应用程序的主窗口类派生自CFrameWnd类,而多文档应用程序的主窗口类则是CMDIFrameWnd类的派生类。

在文档模板方面,单文档应用程序的文档模板是CSingleDocTemplate类,多文档应用程序的文档模板则是CMultiDocTemplate类。CSingleDocTemplate类构造函数的用法与CMultiDocTemplate类相同,但从建立文档模板所给定的参数不难发现,单文档应用程序文档模板的框架窗口与其主框架窗口使用相同的类(CMainFrame),而多文档应用程序文档模板的文件框架窗口使用的是子窗口类(CChildFrame)。多文档应用程序中可以有多个子窗口,因此多文档应用程序中可以有多个文档模板。通过观察单文档和多文档应用程序的InitInstance()函数,可以明显看出它们之间的差别:

//单文档应用程序的InitInstance()函数BOOLCEx02App::InitInstance(){ …… //建立文档模板

CSingleDocTemplate*pDocTemplate; pDocTemplate=newCSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CEx02Doc), RUNTIME_CLASS(CMainFrame),//mainSDIframewindowRUNTIME_CLASS(CEx02View)); AddDocTemplate(pDocTemplate);…… //只有一个窗口

m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); returnTRUE;}//多文档应用程序的InitInstance()函数BOOLCEx01App::InitInstance(){ …… //建立文档模板CMultiDocTemplate*pDocTemplate; pDocTemplate=newCMultiDocTemplate( IDR_EX01TYPE, RUNTIME_CLASS(CEx01Doc), RUNTIME_CLASS(CChildFrame),//customMDIchildframe RUNTIME_CLASS(CEx01View)); AddDocTemplate(pDocTemplate); //建立MDI主窗口框架CMainFrame*pMainFrame=newCMainFrame; if(!pMainFrame->LoadFrame(IDR_MAINFRAME)) returnFALSE; m_pMainWnd=pMainFrame; …… //显示主窗口

pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow();

returnTRUE;}

下面是建立一个多文档应用程序的例子。操作如下:

(1)利用MFCAppwizard创建多文档的应用程序Ex04。

(2)在项目工作区的类视图中双击CEx04Doc图标,出现类编辑

温馨提示

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

评论

0/150

提交评论