VC++ 入门编程教程

wz3838

贡献于2012-11-25

字数:311991 关键词: C/C++开发 C/C++

 第1章 Visual C++程序的建立 VISUAL C++程序的建立 4 1.1 C程序和C++程序 4 1.2 面向对象的编程技术 7 1.2.1 类与对象 7 1.2.2 类及其成员变量、成员函数的声明和定义 8 1.2.3 构造函数和析构函数 10 1.2.4 类的继承 12 1.2.5 C++在非面向对象方面的扩充 15 1.3 Visual C++程序 19 1.4 使用MFC AppWizard应用程序向导 21 1.4.1 应用程序框架类型 21 1.4.2 用MFC AppWizard(exe)创建一个单文档的应用程序 22 1.4.3 项目工作区 26 1.4.4 输出窗口 29 1.5 ClassWizard类向导 29 1.5.1 ClassWizard的使用 29 1.5.2 消息和消息映射 30 1.5.3 消息映射方法实例 35 1.6 章后实训 44 实训1 键盘字符输入,并使输入的文本居中 44 实训2 向窗口中添加一个闪亮的插入符 47 实训3 制作一个每次单击窗口都出现不同鼠标光标图形的程序 50 菜单、工具栏和状态栏的设计 53 2.1 设计菜单 53 2.1.1 用编辑器设计菜单 53 2.1.2 菜单的编程控制 60 2.1.3 使用快捷菜单 64 2.2 工具栏 67 2.2.1 使用工具栏编辑器 67 2.2.2 多个工具栏的使用 69 2.3 状态栏 74 2.3.1 状态栏的定义 74 2.3.2 状态栏的常用操作 75 2.4 交互对象的动态更新 80 2.5 章后实训 82 实训1 通用菜单 82 第1章 Visual C++程序的建立 实训2 多信息状态栏 88 实训3 自定义工具条 95 对话框与控件 98 3.1 对话框的使用 98 3.4.1 控件的创建方法 109 3.4.2 基于对话框的应用程序 110 3.4.3 控件的消息及消息映射 111 3.4.4 控件的数据交换和数据效验 115 3.4.5 控件的通用属性 117 3.5.1 静态控件 118 3.5.2 按钮控件 121 3.5.3 编辑框控件 129 3.5.4 列表框 136 3.5.5 组合框 143 3.5.6 旋转按钮控件 148 3.5.7 进展条 154 3.5.8 滚动条 161 3.5.9 滑动条 166 3.6.1 标签控件 172 3.6.2 图像列表控件 178 3.6.3 属性表及属性页 179 3.9.1 通用对话框 189 3.9.2 消息对话框 190 实训1 计算器应用程序 192 实训2 对话框与控件的综合运用 199 实训3 更改字体设置 205 第1章 Visual C++程序的建立 C++面向对象程序设计语言是在C语言的基础上发展起来的,它与传统的程序设计方式不同,是一种新的程序设计范型。它对降低软件的复杂性,改善其重要性和维护性,提高软件的生产效率,有着十分重要的意义。因此面向对象程序设计被普遍认为是程序设计方法的一场实质性的革命。 第1章 Visual C++程序的建立 Visual C++支持面向对象程序设计,是Microsoft公司推出的目前应用最为广泛的基于Windows平台的可视化编程工具。 使用Visual C++的强大功能,可以开发Windows应用程序,设计完成色彩亮丽的可移动的图形图像及千变万化的文字信息和广泛流行的企事业管理、银行、电信、商业、交通、航空航天、教育、游戏等众多的实用软件。 1.1 C程序和C++程序 只有在具备C和C++编程基础后,才能更好地运用Visual C++编程工具开发Windows应用程序。下面先用C语言、C++语言编写一个同样的显示学生信息的程序,来熟悉一下它们的编程方法。特别是通过C++程序设计,进一步掌握面向对象的编程技术,这将对学好可视化的Visual C++程序设计起到促进作用。 [例1.1]用C语言编写显示学生信息程序 将Visual C++6.0软件安装到本机器后,创建其桌面快捷方式。在常用的e盘上建一个文件夹名为:vcpp。 双击桌面Visul C++6.0快捷方式图标,在打开的窗口中选择File→New命令,在弹出的对话框中打开File选项卡,选择C++source File选项,然后在右边File处写要建的文件名:学生信息1,在其下面的Location即路径名处写:e:\vcpp,然后单击OK按钮,如图1.1所示,在出现的界面(文档窗口)上可输入C程序,之后在弹出的对话框中依次单击Build→Build→是→是→!(编译运行)按钮,便出现图1.2的结果。输入的C程序如下: #include #include struct student //定义student结构体类型 { long num; //学号 char name[20]; //年龄 char sex[10]; //性别 float score; //成绩 }stu,*p; //直接定义student结构体类型变量stu和student结构体类型指针*p int main() { p=&stu; //student结构体的起始地址赋给指针变量p stu.num=89101; //将学号赋给stu结构体变量中的成员num中 strcpy(stu.name,"李明"); //将姓名赋给stu结构体变量中的成员name strcpy(stu.sex,"男"); //将姓别赋给stu结构体变量中的成员sex stu.score=89.5; //将成绩赋给stu结构体变量中的成员score //以下两个printf函数输出的结果是相同的 printf("学号 No.:%ld\n姓名 name:%s\n性别 sex:%s\n成绩 score:%2.1f\n", stu.num,stu.name,stu.sex,stu.score); //stu.num表示stu结构体变量中的成员num printf("\n学号 No.:%ld\n姓名 name:%s\n性别 sex:%s\n成绩 score:%2.1f\n", 第1章 Visual C++程序的建立 p→num,p→name,p→sex,p→score); //p→num是代替(*p).num的书写形式,也就是p→num //等价于(*p).num,它表示p指向结构体变 //量stu中的成员num return 0; } 图1.1 建立C语言程序对话框 图1.2 C语言程序运行结果 [例1.2]用C++编写显示学生信息程序 (1)双击桌面Visul C++6.0快捷方式图标,在打开的窗口中选择File→New命令,在弹出的对话框中打开projects选项卡,选择Win32 Console Applicator选项,然后在Project name处写文件名:学生信息2,在Location处写e:\vcpp,然后在弹出的对话框中依次单击OK→Finish→OK按钮。 (2)再在主菜单处选择File→New命令,在弹出的对话框中打开File选项卡,选择C++ Source选项,右边Add to Project要置于选中状态,并显示出刚才输入的文件名然后在File处写:学生信息2.cpp,单击OK按钮,如图1.3所示。在出现的窗口界面(文档窗口)中输入C++程序。再依次单击Build→Build(编译运行)按钮,结果如图1.4所示。输入的C++程序如下: #include #include //类的说明部分 class student //定义类(class) student { private: //私有部分 public: //公共部分 long num; //学号 char name[20]; //姓名 char sex[10]; //性别 float score; //成绩 void sc(); //输出函数 protected: //保护部分 }; 第1章 Visual C++程序的建立 //类的实现部分 void student::sc() //在student类里面定义的函数sc()的具体实现 { student stu; student *p=&stu; stu.num=89101; strcpy(stu.name,"李明"); strcpy(stu.sex,"男"); stu.score=89.5; //以下两种输出结果都是相同的 cout<<"学号 No: "<num<<"\n"; cout<<"姓名 name: "<name<<"\n"; cout<<"性别 sex: "<sex<<"\n"; cout<<"成绩 score: "<score<<"\n\n"; } //主函数体 void main() //主函数,程序从这里开始执行 { student xx; //定义student类的对象xx xx.sc();//sc()是xx(student)的成员函数,xx.sc()是表示访问xx的成员函数sc() } 可以看到,C语言是先定义结构体和结构体变量、结构体指针,之后在主函数main()里用结构体变量和结构体指针输出其变量内容。C++是先定义类,在主函数里用类定义对象,再通过对象里的成员函数输出其变量内容。C和C++的最大区别是C++定义的类里可以有成员函数,通过成员函数完成所要完成的工作,在程序结构上有了深刻变化,是面向对象的。而C的结构体里不能定义函数,它不具备类的功能,程序是结构化的、面向过程的。 图1.3 建立C++程序对话框 图1.4 C++程序运行结果 第1章 Visual C++程序的建立 1.2 面向对象的编程技术 面向对象包括面向对象分析和面向对象程序设计。面向对象程序设计技术的提出,主要是为了解决传统的结构化程序设计所不能解决的代码重用问题。 1.2.1 类与对象 前面在例1.2程序中已经定义了类student和其对象xx。“类”就是对具有相同数据和相同操作的一组相似对象的定义,即类是对具有相同特征和行为的一个或多个对象的描述。在面向对象程序设计中,“类”是一个最重要的概念。 类的结构(也即类的组成,如例1.2中的:学号num、姓名name、性别sex、成绩score、输出函数sc())是用来确定一类对象的行为的,而这些行为是通过类的内部数据结构和相关操作来确定的。类通过其成员函数(如例1.2中的sc())来描述这些行为,而成员函数又被称为类的对象向其他对象所提供的服务。 与类对应的就是对象了。对象就是由某个特定的类所描述的一个具体的实例。例如,“车”是一个类,一种具体的车如“货运车”就是一个具体的对象了。对象是类的实例,类是具有公共特性的对象的抽象。 对象的定义:一个类定义后,就可以定义该类的对象,如:例1.2中的student xx; 其定义的格式为:<类名> <对象表名>; 其中,类名是用户定义过的类的标识符,对象名可以有一个或多个,多个时要用逗号分隔。被定义的对象即可以是一个普通对象,也可以是一个数组对象或指针对象。如: CMeter myMeter, *Meter, Meters[5]; 这时,myMeter是类CMeter的一个普通对象,Meter和Meters分别是类的一个指针对象和对象数组。 一个对象的成员就是该对象的类所定义的数据成员(成员变量)和成员函数。访问对象的成员变量、成员函数和访问变量和函数的方法是一样的,只不过要在成员面前加上对象名和成员运算符“.”,例如:例1.2中的xx.sc()。具体表示方式如下: < 对象名 >.< 成员变量 > < 对象名 >.< 成员函数>(<参数表>) 例如:myMeter.m_nPercent,myMeter.SetPos(5),Meters[0].StepIt(); 需要说明的是,一个类对象只能访问该类的公有型成员,而对于私有型成员则不能访问。上述成员m_nPercent、SetPos()、StepIt()都是public(公有)访问类型。 若对象是一个指针,则对象的成员访问形式如下: <对象指针名>-><成员变量> <对象指针名>-><成员函数>(<参数表>) 例如(见例1.4所示): Date *datel; // 定义Date类的指针对象 datel=new Date(1998,4,28); // Date()是类Date中定义的构造函数,这里是执行构造函数, 第1章 Visual C++程序的建立 //并为类Date开辟一片内存单元,且将这片内存单元的首地址赋给指针变量datel datel→showDate(); //访问Date类的成员函数 “→”是一个表示成员的运算符,它与“.”运算符的区别是:“→”用于表示指向对象的指针的成员,而“.”用于表示一般对象的成员。 1.2.2 类及其成员变量、成员函数的声明和定义 类的定义格式一般可分为说明部分和实现部分。说明部分用来说明该类中的成员,包含数据成员的说明和成员函数的说明。成员函数是用来对数据成员进行操作的,又称为“方法”。实现部分是对成员函数的定义。概括来说,说明部分是告诉使用者“做什么”,而实现部分则告诉使用者“怎么做”。类的一般定义格式如下: class <类名> { public: 公有数据成员的说明; 公有成员函数的说明; protected: 保护数据成员的说明; 保护成员函数的说明; private: 私有数据成员的说明; 私有成员函数的说明; }; < 各个成员函数的实现 > class是定义类的关键字,<类名>是用户定义的类的名字,通常用大写的C字母开始的标示符作为类名。花括号{ }内是类的说明部分,说明该类的成员。类的成员包含数据成员和成员函数两部分。public称为类的公有部分,这部分的数据成员和成员函数可由程序中的函数(包括类内和类外)访问,即它对外是完全开放的。private称为类的私有部分,这一部分的数据成员和成员函数只能由本类的成员函数访问,而类外部的任何访问都是非法的。实现了访问权限的有效控制。protected称为类的保护部分,这部分的数据成员和成员函数可以由本类的成员函数访问,也可以由本类的派生类的成员函数访问,而类外的任何访问都是非法的。即它是半隐蔽的。 关键字public、private和protected被称为访问权限修饰符或访问控制修饰符。它们在类体内(即花括号{ }内)出现的先后顺序不限,并且允许多次出现,可用它们来说明类成员的访问权限。 <各个成员函数的实现>是类定义中的实现部分,这部分包含所有在类体内说明的函数的定义,如例1.2中的void student::sc(); 如果一个成员函数在类体内定义了,则实现部分将不再出现。如果所有的成员函数都在类体内定义,则实现部分可以省略。需要说明的是,当类的成员函数的函数体在类的外部定义时,必须由作用域运算符“:: ”来通知编译系统该函数所属的类。 第1章 Visual C++程序的建立 为了进一步明确类的定义,除了例1.2外,下面再给出一个日期类定义的例子。 [例1.3]定义日期类 #include //类的说明部分 class Date { public: //公有部分 void SetDate(int y,int m,int d);//说明为year、month、day设置初值的函数 int IsLeapYear(); //说明判别是否是闰年函数 void Print(); //说明输出函数 private: //私有部分 int year,month,day; //说明用来表示年、月、日的整型变量 }; //类的实现部分 void Date::SetDate(int y,int m,int d) //为year、month、day设置初值函数的实现 { year = y; //年 month = m; //月 day = d; //日 } //判别是否是闰年函数的实现,能被4整除与不能被100整除或能被400整除 int Date::IsLeapYear() { return((year%4 == 0 )&& ((year%100)!= 0)||(year%400 == 0)); } void Date::Print() //输出函数的实现 { cout<>”可以连续写多个,每个后面跟一个表达式,该表达式通常是获得输入值的变量或对象。若程序中有如下语句: int nNum1,nNum2,nNum3; cin>>nNum1>>nNum2>>nNum3; 这两条语句是定义3个整型变量,并要求用户从键盘上输入3个数。输入时,必须在3个数值之间加上一些空格来分隔,空格的个数不限,最后按回车键结束输入。 1.2.3 构造函数和析构函数 构造函数和析构函数是在类体中说明的两种特殊的成员函数。构造函数的功能是在创建对象时,使用给定的值将对象初始化;析构函数与构造函数正好相反,其功能是释放一个对象,在删除对象前,用它来做一些内存释放等清理工作。 1. 构造函数 由于在类的定义中不能对数据成员进行初始化,为了能给数据成员自动设置某些初始值,需要使用构造函数。构造函数具有以下特征。 (1)构造函数的名字与类名相同,否则编译程序将把它当作一般的成员函数来处理。 (2)构造函数没有返回值。 (3)构造函数的功能是对对象进行初始化,且一般只对数据成员进行初始化。在构造函数中一般不做赋初值以外的事情。 (4)构造函数不能像其他成员函数那样被显示地调用,它是在对象创建时被调用的。 (5)在一个类中可以定义多个构造函数。 [例1.4]定义构造函数 #include class Date { public: Date(int y,int m,int d); //构造函数 第1章 Visual C++程序的建立 void setDate(int y,int m,int d);//一般的成员函数,设置日期函数 void showDate();//一般的成员函数,显示日期函数 private: int year; //私有成员变量,年 int month; //私有成员变量,月 int day; //私有成员变量,日 }; Date::Date(int y,int m,int d) //构造函数的实现 { cout<<"Constructing Function "<showDate(); //访问显示成员函数,显示构造函数所赋的年、月、日值 datel->setDate(2008,10,22); //访问设置日期函数,参数是年、月、日 cout<<"Datel output2:"<showDate(); //访问显示成员函数,显示setDate()函数所赋的年、月、日值 delete datel; //释放datel所占用的内存空间 } 2. 析构函数 与构造函数相对应的是析构函数。析构函数是另一个特殊的C++成员函数,它只是在类名称前面加上一个“~”符号。每一个类只有一个析构函数,没有任何参数,也不返回任何值。例如:class CMeter { public: …… 第1章 Visual C++程序的建立 ~ CMeter() { } //析构函数 …… } 析构函数只有在下列两种情况下才会被自动调用: (1)当对象定义在一个函数体中,该函数调用结束后,析构函数被自动调用; (2)用new为对象分配动态内存,当使用delete释放对象时,析构函数被自动调用。 3. 默认构造函数和析构函数 在类定义时,如果没有定义任何构造函数,则编译器自动生成一个函数不带参数的默认构造函数。同样,如果一个类中没有定义析构函数时,则编译系统也生成一个函数,称为默认析构函数。例如,前面定义的CMeter类来说,当没有定义任何构造函数和析构函数时,系统自动生成的默认构造函数和析构函数如下所示: CMeter()//默认构造函数的形式 { } ~ CMeter() //默认析构函数的形式 { } 需要说明的是,在用户定义一个对象时,编译器会自动根据对象定义的格式选择相应的构造函数。例如:CMeter m1,m2; 由于m1和m2不带任何参数,当类没有用户定义的构造函数时,则编译器会使用默认构造函数对m1和m2进行初始化。用默认构造函数对对象进行初始化,则将对象的所有数据成员都初始化为零或空。 1.2.4 类的继承 继承是面向对象程序设计的一个重要特性,它允许在已有类的基础上创建新的类,新类可以从一个或多个已有类中继承成员函数和数据成员,而且可以重新定义或加进新的数据和函数,从而形成类的层次或等级。其中已有类称为基类或父类,在它基础上建立的新类称为派生类、导出类、子类。 1. 继承与派生类 为什么要使用派生类? 例如有类:class CPerson { private: public: CPerson(); //构造函数 protected: char name[10]; //姓名 char sex[4]; //性别 }; 再如此时又要声明一个类employee类,它也包含有char name[10](姓名)、char sex(性别)。还包含char department(部门)、float salary(工资)等数据成员和显示函数show()。 class CEmployee 第1章 Visual C++程序的建立 { private: public: void show(); //显示函数 protected: char name[10]; //姓名 这条重复 char sex[4]; //性别 这条重复 char department[20]; //部门 char pos[10]; //职务 }; 两个类中的代码出现像这样重复的现象,必须引入继承,将CEmployee类说明成CPerson类的派生类,那些相同的成员在CEmployee类中就不需要再说明了。 2. 派生类的声明 将上面的CPerson类定义好后,再定义其派生类CEmployee 如: class CEmployee : public CPerson // CEmployee是派生类,public表示公共继承,CPerson是基类 { private: public: void show(); //显示函数 和CPerson类不重复的语句 protected: char department[20]; //部门 和CPerson类不重复的语句 char pos[10]; //职务 和CPerson类不重复的语句 } 这样CEmployee类即具有了CPerson类的功能(姓名、性别),又增加了具有“部门”、“职务”和“显示函数”的功能。下面是声明一个派生类的格式: class 派生类名 :继承方式 基类名 { 派生类新增的数据成员和成员函数 }; 继承方式: (1)公有继承:class employee : public person{ }; (2)私有继承:class employee : private person{ }; (3)保护继承:class employee : protected person{ }; 类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。 从已有类派生出新类时,可以在派生类内完成以下几种功能: (1)可以增加新的数据成员和成员函数; (2)可以重新定义基类中已有的成员函数; (3)可以改变现有成员的属性。 [例1.5]派生类的公有继承示例 #include #include 第1章 Visual C++程序的建立 class CPerson { private: public: CPerson(); //构造函数 protected: char name[10]; //姓名 char sex[4]; //性别 }; //构造函数的实现,在主函数中定义对象时,该函数自动执行,为name和sex赋值 CPerson::CPerson() { strcpy(name,"李明"); //为name赋值 strcpy(sex,"男"); //为sex赋值 } //CEmployee类继承了CPerson类的所有功能,同时还增加了"部门"、"职务"和"输出函数"的功能 // CEmployee是派生类,public表示公有部分,CPerson是基类,该派生类中还具有了name和sex class CEmployee : public CPerson { private: public: void show(); //输出函数 和CPerson类不重复的语句 protected: char department[20]; //部门 和CPerson类不重复的语句 char pos[10]; //职务 和CPerson类不重复的语句 }; void CEmployee::show() //派生类CEmployee的输出函数的实现 { CEmployee cem; //定义派生类CEmployee的对象cem CEmployee *p=&cem; //对象的首地址赋给CEmployee类型的指针变量p cout<<"姓名:"<name<sex<department<pos<>x;此时用户从键盘输入的数值会自动地转换为变量x的类型,并存入变量x内。x必须是基本数据类型,但不能是void类型,它还允许一连串的输入数据,如cin>>a>>b>>c;按书写顺序从键盘上提取所要求的数据,并存入对应的变量中,两个数据间用空格分隔; cout< int main() { char name[20]; cout<<"输入你的名字:"; //在终端上输出字符串:输入你的名字: cin>>name; //在终端上输入姓名后,存入到变量name中 cout< void main() { int *p1,*p2,*p,a,b; cout<<"请输入两个数:"; //在终端上输出提示:请输入两个数: cin>>a>>b; //在终端上输入a和b两个数,输入时这两个数用空格分开 p1 = &a; //存放a值的地址赋给指针变量p1 p2 = &b; //存放b值的地址赋给指针变量p2 if(a class CStudent { public: CStudent (int zx) { x = zx; } void disp() { cout<<" x= "< main() { void *pc; //声明void(无类型或叫通用性)指针pc int i = 123; char c = ‘a’; pc = &i; //将存放整型数123的变量i的地址赋给void型指针pc cout << *(int *) pc << endl; //输出指针值123,要进行整型数类型转换 pc = &c; //将存放字符a的变量c的地址赋给void型指针pc cout << *(char *) pc < main() { int *p; //声明一个整型指针变量p p = new int; //动态分配一个存放int整型数据的内存空间,并将首地址赋给p *p = 10; //将整型数10送到new分配的内存空间 cout << *p< int avar; // 全局变量 main() { int avar; //局部变量 avar = 25; //25赋给局部变量avar ::avar = 10; //10赋给全局变量avar cout<<"local avar = "<< avar<TextOut(20,20,"学号 num: "); pDC->TextOut(110,20,num); pDC->TextOut(20,40,"姓名 name: "); pDC->TextOut(110,40,name);//输出姓名 pDC->TextOut(20,60,"性别 sex: "); pDC->TextOut(110,60,sex); //输出性别 pDC->TextOut(20,80,"成绩 score: "); pDC->TextOut(110,80,score);//输出成绩 } (4)选择Build→Build(编译运行)命令,便在窗口上显示出如图1.6所示的学生信息。 说明: ① OnDraw(CDC *pDC)函数是视图类中的显示函数(函数也叫对象方法)。 ② CDC类:是FMC的设备环境类,CDC *pDC是定义CDC类的指针对象*pDC。 ③ CMyDoc *pDoc:是定义文档类的指针对象*pDoc。 ④ GetDocument()函数是获得文档类指针。 ⑤ CMyDoc* pDoc = GetDocument():是将文档类首地址赋给文档类指针*pDoc。 第1章 Visual C++程序的建立 图1.6 Visual C++运行结果 ⑥ ASSERT_VALID(pDoc):该宏用来在运行时检查一个对象的内部合法性,这里是测试获得的文档类指针。 ⑦ CString:是处理文本串的类,它能将字符串处理为字符数组。 ⑧ TextOut(x,y,str):是CDC类的显示函数,参数x,y是字符串在窗口中的显示位置(x、y坐标),str是以上已定义和赋好值的字符串。 Visual C++编程方式与C和C++又有了很大不同,可以看到在程序建立后,系统自动生成一些类,这些类以文件的形式存在。如:视图类(学生信息3View.h、学生信息3View.cpp)、文档类(学生信息3Doc.h、学生信息3Doc.cpp)、主框架类(Mainfrm.h、Mainfrm.cpp)、应用程序类(学生信息3.h、学生信息3.cpp)等4个主要的类(见附录)。以后所有的单文档项目都要用到这4个主要的类,通过添加代码等,来完成工作。 1.4 使用MFC AppWizard应用程序向导 在1.3节中,已经用MFC AppWizard建立了一个学生信息3的应用程序,程序建立后,系统自动为其生成了一些类文件,有了这些文件,就可以不加任何代码运行程序;其结果是在屏幕上显示一个空白窗口。可以在这个空白窗口上完成任何工作(例1.3就是在这个空白窗口上显示学生信息的)。Visual C++ 6.0中的MFC AppWizard能快速、高效、自动地生成一些常用的标准程序结构和编程风格,它们被称为应用程序框架结构。 1.4.1 应用程序框架类型 在用MFC Windows建立一个单文档应用程序时,在开始的New对话框中就要选择最常用的MFC AppWizard(exe)应用程序框架类型。该类型是用于创建可执行的Windows应用程序,它包含用户最常用、最基本的以下3种应用程序类型。 (1)单文档应用程序:它的功能比较简单,复杂程度适中,虽然每次只能打开和处理一个文档,但已能满足一般工程上的需要。 (2)多文档应用程序:能允许同时打开和处理多个文档。与单文档应用程序相比,增加了许多功能,需要大量额外的编程工作。例如它不仅需要跟踪所有打开文档的路径,而且还需要管理各文档窗口的显示和更新等。 (3)基于对话框的应用程序:对比单文档更简单,也是最紧凑的。它没有菜单、工具栏及状态栏,也不能处理文档,但它的优点是速度快,代码少,程序员所花费的开发和调试的时间短。 总之,无论选择何种类型的应用程序框架,一定要根据自己的具体需要而定。表1.1列出了在Visual C++ 6.0中,MFC AppWizard创建的应用程序框架类型。这些类型基本上满足了各个层次用户的需要。 第1章 Visual C++程序的建立 1.4.2 用MFC AppWizard(exe)创建一个单文档的应用程序 [例1.14]创建一个“显示两行字”的单文档应用程序 (1)双击桌面上的Visual C++6.0快捷方式图标,选择File→New命令,在弹出的New对话框中,可以看到工程标签页面中显示出一系列的应用程序项目类型,表1.1列出了所有应用程序项目类型;选择MFC AppWizard(exe)项目类型(该类型用于创建可执行的Windows应用程序),将Location(项目工作文件夹)定位在e:\vcpp,并在Project name(工程名)中输入项目名:显示两行字,如图1.7所示。 表1.1 MFC AppWizard创建的应用程序框架类型 名 称 项 目 ATL COM AppWizard 创建ATL(Active Template Library)应用模块工程 Cluster Resource Type Wizard 创建ClusterResource(用于WindowsNT服务器) Custom AppWizard 创建自己的应用程序向导 Database Project 创建数据库应用程序 DevStudio Add-in Wizard 创建ActiveX组件或VBScript宏 Extended Store Proc Wizard 创建基于SQL服务器下的外部存储过程 ISAPI Extension Wizard 创建Internet Server程序 MakeFile 创建独立于VisualC++开发环境的应用程序 MFC ActiveX ControlWizard 创建ActiveXControl应用程序 MFC AppWizard(dll) MFC的动态连接库 MFC AppWizard(exe) 一般MFC的Windows应用程序 Utility Project 创建简单、实用的应用程序 Win32 Application 其它Win32的Windows应用程序 Win32 Console Application Win32的控制台应用程序 Win32 Dynamic-Link Library Win32的动态连接库 Win32 Static Library Win32的静态连接库 图1.7 MFC AppWizard的New(新建)对话框 第1章 Visual C++程序的建立 (2)单击OK按钮,弹出如图1.8所示的Step 1对话框,进行下列选择。 1)应用程序类型选择Single document(单个文档)、Multiple documents(多重文档)和Dialog based(基于对话框)。这里选中Single document(单选按钮)。 2)选中Document/View architecture support(文档/视图结构的支持)复选框,若不选中,则程序中的磁盘文件的打开、保存以及文档和视图的相互作用等功能需要用户来实现,且将跳过Step2~Step 5,直接弹出Step 6对话框。 图1.8 MFC AppWizard的Step 1对话框 3)选择资源所使用的语言,这里是“中文[中国](APPWZCHS.DLL)”。 (3)单击Next按钮,弹出如图1.9所示的Step 2 of 6对话框,让用户选择程序中是否加入数据库的支持(有关数据库编程以后章节介绍),这里默认选中None单选按钮。 (4)单击Next按钮,弹出如图1.10所示的Step 3对话框。允许用户在程序中加入复合文档、自动化、ActiveX控件的支持。默认选择,单击Next按钮进入下一步。 (5)弹出如图1.11所示的Step 4 of 6对话框,对话框的前几项依次确定对浮动工具条、打印与预览以及通信等特性的支持。表1.2列出了各个选项的含义。 图1.9 MFC AppWizard的Step 2对话框 第1章 Visual C++程序的建立 图1.10 MFC AppWizard的Step 3对话框 图1.11 MFC AppWizard的Step 4对话框 表1.2 SDI第4步对话框各项的含义 项 目 各个选择 默认设置 含 义 特性 Docking toolbar 选中 使工具栏具有“浮动”和“停泊”特征 Initial Status bar 选中 对状态栏进行初始化 Printing and print preview 选中 具有“打印与预览”功能 Context-sensitive Help 不选中 上下文帮助,在应用程序的“帮助”菜单中增加许多与帮助相关的选项 3D controls 选中 3D控件,与Windows95控件风格相同 MAPI(Messaging API) 不选中 MAPI标准接口,用以处理电子邮件的信息、声音邮件及传真数据 Windows Sockets 不选中 TCP/IP等网络 工具栏 外观 Normal 选中 普通的外观 Internet Explorer ReBars 与IE4.0相似的外观 对话框的最后两项是最近文件列表数目的设置(默认为4)和一个Advanced按钮。单击Advanced按钮将弹出一个对话框,允许用户对文档及其扩展名、窗口风格进行修改。仍然默认 第1章 Visual C++程序的建立 选择,单击Next按钮进入下一步。 (6)在弹出的Step 5 of 6对话框中,如图1.12所示,出现3个方面的选项供用户选择: 1)应用程序的主窗口是MFC标准风格还是窗口左边有切分窗口的浏览器风格; 2)在源文件中是否加入注释用来引导用户编写程序代码; 3)使用动态链接库还是静态链接库。 默认选择,单击Next按钮进行下一步。 (7)弹出如图1.13所示的Step 6 of 6对话框。用户可以在该对话框上对MFC AppWizard提供的默认类名、基类名、各个源文件名进行修改。 单击Finish按钮,弹出如图1.14所示的对话框,显示出用户在前面几个步骤中选择的内容。单击OK按钮,系统开始创建,并又回到了Visual C++6.0界面。 图1.12 MFC AppWizard的“Step 5”对话框 图1.13 MFC AppWizard的“Step 6”对话框 (8)双击项目工作区FileView,打开“显示两行字files”,打开“Header Files”,双击“显示两行字Doc.h(文档类头文件)”,在右边窗口显示出的“显示两行字Doc.h”文件中的public下添写代码: CString str1; //用于存放第一行字符串 CString str2; //用于存放第二行字符串 (9)再双击项目工作区的:显示两行字Doc.cpp(文档类实现文件),在其构造函数中添写如下代码,如图1.15所示。 CMyDoc::CMyDoc() { str1=" 努力学好Visual C++,为发展我国的软件业做出贡献!"; str2=" 中国改革开放的31年颂歌!"; } (10)再双击项目工作区的“显示两行字View.cpp(视图类实现文件)”,在其OnDraw函数中添写如下代码,如图1.16所示。 void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDC->TextOut(20,100,pDoc->str1); pDC->TextOut(20,120,pDoc->str2); } 第1章 Visual C++程序的建立 图1.14 项目信息对话框 图1.15 在文档类的构造函数中添代码图 图1.16 在“显示两行字View.cpp”文件中添写代码 (11)编译并运行。单击编译工具栏上的“运行”工具按钮或按Ctrl+F5组合键,系统开始编译并运行,运行结果如图1.17所示。 可以看到,“学生信息3”和“显示两行字”这两个程序,在定义变量时用的方法不同。“学生信息3”是在视图类的实现文件中直接定义变量并赋值,直接用TextOut函数输出。而“显示两行字”程序是在文档类头文件即“显示两行字Doc.h”中定义变量,在文档类实现文件即“显示两行字Doc.cpp”中的构造函数里赋初值,在视图类的实现文件即“显示两行字View.cpp”中的OnDraw函数里用文档类指针pDoc指向变量,输出变量内容。注意,在文档类定义变量,在视图类输出,一定要用指针完成,视图类OnDraw函数中的:CmyDoc * pDoc = GetDocument();语句是系统自动生成的,它就是将文档类的首地址赋给pDoc,直接用pDoc指向文档类中的变量,就能完成在视图类中输出文档类中变量的值的目的。 1.4.3 项目工作区 项目工作区窗口如图1.16所示,包含用户项目的一些信息,如类、项目文件以及资源等。在项目工作区窗口中的任何标题或图标处右击,都会弹出相应的快捷菜单,包含当前状态下的一些常用操作。 1. FileView页面 FileView将项目中所有文件分类显示,每一类文件在FileView页面都有自己的目录项(节点)。 第1章 Visual C++程序的建立 图1.17 显示两行字应用程序运行结果 (1)应用程序类对象:CMyApp.h(头文件)、CMyApp.cpp(实现文件) 应用程序类主要用于应用程序对象的初始化和运行,它实际上是Windows最先运行的,当这个程序开始时,它将会把主窗口放在屏幕上。C或C++是从main()主函数开始执行,而Visual C++是从WinMain()函数开始执行,且WinMain()函数就是隐含在此应用程序中。应用程序类对象CMyApp的基类是CWinApp,而CWinApp的基类又是CWinThread。CWinThread类是用来完成对线程的控制,包括对线程的创建、运行、终止和挂起等。 (2)主框架类对象:CMainFrm.h(头文件)、CMainFrm.cpp(实现文件) 主框架类对象CMainFrm的基类是CFrameWnd,该类主要是显示主窗口,是找到菜单栏、窗口的标题栏以及工具栏的地方。主窗口对象负责窗口中出现的、围绕操作领域的一切,如:图画、文本和其他图形,这个区域称为窗口中的客户区。它用于管理应用程序窗口显示,如:标题栏、菜单栏、工具栏、状态栏、控制菜单、控制按钮等。它是所有MDI(多文档)和SDI(单文档)子窗口的包容器。 (3)视图类对象:CMyView.h(头文件)、CMyView.cpp(执行文件) 视图类对象CMyView的基类是CView,用于处理客户区,完成用户与文档的交互工作,是在程序中设置数据格式及显示数据的位置。 (4)文档类对象:CMyDoc.h(头文件)、CMyDoc.cpp(实现文件) 文档类对象CMyDoc的基类是CDocument,它主要是为程序存储数据,而在视图对象中处理并显示。它包含了应用程序在运行期间所用到的数据文档。 (5)其他有关文件 1)Resource.h:是标准的头文件,包含所有资源符号的定义。 2)Stdafx.h:用于建立预编译头文件(文件名.pch)和一个预定义的类型文件Stdafx.obj,由于MFC体系结构非常大,如果每次都编译的话很烦琐,因此,把常用的MFC头文件都放在Stdafx.h中,如afxwin.h、afxext.h、afxdisp.h等,然后让Stdafx.cpp包含Stdafx.h文件。这样由于编译器可以识别哪些文件已经编译过,所以Stdafx.cpp就只编译一次。因为它存放的是头文件编译后的信息,故称作预编译头文件。如果以后在编程时不想让有些MFC头文件每次都被编译,也可以将它加入到Stdafx.h中,采用预编译头文件,可以加速编译过程。 3)文件名.clw:是ClassWizard数据库文件,存放由ClassWizard使用的信息,Wizard还使用这个文件来存储信息,以便创建和编辑消息映射、对话框数据映射、以及创建成员函数的原型。 4)CMy.rc:是包含资源描述信息的资源文件,资源文件列有应用程序用到的所有资源,包括存储在“\res”子文件夹中的位图、图标和光标,这个文件可以在VC++6.0中直接编辑。 5)CMy.rc2:包含不是由Visual C++6.0编辑的资源,可以将所有不能由资源编辑器编辑的资源放置到这个文件中。 第1章 Visual C++程序的建立 6)CMyDoc.ico:是包含MDI子窗口图标的图标文件,这个图标包含在资源文件的文件名.rc中。 7)CMy.ioc:是包含应用程序图标的图标文件,应用程序图标包含在资源文件CMy.rc中。 8)Toolbar.bmp:用于创建工具栏按钮的位图文件,初始工具栏和状态栏,是在主边框窗口类中构造。 2. ResourceView页面 ResourceView包含了项目中所有资源的层次列表,在Visual C++中,每一个图片、字符串值、工具栏、图标或其他非代码元素等都可以看作是一种资源。 (1)Accelerator:快捷键列表,一系列组合键的集合,被应用程序用来引发一个动作。该列表一般与菜单命令相关联,用来代替鼠标操作。 (2)Bitmap:位图,图形映射的二进制形式,位图常被应用程序用于产生滚动条、最小化框和最大框等。 (3)Cursor:光标,这是一个32像素×32像素的位图,它指示鼠标当前在屏幕上的位置。 (4)Dialog:对话框,含有按钮、列表框、编辑框等各种控件的窗口。 (5)Icon:图标,图标代表应用程序显示在Windows桌面上的位图,它同时有32像素×32像素和16像素×16像素两种规格。 (6)Menu:菜单,用户通过菜单可以完成应用程序的大部分操作。 (7)String Table:字符串列表,它是应用程序使用的全局字符串列表。 (8)Toolbar:工具栏按钮,它是一系列具有相同尺寸的位图组成的,通常与一些菜单命令项相对应,用以提高用户的工作效率。 (9)Version:版本信息,包含应用程序的版本、用户注册码等相关信息。 3. ClassView页面 ClassView用以显示项目中的所有的类的信息。 (1)CAboutDlg:是对话框类,该类是每一个应用程序框架都有的,用于显示本程序的有关信息。它是从MFC的CDialog类派生来的。 (2)其他还包括:MainFrm(主框架类)、CMyApp(应用程序类)、CMyView(视图类)、CMyDoc(文档类)。Globals是应用程序的实现文件。 4. 文件类型 在Visual C++6.0中,项目中所有的源文件都是采用文件夹的方式进行管理的,它将项目名作为文件夹名,在此文件夹下包含以下一些文件类型。 (1).cpp,.h:源程序代码文件(.cpp实现文件,.h头文件)。 (2).dsp:项目文件(DeveloperStudio Project)。 (3).dsw:项目工作区文件。 (4).opt:关于开发环境的参数文件,如工具条位置等信息。 (5).plg:编译信息文件。 (6).aps:资源辅助文件(AppStudio File),二进制格式。 (7).bsc:用于浏览项目信息。 第1章 Visual C++程序的建立 (8).clw:类向导ClassWizard信息文件。 (9).hpj:帮助文件项目(Help Project)。 (10).map:执行文件的映像信息记录文件。 (11).mdp:旧版本的项目文件(Microsoft DevStudio Project)。 (12).pch:预编译文件(Pre-Compiled File),可以加快编译速度,但是文件非常大。 (13).pdb:记录程序有关的一些数据和调试信息(Program Database)。 (14).exp:记录DLL文件中的一些信息,只有在编译DLL才会生成。 (15).ncb:无编译浏览文件(no compile browser)。 (16)Debug:调试文件夹。 (17)Relcase:发行文件夹。 (18)Res:资源文件夹。 当用户用Visual C++6.0应用程序向导创建项目时,系统会自动为项目创建Win32 Debug(或让其自动生成Win32 Release)的运行程序,并使用相应的默认配置。它和Win32 Release版本的区别在于:Debug版本的运行程序有相应的调试信息码,而Release版本的运行程序没有相应的调试信息码。但Release版本的运行程序经过代码的优化,其程序的运行速度被最大加速。Visual C++程序编译后的Debug要在有Visual C++系统环境下运行,而Release可在无Visual C++系统环境下运行。 创建Release的步骤是:在程序窗口中选择Build→Set Active Configuration命令,在弹出的对话框中选择“文件名-Win32 Release”选项,然后单击OK按钮。这时在本程序文件夹中就有了Release,把该程序复制到没安装Visual C++系统环境的机器里也能运行。 1.4.4 输出窗口 输出窗口如图1.16所示,常常见到程序编译完后,若正确则在此窗口中显示“文件名.exe – 0 error(s),0 warning(s)”,若错误就在该窗口中出现一些错误信息。双击这些错误信息后,再到文档窗口的程序里查找已用箭头指示出的错误,从第一个错误开始查,顺序的排除一个个错误,程序就正常运行了。 1.5 ClassWizard类向导 ClassWizard称为类向导,能为一个项目添加一个类,进行消息和数据映射,创建OLE Automation(自动化)属性和方法以及进行ActiveX事件处理等。 1.5.1 ClassWizard的使用 [例1.15]在窗口上单击就能弹出一个对话框 (1)创建一个单文档的应用程序名为:显示对话框。 (2)在程序窗口中选择View→ClassWizard(或按Ctrl + W组合键)命令,在弹出的对话框中打开Message Maps选项卡,在Class name下拉列表框中选择CMyView(视图类),在Object IDs列表框中选择CMyView→在Messages列表框中选择单击的映射消息:WM_LBUTTONDOWN,如图1.18所示,单击Add Function按钮,单击Edit Code按钮。 第1章 Visual C++程序的建立 (3)在视图类中为刚刚添加的OnLButtonDown()函数中添写代码: void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { MessageBox("攀登计算机科学高峰","鼠标左键按下消息",0); CView::OnLButtonDown(nFlags, point); } 说明:MessageBox()函数只用于创建和显示消息对话框。 参数:1. 消息正文; 2. 消息框标题(默认没有); 3. 消息框的风格。 (4)编译运行,在程序窗口中单击,就弹出如图1.19所示的对话框。 该程序就使用了ClassVizard(类向导),将“鼠标左键按下消息”映射到项目的视图类中,完成了程序要达到的目的。 图1.18 添加鼠标消息对话框 图1.19 输出结果 说明:在程序文档编辑窗口中右击,从弹出的快捷菜单中也能选择ClassVizard命令。 可以看到MFC ClassWizard对话框包含了五个标签页面,其中最常用的有两个。 (1)Message Maps(消息映射):用于添加、删除和编程处理消息的成员函数。 (2)Member Variables(成员变量):添加或删除与控件相关联的成员变量(或称为数据成员),以便与控件进行数据交换。 例1.15的程序设计中用到了Message Maps选项卡,如图1.18所示。表1.3列出了其中的各项功能。 1.5.2 消息和消息映射 Windows编程与其他类型编程的最大不同之处在于使用消息机制。在Windows中发生的一切都可以使用消息作为媒体来表示。消息用于告诉操作系统发生了什么,例如用户的键盘操作、鼠标操作、打印操作等都可以用消息来传递。窗口之间也可以使用消息来通信。可以说,消息机制是Windows应用程序的核心。 1. Windows消息 在Windows中,所有消息都是通过各自的名字来访问的,当然对于不同的操作系统使用不同的数值来标记它们。一系列 #define语句将消息与特定数值相联系,例如,下面的语句定义了一条叫做WM_PAINT的消息: 第1章 Visual C++程序的建立 #define WM_PAAINT 120 可以在程序中通过此消息名“WM_PAAINT”来访问它。此定义将在本章例1.10中用到。在Windows中,不同的消息由应用程序的不同部分进行处理。例如:在窗口中的某个控件上右击,该控件将会接收一条WM_RBUTTONDOWN消息,相应的程序就会处理这条消息;在窗口中右击,就会弹出一个快捷菜单等。 表1.3 ClassWizard对话框的Message Maps选项卡各项功能 项 目 说 明 Project下拉列表框 选择应用程序项目名,一般只有一个 Class name下拉列表框 在相应的项目中选择指定的类,它的名称与项目工作区中ClassView是一样的 Object IDs列表 资源标识符列表中列出了在Class name下拉列表框指定的类中可以使用的ID号,用户从中可以选择要映射的资源号 Messages列表 该列表中列出了相应的资源对象的消息,若用户从中选定某个消息,则按钮Add Function被激活 Member functions列表 列出Class name中指定的类的成员函数,若用户从中选择某个成员函数,则按钮Delete Function被激活 Add Class按钮 向项目中添加类 Add Function按钮 向指定的类中添加成员函数 Delete Function按钮 删除指定类中的成员函数 Edit Code按钮 转向文档窗口,并定位到相应的函数源代码处 2. 消息分类 (1)窗口消息(Windows message):窗口消息主要是指以WM_开头的消息(WM_COMMAND除外),一般由窗口类和视图类对象来处理,窗口消息往往带有参数,以标志处理消息的方法。窗口消息包括以下几项。 1)鼠标消息,如图1.20所示。 WM_MOUSEMOVE 鼠标移动时发送该消息 WM_LBUTTONDOWN 鼠标左键被按下时发送该消息 WM_LBUTTONUP 鼠标左键被释放时发送该消息 WM_LBUTTONDBLCLK 鼠标左键被双击时发送该消息 WM_RBUTTONDOWN 鼠标右键被按下时发送该消息 WM_RBUTTONUP 鼠标右键被释放时发送该消息 WM_RBUTTONDBLCLK 鼠标右键被双击时发送该消息 WM_MBUTTONDOWN 鼠标中键被按下时发送该消息 WM_MBUTTONUP 鼠标中键被释放时发送该消息 WM_MBUTTONDBLCLK 鼠标中键被双击时发送该消息 第1章 Visual C++程序的建立 2)键盘消息如图1.21所示。 WM_CHAR 将一次单击翻译成一个非系统字符时,发送该消息 WM_KEYDOWN 按一个非系统键盘时,发送该消息 WM_KEYUP 释放一个非系统键时,发送该消息 图1.20 窗口鼠标消息 图1.21 窗口键盘消息 3)与窗口操作有关的消息如图1.22所示。 图1.22 窗口类消息 WM_CREATE 生成一个窗口时发送该消息 WM_DESTROY 销毁一个窗口时发送该消息 WM_CLOSE 关闭一个窗口时发送该消息 WM_SIZE 改变窗口大小时发送该消息 WM_MOVE 移动一个窗口时发送该消息 WM_PAINT 当窗口的大小发生变化、窗口内容发生变化、窗口间的层叠关系发生变化或调用函数UpdateWindow或RedrawWindow时,系统都将产生WM_PAINT消息。表示要重新绘制窗口的内容。该消息处理函数的原型是:afx_msg void OnPaint(); WM_ACTIVATE 激活窗口或关闭窗口 WM_ACTIVATEAPP 正被激活的窗口属于不同的app WM_CANCELMODE 取消系统模式 WM_CHIDACTIVATE 移动的窗口 第1章 Visual C++程序的建立 WM_ENABLE 窗口被激活或关闭 WM_ENDSESSION 会话结束 WM_ENTERIDLE 静待用户操作 WM_ERASEBKGND 背景需要被清除 WM_GETMINMAXINFO 获得关于窗口大小的信息 WM_GETTEXT 获得相应于窗口的文本 WM_GETTEXTLENGTH 获得和窗口相关的文本长度 WM_ICONERASEBKGND 窗口背景需要被清除 WM_KILLFOCUS 用户按下不属于当前菜单中的键 WM_MENUSELECT 选取的菜单项 WM_PAINTICON 重画部分图标 WM_PARENTNOTIFY 窗口被创建或清除 WM_QUERYENDSESSION 结束Session命令 WM_QUIT 退出应用程序 WM_SETFOCUS 设置输入焦点 WM_SETFONT 字体更改 WM_SETREDRAW 清除重画标志 WM_SETTEXT 设置窗口标题 WM_SHOWWINDOW 窗口将被隐藏或显示 (2)控件通知消息(Control notifications):如图1.23所示。当控件的状态发生改变(例如用户在控件中进行输入)时,控件就会向其父窗口发送WM_COMMAND通知消息。应用程序框架处理控件消息的方法和窗口消息相同,但按钮的BN_CLICKED通知消息除外,它的处理方法与命令消息相同。 图1.23 控件通知消息 (3)命令消息(Command message):主要包括由用户交互对象(菜单、工具条按钮、加速键等)发送的WM_COMMAND命令消息。命令消息的处理方式与以上两种消息不同,它能够被多种对象接收、处理,这些对象包括文档类、文档模板类、应用程序本身以及窗口和视图类等;而窗口消息和控件的通知消息是由窗口对象接收并处理的。这里的窗口对象是指CWnd中派生的对象,它包括CFRameWnd、CMDIFrameWnd、CMDIChildWnd、CView、CDialog以及从这些类派生的对象等。 第1章 Visual C++程序的建立 3. 消息映射系统自动生成的代码 查看例1.5的程序代码,可以发现:ClassWizard为WM_LBUTTOMDOWN(按下鼠标左键)的消息映射做了以下3个方面的工作。 (1)在视图类的头文件“显示对话框View.h”中声明了鼠标左键按下的消息处理函数OnLButtonDown: protected: //{{AFX_MSG(CMyView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() 代码中的//{{AFX_MSG(CMyView)和//}}AFX_MSG 之间的部分是ClassWizard定义的专门用做消息映射函数声明的标记。表示该程序块中的消息映射声明是由ClassWizard来自动管理的,用户一般不需要去更改。需要说明的是,凡//{{和//}}之间的程序代码块均由ClassWizard自动管理。 (2)在视图类的“显示对话框View.cpp”实现文件前面的消息入口处,添加了相应的映射宏:ON_WM_LBUTTONDOWN() BEGIN_MESSAGE_MAP(CMyView, CView) //{{AFX_MSG_MAP(CMyView) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP (3)在视图类的“显示对话框View.cpp”实现文件中写入一个空的消息处理函数的模板,以便用户填入具体代码: void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonDown(nFlags, point); } 注意:如果要删除消息映射函数,需进行以下3步。 ① 在MFC ClassWizard对话框的Massages列表中选择要删除的消息映射函数,然后单击Delite Function按钮,最后关闭MFC ClassWizard对话框。 ② 在所在的类头文件(.h)的消息声明标记中删除该消息映射函数,在所在的类实现文件(.cpp)的消息声明入口处删除该消息映射宏。 ③ 在所在的类实现文件(.cpp)中删除该消息映射函数体。 第1章 Visual C++程序的建立 1.5.3 消息映射方法实例 1. 鼠标映射消息 鼠标是Windows操作系统中最重要的输入工具之一。在MFC中,鼠标输入,Windows将会产生相应的消息。例如:按下鼠标左键时,Windows就会产生WM_LBUTTONDOWN消息,释放鼠标左键时就会产生WM_LBUTTONUP消息。如果需要,用户只要对这些消息编写相应的响应函数就能完成相应的功能,其中大部分的消息,Windows系统均有默认的处理。 [例1.16]鼠标映射消息 在屏幕x,y坐标各为100处,显示“鼠标位于点[ ]”,当鼠标移动时,显示出鼠标不断变化的位置和鼠标按下、鼠标释放的位置变化。 (1)创建一个单文档应用程序,名为:处理鼠标。 (2)声明视图类的数据成员,为了记录用户操作鼠标的方式和位置,需定义一个变量存储,因此,在视图类的CMyView.h里的public下添加一个数据成员: CString m_MousePoint; //存储鼠标的方式和位置 在视图类CMyView.cpp的构造函数中初始化该数据成员: m_MousePoint = " "; //将存储鼠标的方式和位置初始置为空 (3)在屏幕重画函数OnDraw()中添加输出代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDC→TextOut(100,100,m_MousePoint); } (4)添加鼠标消息WM_LBUTTONDOWN(按鼠标左键被按时发送该消息)。 在程序窗口中选择View→ClassWizard命令,在弹出的对话框中打开Message Maps选项卡,在Class Name下拉列表框中选择CMyView选项,在Object IDs列表中选择CMyView选项,在Messages列表框中选择WM_LBUTTONDOWN选项,单击Add Function按钮,就在CMyView类添加了该鼠标消息的响应函数。 (5)用同样的方法,在CMyView类中,添加鼠标消息WM_LBUTTONUP(鼠标左键被释放时发送该消息)和WM_MOUSEMOVE(鼠标移动时发送该消息)。 (6)编写消息响应函数代码: void CMyView::OnLButtonDown(UINT nFlags, CPoint point) //鼠标左键按下 { //下面一条语句是在屏幕上显示鼠标所在位置的坐标 m_MousePoint.Format("鼠标左键在点(%d,%d)按下",point.x,point.y); Invalidate(); //强制执行OnDraw()函数,显示x,y坐标的值 CView::OnLButtonDown(nFlags, point); } void CMyView::OnLButtonUp(UINT nFlags, CPoint point) //鼠标左键释放 { m_MousePoint = "鼠标左键被释放"; Invalidate(); 第1章 Visual C++程序的建立 CView::OnLButtonUp(nFlags, point); } void CMyView::OnMouseMove(UINT nFlags, CPoint point) //鼠标移动 { m_MousePoint.Format("鼠标位于点(%d,%d)",point.x,point.y); Invalidate(); CView::OnMouseMove(nFlags, point); } (7)编译运行,结果如图1.24所示。 图1.24 显示鼠标位置 说明: (1)代码:CString类里定义了Format函数。Format是格式打印函数,和C语言中的printf语句相似。其参数:“鼠标位于点(%d,%d)”在屏幕上显示其中的实际内容,并按%d%d整型输出第2和第3个参数的数字,这里就是x,y的坐标。m_MousePoint是CMyView.h中定义的存储鼠标的方式和位置的CString类对象。 (2)Invalidate()函数的作用是使系统让用户区无效,从而调用重画函数OnDraw(),所以用户只要在OnDraw()中编写显示m_MousePoint的语句即可。 (3)对于所有的鼠标消息来说,ClassWizard都会将其映射成类似afx_msg void OnXXXX的消息处理函数,如上面的鼠标左键按下映射消息函数OnLButtonDown,它具有如下函数原型: afx_msg void OnLButtonDown(UINT nFlags, CPoint point) 对上述参数作以下说明。 point,表示鼠标光标在屏幕的(x,y)坐标。 nFlags,表示鼠标键和键盘组合情况,它可以是下列值的组合(MK前缀表示“鼠标键”): MK_CONTROL 键盘上的Ctrl键被按下时,nFlags设置为MK_CONTROL MK_LBUTTON 鼠标左键被按下时,nFlags设置为MK_LBUTTON MK_MBUTTON 鼠标中键被按下时,nFlags设置为MK_MBUTTON MK_RBUTTON 鼠标右键被按下时,nFlags设置为MK_RBUTTON MK_SHIFT 键盘上的Shift键被按下时,nFlags设置为MK_SHIFT 若想知道某个键被按下,可由对应的标识与nFlags进行逻辑“与”(&)运算,所得结果若为TRUE(非0)时,则表示该键被按下。例如,如果收到了WM_LBUTTONDOWN消息,且值nFlags&MK_CONTROL是TRUE时,则表明按住鼠标左键的同时也按住了Ctrl键。 (4)afx_msg是MFC用于定义消息函数的标志。 2. 键盘映射消息 除鼠标外,键盘也是Windows操作系统中最重要的输入工具之一。在Windows的编程环境中,键盘输入,将产生键盘消息。例如:用户按下一个键盘键时,就产生WM_KEYDOWN消息,释放一个键盘键时,就产生WM_KEYUP消息,而Windows将此键翻译成ASCII码后又将产生WM_CHAR消息,用户只要对这些消息中的一部分编写相应的响应函数即可,对其中大部分的消息,Windows系统均有默认的处理方法。 第1章 Visual C++程序的建立 [例1.17]键盘输入 (1)创建一个单文档应用程序,名为:读取键盘的键击。 (2)声明视图类的数据成员:在CMyView.h文件的protected下写: int m_nLine; //记录用户输入的回车次数 CString m_strDisplay; //存储在当前行输入的字符 (3)在CMyView.cpp的构造函数里对如上变量进行初始化: CMyView::CMyView() { m_nLine = 0; m_strDisplay = " "; } (4)向视图类添加键盘消息WM_CHAR响应函数。 在程序窗口中View→Class Wizard命令,在弹出的对话框中打开Message Map选项卡,在Classname下拉列表框中选择CMyView选项,在Objiect IDs下拉列表框中选择CMyView选项,在Messages列表框中选择WM_CHAR选项,单击Add Function按钮,然后单击Edit Code按钮,便可以看到在视图类View.cpp文件中加上了OnChar()键盘响应函数。 (5)在刚加进去的OnChar消息响应函数中添加代码: void CMyView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { if(nChar==VK_RETURN) //如按下回车键 { //m_strDisplay是上面定义的CString类对象,存储当前行输入的字符,CString类中定义了 //Empty()函数,这里它是用来清空m_strDisplay变量的 m_strDisplay.Empty(); m_nLine++; //行数加 1,即回车次数加1 } else { m_strDisplay +=nChar; } //将输入的字符添加到变量m_strDisplay的尾端 CClientDC dc(this); //窗口客户区的设备文本 dc.TextOut(0,m_nLine*20,m_strDisplay); //利用dc输出文本 CView::OnChar(nChar, nRepCnt, nFlags); } (6)编译运行,如图1.25所示。 说明: ① CClientDC是CDC类的派生类,在构造时调用了Windows函数GetDC,在析构时调用了ReleaseDC。这意味着和CClientDC对象相关的设备上下文是窗口的客户区。dc(this)一般指向本窗口或当前活动视图。CClientDC一般都在OnDraw()中使用,是专门用来绘制客户区的,所以定义的时候要用参数this,该参数是指向要用CclientDC绘图的CView类对象的指针,常用CClientDC  dc(this),是因为常在CView类的成员函数中使用CClientDC  dc(this),所以this就是指向CView类的。因此一定要明白自己用的this到底是不是指向CView的。 第1章 Visual C++程序的建立 图1.25 响应键盘字符输入 ② dc.TextOut(0,m_nLine*20,m_strDisplay);是利用dc输出文本,第1个参数是x坐标,x从0开始。第2个参数是 y坐标,如果是第1行,y就从20开始,该点坐标就是(0,20),如果是第2行(m_nLine=2),也就是m_nLine*20=40,该点坐标就是(0,40),如果是第3行(m_nLine=3),那么该点坐标就是(0,60)。这里x总是0,每换一行,y就下移20,输出下一行字符。 ③ if(nChar==VK_RETURN),nChar表示虚拟键代码,VK_RETURN表示回车键。 从例1.17中可以看出,ClassWizard能自动添加当前类的WM_KEYDOWN(某键按下)和WM_KEYUP(某键抬起)击键消息处理函数的调用,它们具有下列函数原型: afx_msg void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags); afx_msg void OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags); 参数: nChar表示虚拟键代码; nRepCnt表示按键的重复次数,若按下某个键不放,该参数将会持续增加; nFlags表示击键消息标志。 所谓虚拟键代码,是指与设备无关的键盘编码。在Visual C++中,最常用的虚拟键代码定义在Winuser.h中,如表1.4所示。 表1.4 虚拟键代码表 虚拟键名称 对应的虚拟键 VK_UP 光标上移键 VK_DOWN 光标下移键 VK_LEFT 光标左移键 VK_RIGHT 光标右移键 VK_HOME Home键 VK_END End键 VK_PRIOR PageUp页向上键 VK_NEXT PageDown页向下键 VK_RETURN 回车键 VK_SHIFT Shift键 VK_F1 表示功能键F1 和键击消息一样,MFC中的ClassWizard也提供相应的字符消息处理框架并自动添加当前类的WM_CHAR消息处理函数调用,它具有下列函数原型: 第1章 Visual C++程序的建立 afx_msg void OnChar(UINT nChar,UINT nRepCnt,UINT nFlags); 参数: nChar表示键的ASCII码; nRepCnt表示当用户按住一个键时的重复计数; nFlags表示字符消息标志。 值得说明的是:如果创建一个单文档应用程序,而在第六步将基类置成CEditView(具有文本编辑、查找、替换和滚动功能的视图),则什么也不用做,运行后,就能在文档窗口中随意写内容了,还能回车和出现滚动条。 3. 计时器消息 计时器能够以固定的时间间隔产生WM_TIMER消息,它不同于硬件时钟中断,而是用软件实现的。在程序中使用计时器要设置一个WM_TIMER消息发生的频率,例如,如果把定时器的时间间隔设置为500 ms(0.5 s),那么每隔500 ms就会产生一个WM_TIMER事件。如果要使程序运行中每隔一定的时间间隔发出“滴答”声音,这可通过定时器来实现,该功能的实现可分为两步:① 创建一个定时器,使之在规定的时间间隔发出特定的消息;② 在消息响应函数中,编写发“滴答”声音的代码。 [例1.18]使程序运行中,每隔一定的时间间隔发出“滴答”的声音 (1)创建一个单文档应用程序,名为:计时器。 (2)安装计时器,在程序窗口中选择View→ClassWizard命令,在弹出的对话框中打开Message Maps选项卡,在Class name下拉列表框中选择CMyView选项,在Object IDs下拉列表框中选择CMyView选项,在Messages列表框中选择OnInitalUpdate选项,单击Add Function。按钮这样就将OnInitalUpdate函数加到CMyView.cpp中了。再添加如下计时器代码: void CMyView::OnInitialUpdate() { CView::OnInitialUpdate(); SetTimer(1,500,NULL); //设置并启动计时器,SetTimer是CWnd的成员函数 } (3)清除计时器,计时器安装成功后,可以通过调用KillTimer()函数清除它,例如:可以在程序退出时清除计时器。按如上安装计时器的步骤将WM_DESTROY消息(即删除函数)加到CMyView.cpp中,并添加清除代码: void CMyView::OnDestroy() { CView::OnDestroy(); KillTimer(1); //清除计时器 } (4)添加计时器消息WM_TIMER响应函数,按如上安装计时器的步骤将WM_TIMER消息加到CMyView.cpp中,并添加代码: void CMyView::OnTimer(UINT nIDEvent) { MessageBeep(-1); CView::OnTimer(nIDEvent); } 第1章 Visual C++程序的建立 (5)编译运行,每隔一定的时间间隔机器就发出“滴答”声音。 说明: ① OnInitialUpDate函数,一般是对视图的显示做初始化,主要初始化视图中控件及对各个变量进行初始化操作。这里是安装计时器。 注意:在OnCreate产生VIEW的基本结构和变量后,再执行OnInitialUpDate函数。 ② SetTimer()是CWnd的成员函数,是设置并启动计时器,其原型为: UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT* lpfnTimer) (HWND,UINT,UINT,DWORD)); 参数: nIDEvent,用于指定该计时器的标识值(不能为0); nElapse,指定WM_TIMER消息发生的时间间隔,以毫秒为单位; lpfnTimer,指定一个用于响应WM_TIMER消息的处理函数地址,如果设置为NULL,则WM_TIMER消息放在消息队列中,由窗口对象处理,如果安装成功,则返回非零。 ③ KillTimer()函数:当应用程序不再使用计时器,可调用该函数CMnd::KillTimer()来停止WM_TIMER消息的传递,其原型为:BOOL KillTimer(int nIDEvent); 参数:nIDEvent是安装计时器时所指定的计时器的标识值,和SetTimer函数设置的计时器标识值是一致的,例1.18中都是1。 ④ WM_TIMER消息:ClassWizard会将其映射成具有下列原型的消息处理函数: afx_msg void OnTimer(UINT nIDEvent); 参数:nIDEvent可判断出WM_TIMER是哪个计时器传送的。这里置任意数都可以。 ⑤ MessageBeep(-1):播放一个系统报警声音,系统声音的分配方案是在控制面板里决定的,返回值Long,非零表示成功,零表示失败,参数-1表示从机器扬声器中发出蜂鸣声。其他描述见第7章。 [例1.19]文字滚动程序 计时器是一种常用的输入设备,前面已经介绍过,它周期性地按一定的时间间隔向应用程序发送WM_TIMER消息,它能实现“实时更新”以及“后台运行”等功能。应用程序是通过CWnd类的SetTimer函数来设置并启动计时器的。 (1)创建一个SDI应用程序,名为:文字滚动。 (2)程序设计,包括以下几步。 1)在视图的头文件CMyView.h里添加如下代码: private: int x; //x坐标向前步进 //如果要加颜色,在public里写: COLORREF m_pColor1; //第一种颜色 COLORREF m_pColor2; //第二种颜色 COLORREF m_pColor3; //第三种颜色 2)在视图的执行文件CMyView.cpp的构造函数里添加如下代码: CMyView::CMyView() 第1章 Visual C++程序的建立 { x=0; //如果要加颜色,接着写: m_pColor1=RGB(255,0,0); //红 m_pColor2=RGB(0,255,0); //绿 m_pColor3=RGB(0,0,255); //蓝 } 3)在CMyView.cpp里找到OnDraw函数并添加如下代码: View::OnDraw(CDC *pDC) { --- pDC→SetTextColor(m_pColor1); //SetTextColor设置当前文本颜色,这里是红色 pDC→TextOut(x,80,"中国改革开放的28年颂歌!"); pDC→SetTextColor(m_pColor2); //绿 pDC→TextOut(x,100,"我们一定完成祖国的统一大业!"); pDC→SetTextColor(m_pColor3); //蓝 pDC→TextOut(x,120,"攀登计算机科学高峰,培养现代化创新人才!"); x=x+20; //步进长度 RECT re; //矩形结构体对象,也就是窗口矩形。 GetClientRect(&re); //获得矩形窗口的大小 if(x>re.right-re.left) //字走到最右边,就从头(x=0)开始,否则继续 x=0; } 4)添加一个菜单项,名为“启动(&B)”,ID标识为ID_TEXT,将ID_TEXT映射消息连接到视图类CMyView中并添加如下代码: void CMyView::OnText() { SetTimer(1,200,NULL);//设置并启动计时器 } 5)将计时器消息WM_TIMER映射到视图CMyView中并添加代码: void CMyView::OnTimer() { Invalidate(); //强制重新绘制视图 } 图1.26 文字滚动情况 (3)编译运行,选择新建的“启动”命令即可使文字滚动,如图1.26所示。 说明: (1)RECT结构: typedef struct tayRECT { LONG left; //左上角x坐标 LONG top; //左上角y坐标 LONG right; //右下角x坐标 LONG bottom; //右下角y坐标 }RECT; 第1章 Visual C++程序的建立 例1.19中:RECT re;是定义RECT结构体类型变量(re)。 (2)CRect类与如上结构相似,且包括操作CRect对象和WindowsRECT结构的成员函数。 (3)GetClientRect(&re)函数功能:获取窗口客户区的坐标。客户区坐标指定客户区的左上角和右下角。由于客户区坐标是相对子窗口客户区的左上角而言的,因此左上角坐标为(0,0)。 4. 自定义消息 用户可以自定义消息,在应用程序中主动发出,这种消息一般用于应用程序的某一部分内部处理。 [例1.20]自定义消息处理 当用户按键盘上的光标上移键时,程序发送用户自定义消息,在对应的消息响应函数中弹出消息对话框,显示消息发送成功。 (1)创建一个单文档应用程序,名为:自定义消息处理。 (2)定义用户消息的ID(标识符),在视图类的头文件CMyView.h中的前面写如下语句,定义用户消息的标识符: #endif // _MSC_VER > 1000 #define WM_PAAINT 120 //定义消息的ID标识符,这里用大于100的120表示 class CMyView : public CView { protected: // create from serialization only CMyView(); …… } (3)声明并自定义用户消息响应函数:在视图类中添加void OnMyFunction()函数。在ClassView选项卡中右击CMyView,在弹出的快捷菜单中选择Add Member Function命令,在类型写:void,函数名写:OnMyFunction(),这时就将该消息响应函数加到CMyView.cpp中了。并添加代码: void CMyView::OnMyFunction() { MessageBox("恭喜你,消息发送成功"); } (4)在视图类的实现文件CMyView.cpp中的消息入口处添加消息映射: BEGIN_MESSAGE_MAP(CMyView, CView) //{{AFX_MSG_MAP(CMyView) ON_MESSAGE(WM_PAAINT,OnMyFunction) //}}AFX_MSG_MAP (5)编写程序代码。 添加WM_KEYDOWN消息响应函数,用户按光标上移键时,将产生WM_KEYDOWN消息。故加此消息响应函数。 在Message Maps选项卡的Class name下拉列表框中选择CMyView选项,在Object IDs下拉列表框中选择CMyView选项,在Messages列表框中选择WM_KEYDOWN选项,单击Add Function按钮,然后Edit Code按钮,到View.cpp的OnKeyDown()函数中添加代码: void CMyView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 第1章 Visual C++程序的建立 { HWND hWnd = GetSafeHwnd(); //接收消息的窗口句柄 if(nChar==VK_UP) //光标上移键 { ::PostMessage(hWnd,WM_PAAINT,0,0);//将消息插入消息队列并返回 return; } CView::OnKeyDown(nChar, nRepCnt, nFlags); } 图1.27 发送自定义消息 (6)编译运行,按键盘上的光标上移键,程序发送自定义消息,弹出一个对话框,如图1.27所示。 说明: (1)OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)当键盘上某个按键被按下时发送此消息。 参数: nChar为所按的键的字符代码值,这里是VK_UP(光标上移键的虚拟键); nRepCnt为重复击键的次数; nFlags表示扫描码和键转换前后的状态。 (2)用户消息的发送(::PostMessage()函数):Windows提供的发送消息的函数,PostMessage()的功能是将消息插入消息队列并返回,由处理线程处理,而队列中的消息通过GetMessage()或PeekMessage()函数获取。PostMessage()函数的原型为:BOOL PostMessage (HWND hWnd,UINT Msg,WPARAM wParam,WPARAM IParam)。 参数: HWND hWnd,接收消息的窗口句柄; UINT Msg,消息的ID标识符; WPARAM wParam,消息的WPARAM参数; WPARAM IParam,消息的LPARAM参数。 WPARAM wParam,WPARAM IParam都是消息的附加参数,比如要响应键盘按下的消息就得用WM_CHAR消息,但是并不知道按下的是哪个键,这两个参数就指定按键的ASCII码,来确定按下的是哪个键。它俩都是4个字节长,一般都用LPARAM传指针,两者没区别;想怎么用就怎么用。wParam的低4位数也表示消息的ID值,比如某菜单用符号表建的ID符为:ID_NEW_MENUITEM,值为101,则可用if(LOWORD(wParam)= =ID_NEW_MENUITEM)来判别。 例1.20中见到了HWND和UINT,它们是Windows常用的句柄类型和常用的基本数据类型。表1.5列出了一些Windows编程中常用的基本数据类型。表1.6列出了Windows常用的句柄类型。 第1章 Visual C++程序的建立 表1.5 Windows常用的基本数据类型表 Windows的数据类型 对应的基本数据类型 说 明 BOOL bool 布尔值 BSTR ynsigned short* 32位字符指针 BYTE unsigned char 8位无符号整数 COLORREF unsigned long 用做颜色值的32位值 DWORD unsigned long 32位无符号整数,段地址和相关的偏移地址 LONG long 32位带符号整数 LPARAM long 作为参数传递给窗口过程或回调函数的32位值 LPCSTR const char* 指向字符串常量的32位指针 LPSTR char* 指向字符串的32位指针 LPVOID void* 指向末定义类型的32位指针 LRESULT Long 来自窗口过程或回调函数的32位返回值 UINT unsigned int 32位无符号整数 WORD unsigned short 16位无符号整数 WPARAM unsigned int 当做参数传递给窗口过程或回调函数的32位值 表1.6 Windows常用的句柄类型 句 柄 类 型 说 明 HBITMAP 保存位图信息的内存域的句柄 HBRUSH 画刷句柄 HCURSOR 鼠标光标句柄 HDC 设备描述表句柄 HFONT 字体句柄 HICON 图标句柄 HINSTANCE 应用程序的实例句柄 HMENU 选单句柄 HPALETTE 颜色调色板句柄 HPEN 在设备上画图时用于指明线型的笔的句柄 HWND 窗口句柄 1.6 章后实训 实训1 键盘字符输入,并使输入的文本居中 1. 该程序的主要工作 (1)处理Windows消息; (2)使用Visual C++ ClassWizard(类向导); 第1章 Visual C++程序的建立 (3)读取来自键盘的键击; (4)设置显示文本的格式; (5)决定窗口的尺寸; (6)决定文本串的屏幕尺寸。 2. 用到的一个主要函数是字符消息处理框架函数 afx_msg void OnChar(UNIT nChar,UINT nRepCnt,UINT nFlags) 参数: nChar表示键盘码; nRepCnt表示按键的个数; nFlags表示字符消息标志。 Windows操作系统是通过格式化的消息在应用程序中通信的,每个事件发生后,Windows就将它转化为一条消息,判断这条消息应该由哪个窗口来处理,然后将该消息发往该窗口,并交由该窗口的拥有程序去处理,这就是Windows的消息处理机制。 3. 程序编制 (1)创建一个单文档应用程序,名为:键盘输入。 (2)为键盘数据建立存储,在文档类CMyDoc.h文件的public下写: CString str; //定义字符串对象 在文档类CMyDoc.cpp的构造函数里写: str=" "; //将字符串对象置为空 当用户键入每个键字符时,就把它们送到str中。 (3)读取键盘的键击,当用户键入一个键时,Windows将会发送给一个WM_CHAR消息,把这个消息映射到视图类(CMyView)中,并在函数OnChar()中添加下列代码: CMyView::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags) { CMyDoc *pDoc=GetDocument(); //获取文档类首地址指针 ASSERT_VALID(pDoc); //测试指针 pDoc->str+=nChar; //读取键击 Invalidate(); //重绘视图,强制绘图 } (4)显示文本,Invalidate()函数通过调用OnDraw()函数,强制程序重绘视图,故在视图类View.cpp的OnDraw(CDC *pDC)函数里添加下列代码: CMyView::OnDraw(CDC *pDC) { ------- pDC->TextOut(20,20,pDoc→str); //在文档窗口的x=20,y=20处显示输入的信息 } (5)编译运行,键盘输入字符,便在文档窗口上显示出来。 说明: ① pDoc->str+=nChar;相当于pDoc->str=pDoc->str+nChar; ② Draw函数是视图显示函数。 (6)将文本居中,CWnd派生出CView,再由CView派生出CCenteredView类,该类使文本居中。可使用CWnd的GetWindowRect 第1章 Visual C++程序的建立 ()函数,得到客户区的尺寸。 CRect类用于容纳矩形的尺寸和大小,把CRect对象的指针传递到GetWindowRect()函数,来获得视图的尺寸。在OnDraw()函数里添加下列代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); //pDC->TextOut(20,20,pDoc->str);//此条语句是前面加的,这里注释掉 CRect rect; //CRect类用于容纳矩形的尺寸和大小 GetWindowRect(&rect);//得到客户区的尺寸 int x=rect.Width()/2; //确定中心坐标x,y int y=rect.Height()/2; CSize size=pDC->GetTextExtent(pDoc->str);//查明显示的文本串尺寸 x-=size.cx/2; y-=size.cy/2; pDC->TextOut(x,y,pDoc->str); } 图1.28 响应键盘字符输入、使文本居中 (7)编译运行,键盘输入字符,便在窗口中间显示出来,如图1.28所示。 说明: ① 使用CDC类(设备环境类)的GetTextExtent()函数来决定文本串将显示在客户区的尺寸。CSize类有两个重要的成员,cx和cy用于保留文本串的尺寸。 ② x,y表示窗口新的左上角坐标。 ③ cx,cy表示窗口新的宽度和高度。 ④ RECT结构: typedef struct tayRECT { LONG left; //左上角x坐标 LONG top; //左上角y坐标 LONG right; //右下角x坐标 LONG bottom;//右下角y坐标 }RECT; //CRect类用于容纳矩形的尺寸和大小 ⑤ SIZE结构://窗口大小 typedef struct tagSIZE { int cx; //当一个函数返回时的x范围 int cy; //当一个函数返回时的y范围 }SIZE; 注意:如果要显示带颜色的字,需做以下工作。 //定义颜色变量 在View.h的public里写: COLORREF m_pColor; 第1章 Visual C++程序的建立 在View.cpp的构造函数里写: m_pColor=RGB(255,0,0); //红色 在刚加进去的pDC->TextOut(x,y,pDoc->str);语句的前面加: void CMyView::OnDraw(CDC* pDC) { …………… pDC->SetTextColor(m_pColor); //显示红色的字体 pDC->TextOut(x,y,pDoc->str); 前面介绍了读取键盘的键击的编程过程,实际上,在创建单文档(或双文档)应用程序时,到第6步(最后一步)停住,在这个界面的最下方Base class处下拉,找到CEditView置好,再单击Finish按钮之后运行,在程序的文档窗口上,直接就能读取来自键盘的键击。 实训2 向窗口中添加一个闪亮的插入符 向窗口中添加一个闪亮的插入符,插入符出现在文本末尾,指示下一个字符的位置;当用户单击另一个窗口或别处时,会将焦点移向另一个窗口,隐藏插入符,当鼠标回到本窗口单击时,又获得焦点,显示插入符。 (1)建立一个单文档应用程序,名为:插入符。 (2)在文档类CMyDoc.h里加(public下面):CString str; (3)在文档类CMyDoc.cpp里的构造函数里加:str=" "; //置空 (4)向视图类CMyView里加映射消息OnChar(),即:WM_CHAR读取键击,并添加代码: void CMyView::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags) { CMyDoc *pDoc=GetDocument();//获得文档类指针 ASSERT_VALID(pDoc); //测试文档指针 pDoc->str+=nChar; Invalidate(); } (5)在视图类CMyView.h里加(可在protected下面加): //布尔类型对象,以了解是否已创建了该插入符,boolean变量也称boolean标志,有两个值:真和假 boolean car; (6)在视图类CMyView.cpp的构造函数中将car设为假:car=false; (7)在视图类CMyvoid CMyView::OnDraw(CDC *pDC)添加代码: { …………… if(!car) { TEXTMETRIC text; // TEXTMETRIC是一个结构体,为获得插入符//的大小 pDC->GetTextMetrics(&text); //创建插入符,插入符宽度是字符宽度的1/8 CreateSolidCaret(text.tmAveCharWidth/8,text.tmHeight); (8)设置插入符的位置,在视图类CMyView.h的protected下写: 第1章 Visual C++程序的建立 CPoint carep; //点坐标对象 在视图类CMyView.cpp的OnDraw()中原有代码下添加: void CMyView::OnDraw(CDC* pDC) { ………… carep.x=carep.y=4; //将插入符的位置设为4 SetCaretPos(carep); //设置插入符位置 ShowCaret(); //在屏幕上显示插入符 car=true; //将布尔标志设为真 } pDC->TextOut(0,4,pDoc->str); } 这时插入符便出现在屏幕上一闪一闪的,还可以往里输入东西,但插入符不动。 (9)将插入符放在显示文本串的末尾,就必须决定文本串的末尾位置,通过GetTextExtent()来提供一个名为size的CSize对象来实现。 接着在函数OnDraw()中刚加入的代码pDC→TextOut(0,4,pDoc→str);的下面写: void CMyView::OnDraw(CDC* pDC) { ………… TextOut(0,4,pDoc→str); CSize size=pDC->GetTextExtent(pDoc->str); //文本大小 //下面是在文本串末尾显示插入符,首先使用HideCaret()隐藏它,如果不隐藏,可能会在屏幕上的老//位置留下插入符的映像。接着在CSize size=pDC->GetTextExtent(pDoc->str); 接着写 HideCaret(); carep.x=size.cx; //将CPoint的x数据成员设置在屏幕上文本串的末尾 SetCaretPos(carep);//将插入符移向它的新位置 ShowCaret();//重新显示它 } (10)单击“运行”按钮,可见插入符跟在文本串的后面。 (11)当用户单击另一个窗口或别处时,会将焦点移向那个窗口,插入符隐藏,当失去或获得焦点时,隐藏或显示插入符。 1)失去焦点得到:WM_KILLFOCUS消息。 2)获得焦点得到:WM_SETFOCUS消息。 将这两个消息分别加到CMyview视图类中去,并添加代码: CMyView::OnkillFocus(Cwnd *pNewWnd)//失去焦点 { HideCaret(); } //隐藏焦点 CMyView::OnSetFocus(CWnd *pOldWnd)//获得焦点 { ShowCaret(); } //显示焦点 这时运行,见窗口中有插入符,但把鼠标移到窗口外面单击,插入符就没了,而再把鼠标移回窗口单击,插入符又出现了。 (12)使用鼠标,在用户区任何位置单击,就在该位置显示一插入符,并在这个新位置处绘制文本。再单击另一个位置时,将先前的文本清除掉。 第1章 Visual C++程序的建立 1)加鼠标左键按下的映射消息WM_LBUTTONDOWN(鼠标按下,则第一个任务是存储它的位置,将在变量x和y中存储该位置,即(x,y),从point对象的x和y成员中获得),并添加下列代码: CMyView::OnLButtonDown(UINT nFlags,CPoint point) { x=point.x; y=point.y; CMyDoc *pDoc=GetDocument(); ASSERT_VALID(pDoc); //鼠标到了新的位置,将原来的去掉,置空 pDoc->str.Empty(); Invalidate(); //强制程序重绘视图 CView::OnLButtonDown(nFlags, point); } 2)在视图类CMyView.h的保护或公共类型下添加: int x,y; COLORREF m_pColor; //定义颜色变量 在视图类的执行文件CMyView.cpp的构造函数中添加: m_pColor=RGB(255,0,0); //红色 3)在视图类的执行文件CMyView.cpp的OnDraw函数中添加下列代码: CMyView::OnDraw(CDC *pDC) { ---- pDC->SetTextColor(m_pColor); //以下是将插入符移向文本串的末尾并重新显示它 carep.x=x+size.cx; carep.y=y; SetCaretPos(carep); pDC->TextOut(x,y,pDoc->str); } (13)编译运行,结果如图1.29和图1.30所示。这时,键盘输入时,会出现两行字,可将OnDraw函数中前面写入的pDC->TextOut(0,4,pDoc->str);注释掉。 图1.29 获得焦点(有插入符) 图1.30 失去焦点(插入符消失) 第1章 Visual C++程序的建立 在文档窗口中单击一下,再敲入字符,之后再在其他位置单击,再敲入字符,前面敲入的字符便消失了。 实训3 制作一个每次单击窗口都出现不同鼠标光标图形的程序 制作一个每次单击窗口都出现不同鼠标光标图形(6个不同鼠标图形)的程序,而当鼠标移出窗口范围时,又变回原来图形,使新加入的鼠标光标移不出窗口范围的应用程序。 (1)创建一个单文档的应用程序,名为:鼠标范围。 (2)向资源编辑器中添加6个光标资源。 在程序窗口中选择Insert->Resource命令,选中Cursor,单击Import按钮,在C盘上搜寻到6个 .cur图形(光标资源),粘贴到某个文件夹中,同时添加到Import Resource对话框的“文件名”处,再单击Import按钮,就将这6个光标资源添加到了本项目的资源编辑器中了,如图1.31所示。 图1.31 添加到资源编辑器的光标资源 (3)在视图类的头文件CMyView.h的public下添加成员变量及函数: HCURSOR m_hCursor; //用于存放光标句柄 int m_hClick; void LoadCur(UINT CurName);//用于获得要显示鼠标句柄 void GetCur(int ID_NO);//用于得到将要显示鼠标的ID (4)在视图类的执行文件CMyView.cpp中完成自定义的函数功能(全用手写)。添加如下代码: void CMyView::LoadCur(UINT CurName) { m_hCursor=AfxGetApp()->LoadCursor(CurName); } void CMyView::GetCur(int ID_NO) { if(m_hClick>6) m_hClick=1; switch(m_hClick) { case 1:LoadCur(IDC_CURSOR1);break; 第1章 Visual C++程序的建立 case 2:LoadCur(IDC_CURSOR2);break; case 3:LoadCur(IDC_CURSOR3);break; case 4:LoadCur(IDC_CURSOR4);break; case 5:LoadCur(IDC_CURSOR5);break; case 6:LoadCur(IDC_CURSOR6);break; } } (5)分别向视图类CMyView添加鼠标左键按下、抬起和鼠标移动的消息映射,如图1.32所示。 图1.32 添加鼠标左键按下、抬起和鼠标移动消息映射 (6)完成鼠标放下、鼠标抬起、鼠标移动消息映射函数的实现功能,分别添加如下代码: void CMyView::OnLButtonDown(UINT nFlags, CPoint point) //鼠标按下 { ++m_hClick; GetCur(m_hClick); SetCursor(m_hCursor); RECT rect; GetClientRect(&rect); ClientToScreen(&rect); ClipCursor(&rect); CView::OnLButtonDown(nFlags, point); } void CMyView::OnLButtonUp(UINT nFlags, CPoint point) //鼠标抬起 { ClipCursor(NULL); } void CMyView::OnMouseMove(UINT nFlags, CPoint point) //鼠标移动 { SetCursor(m_hCursor); CView::OnMouseMove(nFlags, point); } 第1章 Visual C++程序的建立 (7)在视图类的执行文件CMyView.cpp的构造函数中,添加代码: CMyView::CMyView() { m_hClick=0; } //初始化m_hClick变量 (8)编译运行,用鼠标多次单击窗口,将多次出现以上加入的6个不同的光标图形,而将其移出文档窗口外边时,它又变回了原来的图形,新加入的光标移不出窗口。 第2章 菜单、工具栏和状态栏的设计 第2章 菜单、工具栏和状态栏的设计 在Windows应用程序中,菜单、工具栏、状态栏等内容都是不可缺少的界面元素。菜单是一系列可视的命令列表,用户能够选中其中的菜单项(命令)并执行相应任务。工具栏提供图形按钮,实现快捷操作,用户可以通过工具栏执行最常用的命令,增强方便程度。状态栏可以显示动态的提示信息,便于用户的一些操作。 2.1 设计菜单 菜单为用户控制程序提供了一套分级选项,无论是标准菜单及命令,还是热键或弹出式菜单,以及为菜单或其命令定义加速键和状态条提示,都可用菜单编辑器来完成;除此之外,菜单项作为一个普通的对象也可在编辑时进行移动、复制、删除等操作。图2.1是一个典型的菜单实例,图中包含了标准菜单命令、快捷键、加速键、子菜单、核对符等。这些都是编写程序时经常遇到的。下面就介绍如何创建和编辑菜单项。 图2.1 典型菜单实例 2.1.1 用编辑器设计菜单 当用户使用AppWizard创建SDI或MDI应用程序时,系统将为用户自动生成默认的菜单栏。用户需要做的工作仅仅是打开菜单编辑器,进行必要的修改,再编写菜单选项相应的消息处理函数即可。当然也可在菜单编辑器中创建新的菜单或创建新的菜单资源,如快捷菜单等。 无论是编辑已有菜单资源还是创建新的菜单资源,首先应当进入菜单资源编辑器。 [例2.1]用编辑器设计一个子菜单 第2章 菜单、工具栏和状态栏的设计 在菜单编辑器的“文件”下拉菜单的某个位置加一个菜单,其作用是单击它后,能在屏幕上显示一行字。下面介绍其编程步骤。 (1)建一个SDI单文档应用程序名为:显示一行字。 (2)用编辑器设计菜单:在项目工作区打开ResourceView选项卡,打开Menu 文件夹,双击IDR_MAINFRAME,右边出现菜单编辑器,如图2.2所示,打开“文件”下拉菜单,如图2.2所示,双击最后的空白菜单,弹出“菜单项属性”对话框,在Caption处写菜单名:显示一行字(&C),在ID处写ID_FILE_XS,在Prompt(注释)栏中写:点击新建菜单项,在窗口显示一行字,然后关闭该对话框。 图2.2 菜单资源编辑器 (3)在文档的头文件CMyDoc.h的public下添加: CString str; //定义字符串变量 在文档的实现文件CMyDoc.cpp的构造函数里添加: str=" "; //将字符串变量赋初值为"空" (4)将菜单的标识ID_FILE_XS映射到视图类CMyView中。 在Message Maps选项卡的Class name下拉列表框中选择CMyView选项,在Object IDs下拉列表框中选择ID_ FILE_XS选项,在Messages列表框中选择COMMAND选项,依次单击Add Function->OK->Edit Code按钮。在OnFileXS()映射函数中添加代码: void CMyView::OnFileXS() { CMyDoc *pDoc=GetDocument(); //获得文档类指针 ASSERT_VALID(pDoc); //检查pDoc指针是否有效 pDoc->str="你成功的在File菜单下,建立了一个显示一行字菜单"; Invalidate(); //去强制执行OnDraw()函数 } (5)再在视图实现文件CMyView.cpp里的OnDraw(CDC *Pdc)函数里添加代码: 第2章 菜单、工具栏和状态栏的设计 CMyView::OnDraw(CDC *Pdc) { pDC->TextOut(100,100,pDoc->str); } //在窗口的x=100,y=100坐标处输出字符串 (6)编译运行: 1)在出现的文档窗口上,选择“文件”→“显示一行字”命令,在屏幕上显示程序中写的一行字; 2)打开“文件”下拉菜单,直接按C键,也出现这行字; 3)将鼠标放在“显示一行字”这个菜单上,下面状态栏会出现在程序中写的注释:“点击新建菜单项,在窗口显示一行字”。 说明: ① TextOut(100,100,pDoc->str)是CDC类的输出函数,100,100是x,y坐标,pDoc->str是输出str内容。 ② 改变菜单位置:鼠标左键选中该菜单不放,拖到你想要加的位置即可。 ③ 想在哪个位置加菜单:可选中后面的那个菜单,按Insert键即可。 ④“显示一行字(&C)”:其中“&”用于将其后面的字符作为该菜单项的助记符,也就是它后面的字符成了快捷键字符。打开这个菜单,直接按这个助记符键,菜单命令即被执行。 注意:快捷键字符C不能与同一级快捷键字符重复,例如:若写“显示一行字(&X)”,则与下面的“退出(&X)”重复,系统无法判别是哪个X,则不能执行。 图2.3中的小对话框(Menu Item Properties)是双击“文件”下的空白菜单出现的菜单属性对话框,用它建立了“显示一行字(&C)”菜单,菜单General选项卡的各项含义如表2.1所示。 图2.3 菜单编辑器和修改菜单项属性的结果 表2.1 菜单General选项卡的各项含义 项 目 含 义 ID 菜单的资源ID标识符 Caption(标题) 用于标识菜单项显示文本,助记符字母前面须有一个&符号,这个字母与Alt构成组合键 第2章 菜单、工具栏和状态栏的设计 续表 项 目 含 义 Separator(分隔符) 选中此复选框时,菜单项是一个分隔符或一条水平线 Checked(选中的) 选中此复选框时,菜单项文本前显示一个选中标记 Pop_up(弹出) 选中此复选框时,菜单项含有一个弹出式子菜单 Grayed(变灰) 选中此复选框时,菜单项显示是灰色的,用户不能选用 Inactive(非激活) 选中此复选框时,菜单项没有被激活,用户不能选用 Help(帮助) 选中此复选框时,菜单项在程序运行时被放在顶层菜单的最右端 Break(暂停) 当为Column时,对于顶层菜单项来说,被放置在另外一行上,而对于弹出式子菜单的菜单项来说,则被放置在另外一列上,当为Bar时,与Column相同,只不过对于弹出式子菜单来说,它还在新列与原来的列之间增加一条竖直线,注意:这些效果只能在程序运行后才能看到 Prompt(提示) 用于指明光标移至该菜单项时,在状态栏上显示的提示信息 [例2.2]用编辑器设计一个顶层菜单 在顶层菜单栏里建立一个菜单项,并在其下面建立带有子菜单的菜单项,使子菜单具有加速键、变灰和核对符,又使每个子菜单都能显示信息。下面是其详细步骤。 (1)创建一个单文档的应用程序(或用例2.1程序),名为:山东旅游。 (2)建立菜单,包括以下几步。 1)打开ResourceView选项卡,打开Menu文件夹,双击IDR_MAINFRAME,右边出现菜单编辑器,左键选中顶层最后的空白菜单不放,将其拖到“帮助”的前面,松开鼠标(或打开“帮助”菜单,按Insert键)。双击这个空白菜单,弹出“菜单属性对话框”,在Caption处写:山东旅游(&S),Pop_up处于选中状态(屏蔽ID),退出。 2)双击下面出现的空白菜单,弹出“菜单属性对话框”,选中Pop_up(屏蔽ID),Capton处写:烟台(&Y)。 3)右边出现空白子菜单,双击它,弹出“菜单属性对话框”,在ID处写:ID_SD_YT_PL,在Capton处写:蓬莱 Ctrl +F5(注:Ctrl+F5是加速键标识),在注释栏prompt处写:蓬莱仙境。 4)双击“蓬莱”下面的子菜单,弹出“菜单属性对话框”,在ID处写:ID_SD_YT_NS,在Capton处写:南山(&N),在prompt处写:南山大佛。 5)双击“烟台”下面的空白菜单,弹出“菜单属性”对话框,选中Pop up(屏蔽ID),在Capton处写:青岛(&Q)。 6)右边出现空白子菜单,双击它,弹出“菜单属性对话框”,在ID处写:ID_SD_QD_LS,在Capton处写:崂山Ctrl +F6,在Prompt处写:崂山道士。 7)双击“青岛”下面的空白菜单,弹出“菜单属性对话框”,选中Pop up(屏蔽ID),在Capton处写:泰安(&T)。 8)右边出现空白子菜单,双击它,弹出“菜单属性”对话框,在ID处写:ID_SD_TA_TS,在Capton处写:泰山Ctrl +F7,在Prompt处写:泰山日出。 9)双击“泰安”下面的空白菜单,弹出“菜单属性对话框”,选中Pop up(屏蔽ID),在 第2章 菜单、工具栏和状态栏的设计 Capton处写:济南(&J)。 10)右边出现空白子菜单,双击它,弹出“菜单属性对话框”,在ID处写:ID_SD_JN_BTQ,在Capton处写:趵突泉Ctrl +F8,在Prompt处写:天下第一泉。 (3)填加加速键表有以下几步。 1)打开项目工作区的ResourceView(资源界面),打开Accelerator文件夹,双击IDR_MAINFRAME,出现加速键表,双击最下面的空白格,如图2.4所示,弹出加速键属性对话框Acel Properties,在ID下拉列表框选择ID_SD_YT_PL选项,在key下拉列表框选择Vk_F5选项(或置好ID后,单击Next Key Typed按钮,弹出一个小对话框后,再按Ctrl+F5组合键也可)如图2.5所示,这样就为“蓬莱”菜单置好了加速键。Accel Properties对话框的各项含义如表2.2所示。 图2.4 加速键资源列表 图2.5 Accel Properties(加速键属性)对话框 2)再双击最下面的空白格,按上步的方法,分别将青岛崂山(ID_SD_QD_LS)、泰安泰山(ID_SD_TA_TS),济南趵突泉(ID_SD_JN_BTQ)菜单分别置好VK_F6 ,VK_F7,VK_F8的加速键。 注意:图2.5中Modifiers处,选中Ctrl复选框说明是Ctrl键,选中Alt复选框说明是Alt键,选中Shift复选框说明是Shift键,小对话框“Press a key to be used as the accelerator”是单击Next Key Typed按钮弹出来的。 第2章 菜单、工具栏和状态栏的设计 表2.2 Accel Properties对话框的各项含义 项 目 含 义 ID 指定资源ID号的列表项,为了能和菜单联用,通常选择某菜单项的ID号 Modifiers 用于确定Ctrl、Alt、Shift是否是构成加速键的组成部分 Type 用于确定该加速键的值是虚拟键(VirKey),还是ASCII Key 是指启动加速键的键盘按键 Next Key Type 单击此按钮后,用户操作的任何按键将成为此加速键的键值 (4)使菜单变灰(不被激活,不起作用):在Message Maps选项卡的Class name下拉列表框中选择CMyView选项,在Object IDs中找到想要变灰的菜单,这里选择ID_SD_TA_TS(泰山)选项,在Messages中选择UPDATE COMMAND UI(命令属性),依次单击Add Function →OK→Edit Code按钮,在该函数里写: void CMyView::OnSdTaTs() { pCmdUI→Enable(false); } 说明: pCmdUI是CCmdUI类的指针对象;CCmdUI类只是被用在一个CCmdTarget派生类中的ON_UPDATE_COMMAND_UI处理程序中;当用户打开一个菜单时,每个菜单项都需要知道它应该被显示为可用还是禁用;当菜单被打开时,(框架)会寻找并调用各个ON_UPDATE_COMMAND_UI处理程序,每个处理程序都调用CCmdUI类中像Enable和Check这样的成员函数,然后框架将(按照其合适的方式)显示各个菜单项。 (5)核对菜单项,使这个菜单名字的前面加个“√”号。 在Message Maps选项卡的Class name下拉列表框中选择CMyView,在Object IDs中找到你想要核对的菜单,这里选择ID_SD_YD_NS(南山),在右边Messages里选择UPDATE COMMAND UI,依次单击AddFoution→OK→Edit Code按钮,在该函数里写: void CMyView::OnUpdateSdYtNs(CCmdUI* pCmdUI) { pCmdUI→Enable(true);//这里如果是Enable(false)则该菜单变灰 pCmdUI→SetCheck(1);//设置核对符,如果括弧里写0是删除核对符 } (6)菜单命令响应,包括以下几步。 1)在View.h里的public:下定义变量:CString str; 在View.cpp的构造函数里将变量值赋空:str=“ ”; 2)将烟台的子菜单“蓬莱”的ID标识符ID_SD_YT_PL映射到视图类View里:在CLassName中选择View(视图类),在Object IDs中选择ID_SD_YT_PL,在Messages中选择选中COMMAND,依次单击Add Function→Edit Code按钮。添加代码: void CMyView::OnSdYtPl() { str="蓬莱仙境"; Invalidate(); } 第2章 菜单、工具栏和状态栏的设计 3)将烟台的子菜单“南山”的ID_SD_YT_NS映射到视图类View里,并添加代码: void CMyView::OnSdYtNs() { str="南山大佛"; Invalidate(); } 4)分别将青岛的子菜单“崂山”ID_SD_QD_LS、泰安的子菜单“泰山”ID_SD_TA_TS、济南的子菜单“趵突泉”ID_SD_JN_BTQ映射到视图类中,并添加代码: void CMyView::OnSdQdLs() //青岛崂山 { str="崂山道士"; Invalidate(); } void CMyView::OnSdTaTs() //泰安泰山 { str="泰山日出"; Invalidate(); }  void CMyView::OnSdJnBtq()//济南趵突泉 { str="天下第一泉"; Invalidate(); } 5)在视图类实现文件CMyView.cpp的OnDraw()函数里添加代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDC->TextOut(50,50,str); } (7)编译运行,结果如图2.1所示。 1)打开“山东旅游”→“烟台”→“南山”子菜单,见其前面有“√”号。 2)打开“山东旅游”菜单,分别选择“蓬莱”、“南山”、“崂山”、“趵突泉”命令便出现各自的信息。而“泰山”是灰色的,不能显示信息。 3)程序运行后,出项空白窗口,按Ctrl+F5、Ctrl+F6、Ctrl+F8组合键,分别在窗口中显示“蓬莱仙境”,“崂山道士”,“天下第一泉”。Ctrl+F7组合键变灰不能显示信息。 4)打开“山东旅游”菜单,按相应的助机记符(烟台)Y、(青岛)Q、(泰山)T、(济南)J键,便出现各自的子菜单,将鼠标放在子菜单“南山”上,按助记符N,便在文档窗口上显示出:南山大佛。 说明: void Invalidate(BOOL bErase = TRUE);该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其他窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。 第2章 菜单、工具栏和状态栏的设计 视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。 它和UpdateWindow( )区别在于: UpdateWindow( )的作用是使窗口立即重绘;调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其他消息发送完后才能被处理;调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。 2.1.2 菜单的编程控制 在交互式软件设计中,菜单有时会随着用户操作的改变而改变,这时的菜单就需要在程序中进行控制。MFC提供的菜单类CMenu可在程序运行时,处理有关菜单的操作,如:创建菜单、装入菜单、删除菜单、获取菜单或设置菜单的状态等。下面给出了CMenu类的常用成员函数。 1. 创建菜单 CMenu类的CreateMenu()和CreatePopupMenu()函数分别用于创建一个菜单或子菜单框架,它们的原型是: BOOL CreateMenu(); 产生一个空菜单 BOOL CreatePopupMenu();产生一个空的弹出式子菜单 2. 装入菜单 将菜单从资源装入应用程序中,需要调用Cmenu类成员函数LoadMenu或者用SetMenu对应用程序菜单进行重新设置。 BOOL LoadMenu(LPCTSTR lpszResourceName); BOOL LoadMenu(UINT nIDResource); 参数: lpszResourceName表示菜单资源名称; nIDResource表示菜单资源ID标识号。 3. 添加菜单项 当菜单创建后,用户可以调用AppendMenu或InsertMenu函数来添加一些菜单项。但每次添加时,AppendMenu是将菜单项添加在菜单的末尾处,而InsertMenu在菜单的指定位置处插入菜单项,并将后面的菜单项依次下移。 BOOL AppendMenu(UINT nFlags,UINT nIDNewItem=0, LPCTSTR lpszNewItem=NULL); BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const CBitmap *pBmp); BOOL InSertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0, LPCTSTR lpszNewItem=NULL); BOOL InSertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem, const CBitmap *pBmp); 参数: 第2章 菜单、工具栏和状态栏的设计 nIDNewItem 表示新菜单项的资源ID号; lpszNewItem表示新菜单项的内容; pBmp 用于菜单项的位图指针; nPosition 表示新菜单项要插入的菜单项位置; nFlags表示要增加的新菜单项的状态信息,其含义如表2.3所示。 表2.3 nFlags的值及其对其他参数的影响 nFlage值 含义 nPosition值 nIDNewItem值 lpszNewItem MF_BYCOMMAND 菜单项以ID标识符来标识 菜单项资源ID MF_BYPOSITION 菜单项以位置来标识 菜单项的位置 MF_POPUP 菜单项有弹出式子菜单 弹出式菜单句柄 MF_SEPARATOR 分割线 忽略 忽略 MF_OWNERDRAW 自画菜单项 自画所需的数据 MF_STRING 字符串标志 字符串指针 MF_CHECKED 设置菜单项的选中标记 MF_UNCHECKED 取消菜单项的选中标记 MF_DISABLED 禁用菜单项 MF_ENABLED 允许使用菜单项 MF_GRAYED 菜单项灰显 注意: (1)当nFlags为MF_BYPOSITION时,nPosition表示新菜单项要插入的具体位置,为0时表示第1个菜单项,为-1时,将菜单项添加至菜单的末尾处; (2)在nFlags的标志中,可以用“|”(按位或)来组合,例如MF_CHECKED|MF_STRING等。但有些组合是不允许的,例如MF_DISABLED、MF_ENABLED和MF_GRAYED,MF_STRING、MF_OWNERDRAW、MF_SEPARATOR和位图,MF_CHECKED和MF_UNCHECKED都不能组合在一起; (3)当菜单项增加、改变或删除后,不管菜单依附的窗口是否改变,都应调用CWnd::DrawMenuBar来更新菜单。 4. 删除菜单项 调用DeleteMenu函数可将指定的菜单项删除。函数DeleteMenu的原型如下: BOOL DeleteMenu(UINT nPosition,UINT nFlags); 参数: nPosition表示要删除的菜单项位置,它由nFlags进行说明; 当Flags为MF_BYCOMMAND时,nPosition表示菜单项的ID标识符; 第2章 菜单、工具栏和状态栏的设计 当Flags为MF_BYPOSITION时,nPosition表示菜单项的位置(第一个菜单项为0)。 5. 获取菜单项 以下三个CMenu成员函数分别获得菜单的项数、菜单项的ID标识符以及弹出式子菜单的句柄。 UINT GetMenuItemCount()const;获得菜单项的项数,调用失败返回-1 UINT GetMenuItemID(int nPos)const;获得由nPos指定菜单项位置(以0为基数)的菜单项的标识号,若nPos是SEPARATOR(分隔符)则返回-1 CMenu *GetSubMenu(int nPos)const;获得指定菜单的弹出式菜单的菜单句柄,该弹出式菜单位置由参数nPos指定,开始位置为0,若选单不存在,则创建一个临时菜单指针 [例2.3]用编写程序的方法添加并处理一个新的菜单项 (1)创建一个单文档(SDI)应用程序(或用例2.2程序),名为:添加菜单项。 (2)在程序窗口中选择View→ResourceSymbols命令,弹出如图2.6所示的对话框。 (3)单击New按钮,弹出New Symbol对话框,在Name(名字)文本框中输入一个新的标识符ID_NEW_MENUITEM。在Value(值)文本框中,输入该ID的值,如图2.7所示。系统要求用户定义的ID值应大于15(0X000F)而小于61440(0XF000)。这里保留默认的ID值101,单击OK按钮。 图2.6 “资源符号”对话框 图2.7 New Symbol对话框 (4)关闭“资源符号”对话框,在CMainFrame::OnCreate函数中添加下列代码,该函数在框架窗口创建时自动调用。 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { …… CMenu *pSysMenu=GetMenu();//获得主菜单句柄指针 CMenu *pSubMenu=pSysMenu->GetSubMenu(1);//获得第二个子菜单指针 CString StrMenuItem("新的菜单项");//字符串对象 pSubMenu→AppendMenu(MF_SEPARATOR);//增加一水平分割线(如表2.3所示) //在子菜单末尾增加一菜单项,允许使用ON_UPDATE_COMMAND_UI或ON_COMMAND的菜单 //项下面MF_STRING是字符串标志,ID_NEW_MENUITEM是新的菜单标识符,StrMenuItem是新菜单//项内容 pSubMenu->AppendMenu(MF_STRING,ID_NEW_MENUITEM,StrMenuItem); m_bAutoMenuEnable=FALSE;//关闭系统自动更新菜单状态,见下面的说明③ pSysMenu->EnableMenuItem(ID_NEW_MENUITEM,MF_BYCOMMAND| MF_ENABLED);//激活菜单项 第2章 菜单、工具栏和状态栏的设计 DrawMenuBar();//更新菜单 return 0; } (5)编译运行,程序添加的菜单如图2.8所示,但此时只是将菜单项加上了,命令却无反映。 (6)使用ClassWizard在CMainFrame主框架类中添加OnCommand消息函数的重载,并检测用户菜单的nID参数:在Class name中选择CMainFrame ,在Messages处找到OnCommand消息,将其映射到CMainFrame里并添加代码: BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam) { //参数wParam的低字节表示菜单、控件、加速键的命令ID if(LOWORD(wParam)==ID_NEW_MENUITEM) MessageBox("你选中了新的选单项"); } (7)编译运行并测试。选择“编辑”→“新的选单项”命令;弹出如图2.9所示的对话框,显示“你选中了新的选单项”。 图2.8 程序添加的菜单项 图2.9 菜单命令执行结果 说明: ① WPARAM wParam,MPARAM lParam参数,见第1章例1.20的说明。 ② LOWORD是获取其参数中的整型值位数。 下面是用C++语言编写的程序,可以看到LOWORD是取wParam参数的右边的4位数,即菜单的ID值(是用资源符号创建菜单的Value框中写的101)。 #include // #include #include int main() { int iInWord = 101; cout<GetMenu(); //获得程序窗口菜单指针 int nCount=pSysMenu->GetMenuItemCount();//获得顶层菜单个数 int nSubMenuPos=-1; //给特征变量送个标记,下面要用 for(int i=0;iGetSubMenu(nSubMenuPos) //获得"文件"下面的子菜单 ->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, //如表2.4所示 point.x,point.y,this);//在屏幕的任意地方显示一个弹出式菜单 } (3)在视图的执行文件“快捷菜单View.cpp”文件头部加:#include “MainFrm.h”。 (4)编译运行,在应用程序窗口的客户区中右击,会弹出快捷菜单(将原File下拉的菜单弹出来),如图2.10所示。 图2.10 例2.4快捷菜单显示结果 说明: ① WM_CONTEXTMENU:鼠标右键按下时发送的消息。 ② GetMenuString(i,str,MF_BYPOSITION);将指定菜单项的标签复制到指定的缓冲区。如果其最后的参数为MF_BYCOMMAND,则其他参数指定菜单项标识符。如果其最后的参数为MF_BYPOSITION,则其他参数指定菜单项位置。如果其最后的参数为: MF_BYPOSITION,这个参数就代表菜单条目在菜单中的位置(第一个菜单条目的位置为零)。 ③ AfxGetApp ()函数:是获取应用程序实例指针;GetMainWnd()函数:是获取主窗口对象指针。这两个函数可以合成一个 第2章 菜单、工具栏和状态栏的设计 :afxgetmainwnd();。通常把一些重要的工程一开始就需要初始化的并且在其他地方类中都要用到的变量或函数定义在C***App类中,然后通过此函数获得这些变量或函数。 由于菜单、工具栏、状态栏是由主框架类CMainFrame来控制的,虽在视图类可以添加快捷菜单消息映射,但若要在视图类中访问应用程序的主框架窗口的系统菜单,则必须通过AfxGetApp来获取主框架类对象指针后才能获取相应的菜单。AfxGetApp是CWinApp类的一个成员函数,该函数可在应用程序项目中的任何类中使用,用于获取应用程序中的CWinApp类对象指针。 [例2.5]使用快捷菜单(以快捷方式弹出自己设计的菜单项) (1)创建一个单文档应用程序,名为:建立快捷菜单。 (2)选择Insert→Resource命令,选择Menu,单击New按钮,便在Menu资源下出现一个新菜单资源(默认的ID号为IDR_MENU1),将此ID号改为:IDR_MYFLOATMENU。 (3)双击右边出现的空白菜单项,弹出Menu Item Properties对话框,在对话框中选中Pop up,在Caption处写:弹出快捷菜单,关闭对话框,打开下面的子菜单,依次添加如表2.5所示的子菜单项。 表2.5 添加的子菜单项 菜单ID 标 题 属 性 ID_MENU_SCOREIN 成绩输入(&S) 默认 ID_MENU_SCOREPRINT 成绩打印(&P) 默认 ID_SEPARATRO 选中Separator … 其他(&Q) 选中Pop_up,其余默认 (4)将ID_MENU_SCOREIN的COMMAND消息映射到主框架类MainFrame中(如果要弹出提问对话框,选择Select a new class,弹出Select Class对话框,选择对话框上的CMainFrame→Select,回到MFC ClassWizard,接着加如上ID的COMMAND消息)。 在Classname下拉列表框中选择CMainFrame,将上面的ID分别加COMMAND消息。这里仅添加ID_MENU_SCOREIN的COMMAND消息,并添加如下代码: void CMainFrame::OnMenuScorein() { AfxMessageBox("现在就输入成绩吗?"); } (5)在CMainFrame类加入WM_CONTEXTMENU消息处理函数,添加代码: void CMainFrame::OnContextMenu(CWnd *pWnd,CPoint point) { CMenu menu; menu.LoadMenu(IDR_MYFLOATMENU);//刚才加上的菜单资源 menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN| TPM_RIGHTBUTTON,point.x,point.y,this); } (6)运行并测试,在出现的应用程序窗口中右击,会弹出创建的快捷菜单,如图2.11所示。再选择“成绩输入”命令,会弹出写有“现在就输入成绩吗?”的对话框。 第2章 菜单、工具栏和状态栏的设计 2.2 工具栏 图2.11 例2.5快捷菜单显示结果 工具栏是一系列工具按钮的组合,借助它们可以提高用户的工作效率。Visual C++6.0系统保存了每个工具栏相应的位图,其中包括所有按钮的图像,而所有的按钮图像具有相同的尺寸(15像素高,16像素宽),它们在位图中的排列次序与屏幕上的按钮在工具栏上的次序相同。 2.2.1 使用工具栏编辑器 在项目工作区中打开ResourceView选项卡,双击Toolbar项目中的IDR_MAINFRAME,则工具栏编辑器出现在主界面的右边,如图2.12所示。 图2.12 工具栏编辑器 [例2.6]工具栏按钮和菜单联用 (1)创建一个SDI单文档应用程序,名为:工具栏。 (2)在ResourceView页打开Toobar文件夹,单击IDR_MAINFRAME,则工具栏编辑器出现在主界面的右边,单击最后一个空的工具栏按钮(创建完该工具栏按钮后,其后面又会自动出现一个新的空工具栏按钮),单击颜色工具箱的红色,再点图形工具箱的画刷,之后将鼠标移到下面较大的方块图上,来回移动鼠标将这个按钮涂红色后,如图2.12所示,再双击上面这个按钮,弹出属性对话框,如图2.13所示,在ID处写:ID_TOOLBAR,在“M提示”(注释)框内输入:建立新文档\n新建。 (3)将ID_TOOLBAR映射到View视图类中,并添加代码: void CMainFrame::OnToolbar() 第2章 菜单、工具栏和状态栏的设计 图2.13 工具栏按钮属性对话框 { MessageBox("工具栏显示"); } (4)编译运行鼠标放在刚创建的工具栏上出现“新建”框,而一会儿便消失。在状态栏上出现“建立新文档”字样。再单击这个按钮,在文档窗口出现小对话框,上面写的是“工具栏显示”。 (5)创建一个菜单项,在ResourceView页打开Menu,双击IDR_MAINFRAME,再双击右边出现的空白菜单,在标题处写:和工具栏联用,在ID处写(和工具栏的ID一样):ID_TOOLBAR,运行它,则点这个菜单和点刚才创建的工具栏的显示结果是一样的(这就是工具栏和菜单相结合)。鼠标左键按住这个菜单不放,状态栏出现“建立新文档”,放开鼠标,状态栏的“建立新文档”没了,但在文档窗口出现一个小对话框,和点刚才建的工具栏按钮一样,上面也写着“工具栏显示”。 说明: 1)移动一个工具栏按钮:用鼠标左键按住这个按钮,拖动至相应位置即可。 2)删除一个工具栏按钮:用鼠标左键按住这个按钮,拖动它离开工具栏。 3)复制一个工具栏按钮:若在移动一个按钮的同时,按下Ctrl键,则在新位置复制一个按钮。 4)在工具栏中插入空格有以下几种情况。 ① 如果工具栏按钮前没有空格,拖动该按钮向右移动并当覆盖相邻按钮的一半以上时,释放鼠标,则此按钮前出现空格。 ② 如果工具栏按钮前有空格而按钮后没空格:拖动该按钮向左移动并当按钮的左边界接触到前面按钮时,释放鼠标键,则此按钮后出现空格。 ③ 如果工具栏按钮前后均有空格,拖动该按钮向右移动并当接触到相邻按钮时,则此按钮前的空格保留,按钮后的空格消失。相反,拖动该按钮向左移动并当接触到前一个相邻按钮时,则此按钮前面的空格消失,后面的空格保留。 5)工具栏按钮属性的设置:双击某按钮弹出“工具栏按钮属性”对话框,如图2.13所示。“工具栏按钮属性”对话框中的各项含义如表2.6所示。 表2.6 工具栏按钮属性对话框中的各项含义 项 目 含 义 ID 工具栏按钮的标识符,可以输入自己的标识符名称,也可从ID的下拉列表中选取标识符名称 Width(宽) 工具栏按钮的像素宽度 Height(高) 工具栏按钮的像素高度 Prompt(提示) 工具栏按钮提示文本:若为“建立新文档\n新建”,则表示将鼠标指向该按钮时,在状态栏中显示“建立新文档”,而在弹出的提示信息中出现“新建”字样。“\n”是它们的分割转义符 第2章 菜单、工具栏和状态栏的设计 2.2.2 多个工具栏的使用 实际应用中,常常需要多个工具栏,下面就讨论多个工具栏的创建、显示和隐藏,以及多个工具栏和菜单之间的联动操作等。 [例2.7]使用多个工具栏 (1)添加并更改应用程序菜单 1)创建一个单文档应用程序,名为:多个工具栏。 2)打开Resource页,在资源类型中选中Menu(或按Ctrl+R组合键),单击New按钮,便在右边出现一个空菜单,系统给的默认ID为IDR_MENU1,如图2.14所示。 3)在Menu资源的ID_MENU1上右击,从弹出的快捷菜单中选择Properties命令,弹出如图2.15所示的Menu Properties对话框,在这里可以重新指定菜单资源ID,设置菜单资源的语言和条件。这个条件用于决定菜单资源包含到哪个环境中,例如当指定条件为_DEBUG,则菜单资源只存于Debug编译环境中。 图2.14 添加菜单资源后开发环境 图2.15 Menu Properties对话框 4)为菜单ID_MENU1添加一个顶层弹出菜单项:测试(&T),并在该菜单下添加一个子菜单:返回(&R),ID设为ID_TEST_RETURN,如图2.16所示。注意:“测试(&T)”中的符号“&”用于指定后面的字符(T)是一个助记符。 5)打开此程序的菜单资源Menu,双击IDR_MAINFRAME,在“查看”菜单的最后添加一个子菜单项:显示测试菜单(&M),将ID设为:ID_VIEW_TEST,如图2.17所示。 图2.16 设计新的菜单资源 图2.17 在“查看”下面建一个菜单项 第2章 菜单、工具栏和状态栏的设计 6)在CMainFrame类头文件MainFrm.h中的public下添加成员变量: CMenu m_NewMenu; 7)选择View→ClassWizard(或按Ctrl+W组合键)命令,在弹出的对话框中切换到Message Maps选项卡,从“Classname”列表中选择CMainFrame,分别为菜单项ID_VIEW_TEST和ID_TEST_RETURN添加COMMAND消息映射,使用默认的消息映射函数名,并添加代码: void CMainFrame::OnViewTest() { m_NewMenu.Detach(); //使菜单对象和菜单句柄分离 m_NewMenu.LoadMenu(IDR_MENU1); //将菜单从资源装入应用程序中 SetMenu(NULL); //清除应用程序菜单 SetMenu(&m_NewMenu); //设置应用程序菜单 } void CMainFrame::OnTestReturn() { m_NewMenu.Detach(); //使菜单对象和菜单句柄分离 m_NewMenu.LoadMenu(IDR_MAINFRAME); SetMenu(NULL); SetMenu(&m_NewMenu); } 说明: 代码中,LoadMenu和Detach都是CMenu类成员函数,LoadMenu用于装载菜单资源,而Detach是使菜单对象与菜单句柄分离,在调用LoadMenu后,菜单对象m_NewMenu就拥有一个菜单句炳,当再次调用LoadMenu时,由于菜单对象的句柄已经创建,因而会发生运行错误,但当菜单对象与菜单句柄分离后,就可以再次创建菜单了。SetMenu是CWnd类的一个成员函数,用于设置应用程序的菜单。 8)编译运行:单击菜单进行查看,显示测试菜单,菜单上面出现“测试”字样,选择“测试”→“返回”命令,又回到原菜单。 (2)添加并设计工具栏按钮 1)在ResourceView页打开resources,再打开Toolbar,双击IDR_MAINFRAME,显示出工具栏编辑器,利用工具栏编辑器设计两个工具栏按钮,设计结果如图2.18所示。 图2.18 设计的两个工具栏按钮 2)双击设计的第1个工具栏按钮,弹出该工具栏按钮的属性对话框,将该工具栏按钮的ID号设为:ID_VIEW_TEST,在提示框内输入: “显示测试菜单\n显示测试菜单”。 3)再双击设计的第2个工具按钮,弹出该工具栏按钮的属性对话框,将该工具栏按钮的ID号设为ID_TEST_RETURN,在提示框内输入:“返回应用程序主菜单\n返回主菜单”。 4)再编译运行后,将鼠标移至第1个工具栏按钮处,这时在状态栏上显示出“显示测试菜单 第2章 菜单、工具栏和状态栏的设计 ”,稍等片刻后,还会出现一个小窗口,也显示“显示测试菜单”字样。再将鼠标移至第2个工具栏按钮处,在状态栏上显示“返回应用程序主菜单”,稍等片刻后,又会出现一个小窗口,显示“返回主菜单”,如图2.19所示。先单击第1个按钮,再单击第2个按钮,会各自执行和菜单一样的命令。 图2.19 显示测试菜单 (3)添加工具栏 1)在项目工作区的ResourceView页面中,展开Toolbar(工具栏)资源,用鼠标单击IDR_MAINFRAME不松开,然后按下Ctrl键,移动鼠标将ID_MAINFRAME拖到Toolbar资源名称上,松开鼠标和Ctrl键,这样就复制了工具栏默认资源:ID_MAINFRAME,复制后的资源标识,系统自动设为:IDR_MAINFRAME1。 2)右击工具栏资源IDR_MAINFRAME1,从弹出的“工具栏属性”对话框中选择Properties命令,将ID设为IDR_TOOLBAR1,如图2.20所示。 3)双击IDR_TOOLBAR1,打开工具栏资源,删除(鼠标按住原有按钮不放,将其拖出工具栏即可)不要的工具栏按钮,如图2.21所示。 图2.20 “工具栏属性”对话框 图2.21 删除不要的工具按钮 4)在CMainFrame类头文件CMainFrm.h的public下添加成员变量: CToolBar m_wndTestBar;// CToolBar类封装了工具栏的操作 5)在CMainFrame::OnCreate函数中添加如下工具栏创建代码: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; int nRes = m_wndTestBar.CreateEx(this,TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS| CBRS_FLYBY|CBRS_SIZE_DYNAMIC,CRect(0,0,0,0), AFX_IDW_TOOLBAR+10); if(!nRes||!m_wndTestBar.LoadToolBar(IDR_TOOLBAR1)) { TRACE0("Failed to create toolbar\n"); return -1; 第2章 菜单、工具栏和状态栏的设计 } ……… m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); m_wndTestBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); DockControlBar(&m_wndTestBar); return 0; 图2.22 运行结果 } 6)编译运行,结果如图2.22所示,单击原工具栏新加的两个按钮和复制的两个工具栏钮,都执行和菜单一样的命令。 说明: ① CreateEx(this,TBSTYLE_FLAT,WS_CHILD| WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRSTOOLTIPS| CBRS_FLYBY|CBRS_SIZE_DYNAMIC,CRect(0,0,0,0), AFX_IDW_TOOLBAR+10);创建工具栏 第一个参数用于指定工具栏所在的父窗口指针(this表示当前的CMainFrame类窗口指针)。 第二个参数用于指定工具按钮的风格(TBSTYLE_FLAT表示工具按钮是“平面”的)。 第三个参数用于指针工具栏的风格,由于这里的工具栏是CMainFrame的子窗口,因此需要指定WS_CHILD|WS_VISIBLE(见第4章表4.1:创建子窗口|窗口最初是可见的)。 CBRS_TOP 将工具栏放在边框窗口的顶部 CBRS_BOTTOM 将工具栏放在边框窗口的底部 CBRS_GRIPPER 表示工具栏前面有一个“把手” CBRS_SIZE_DYNAMIC 表示工具栏在浮动时,其大小是可以动态改变的 CBRS_NOALIGN 边框窗口改变大小时,工具栏不重定位 CBRS_TOOLTIPS 使工具栏提示有效 CBRS_SIZE_FIXED 工具栏尺寸固定 CBRS_FLOATIONG 工具栏是浮动的 CBRS_FLYBY 在状态栏中显示按钮的有关信息 CBRS_HIDE_INPLACE 不显示工具栏 第四个参数用于指定工具栏四周的边框大小,一般都为0,如:CRect(0,0,0,0)。 最后一个参数用于指定工具栏这个子窗口的标识ID(与工具栏资源标识不同)。 ② if语句中的LoadToolBar函数用于装载工具栏资源。若CreateEx或 LoadToolBar的返回值为0,既调用不成功,则显示诊断信息“Failed to create toolbar”。 TRACEO是一个用于程序调试的跟踪宏OnCreate函数返回-1时,主框架窗口被清除。 第2章 菜单、工具栏和状态栏的设计 ③ 应用程序中的工具栏一般具有停靠或浮动性: m_wndTestBar.EnableDocking使得m_wndTestBar对象可以停靠。 CBRS_ALIGN_ANY表示可以停靠在窗口的任一边。 EnableDocking(CBRS_ALIGN_ANY)调用的是CFrameWnd类的成员函数,用于让工具栏或其他控制条在主框架窗口可以进行停靠操作。 DockControlBar也是CFrameWnd类的成员函数,用于将指定的工具栏或其他控制条进行停靠。 ④ 代码中的AFX_IDW_TOOLBAR是系统内部的工具栏子窗口标识,并将: AFX_IDW_TOOLBAR+1的值表示默认的状态栏子窗口标识。如果在创建新的工具栏时没有指定相应的子窗口标识,则会使用默认的AFX_IDM_TOOLBAR。这样,当选择“查看”菜单中的“工具栏”命令时,显示或隐藏的工具栏不是原来的工具栏,而是新添加的工具栏。为此,需要重新指定工具栏子窗口的标识,并使其值等于AFX_IDW_TOOLBAR+10。 (4)完善程序代码 1)调用CFrameWnd类的成员函数ShowControlBar,使程序一开始运行时,将工具栏IDR_TOOLBAR1隐藏起来。 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { … … ShowControlBar(&m_wndTestBar,FALSE,FALSE);//关闭测试工具栏 return 0; } 说明: ShowControlBar函数有3个参数,第1个参数用于指定要操作的工具栏或状态栏指针,第2个参数是一个布尔型,当为TRUE时表示显示,否则表示隐藏,第3个参数用于表示是否延迟显示或隐藏,当为FALSE时表示立即显示或隐藏。 2)在CMainFrame::OnViewTest和CMainFrame::OnTestReturn函数中添加下列代码: void CMainFrame::OnViewTest() { … … ShowControlBar(&m_wndTestBar,TRUE,FALSE); //显示测试工具栏 ShowControlBar(&m_wndToolBar,FALSE,FALSE);//关闭主工具栏 } void CMainFrame::OnTestReturn() { ShowControlBar(&m_wndTestBar,FALSE,FALSE);//关闭测试工具栏 ShowControlBar(&m_wndToolBar,TRUE,FALSE); //显示主工具栏 } 3)编译运行:结果如图2.23和图2.24所示。 第2章 菜单、工具栏和状态栏的设计 图2.23 程序运行的结果 图2.24 单击前面的工具按钮后结果 2.3 状态栏 状态栏是一条水平长条,位于应用程序的主窗口的底部,它可以分割成几个窗格,用来显示多组信息。应用程序往往需要把当前的状态信息或提示信息告诉用户,虽然其他窗口(如窗口的标题栏上、提示窗口等)也可显示文本,但它们的功能比较有限,而状态栏能很好地满足应用程序显示信息的需求。 2.3.1 状态栏的定义 在MFC AppWizard创建的SDI或MDI应用程序框架的MainFrm.cpp文件中有一个静态数组indicators数组,它被MFC用做状态栏的定义,如图2.25所示。 图2.25 indicators数组元素与标准状态栏窗口的关系 这个数组中的元素是一些标识常量或是字串资源的ID标识符。默认的indicators数组包含了以下4个元素。 ID_SEPARATOR:是用于标识信息行窗格的,菜单项或工具栏按钮的许多信息都在这个信息行窗格中显示。 ID_INDICATOR_CAPS:是用于标识指示器窗格显示出CapsLock键的状态(大写),CapsLock键就是在键盘左边的那个控制大小写转换的键。 ID_INDICATOR_NUM:是用于标识指示器窗格显示出NumLock键状态(数字)。 第2章 菜单、工具栏和状态栏的设计 ID_INDICATOR_SCRL:是用于标识指示器窗格显示出ScrollLock键的状态(滚动)。 2.3.2 状态栏的常用操作 Visual C++6.0中可以方便地对状态栏进行操作,如增加窗格、减少窗格、在状态栏中显示文本、改变状态栏的风格大小等,并且MFC的CStatusBar类封装了状态栏的大部分操作。 1. 增加和减少窗格 状态栏中的窗格可以分为信息行窗格和指示器窗格两类。 (1)若在状态栏中增加一个信息行窗格,则只需要在indicators数组的适当位置增加一个ID_SEPARATOR标识即可。 (2)若在状态栏中增加一个用户指示器窗格,则在indicators数组中的适当位置增加一个在字符串表中定义过的资源ID,其字符串的长度表示用户指示器窗格的大小。 (3)若状态栏减少一个窗格,其操作与增加相类似,只需减少indicators数组元素即可。 2. 在状态栏上显示文本 (1)调用CWnd::SetWindowText更新信息行窗格(或窗格0)中的文本。由于状态栏也是一种窗口,故在使用时可直接调用。若状态栏变量为m_wndStatusBar,则显示为m_wndStatusBar。SetWindowText(“消息”)语句将在信息行窗格(或窗格0)内显示“消息”字样。 (2)手动处理状态栏的ON_UPDATE_COMMAND_UI更新消息,并在处理函数中调用CCmdUI::SetText函数。 (3)调用CStatusBar::SetPaneText函数更新任何窗格(包括信息行窗格)中的文本,此函数原型描述如下: BOOL SetPaneText(int nIndex,LPCTSTR lpszNewText,BOOL bUpdate=TRUE); 参数: ① nIndex是表示设置的窗格索引(第1个窗格的索引为0); ② lpszNewText表示要显示的字符串,若bUpdate为TRUE,则系统自动更新显示的结果。下面用两种方法在状态栏中显示鼠标在客户区的位置。 [例2.8]在状态栏的最右边两个窗格中显示出当前鼠标在窗口客户区的位置 (1)创建一个单文档应用程序(或用上文中“多个工具栏”程序),名为:状态栏。 (2)将项目工作区切换到ClassView选项卡,展开CMainFrame所有项,双击CMainFrame()函数,在文档窗口中出现该函数的定义,在它的前面是状态栏数组的定义。 (3)将状态栏indicators数组的定义改为下列代码: static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_SEPARATOR, }; (4)将鼠标移动消息WM_MOUSEMOVE映射到视图类“状态栏View”中。 由于鼠标移动消息WM_MOUSEMOVE 在CMainFrame类映射后不起作用,因此只能映射到视图“状态栏View”类中。但是,这样一来,就需要更多的代码,因为状态栏对象m_wndStatusBar是CMainFrame类定义的成员变量,因而需要在视图类“状态栏View”中添加访问CMainFrame类的代码。 第2章 菜单、工具栏和状态栏的设计 void CNnView::OnMouseMove(UINT nFlags, CPoint point) { CString str; //获得主窗口指针 CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd; //获得主窗口中的状态栏指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; if(pStatus) { str.Format("X=%d,Y=%d",point.x,point.y);//格式化文本 //这里"1"是更新第2个窗格的文本,而"0"是第1个窗格 pStatus->SetPaneText(1,str); } } (5)将MainFrm.h文件中的受保护变量m_wndStatusBar改为公共变量。 (6)在“状态栏View.cpp”视图类执行文件的头部写:#include “MainFrm.h”。 (7)编译运行,结果如图2.26所示。 3. 改变状态栏的风格 在MFC的CStatusBar类中,有两个成员函数可以改变状态栏风格,它们是: void SetPaneInfo(int nIndex,UINT nID,UINT nStyle,int cxWidth); viod SetPaneStyle(int nIndex,UINT nStyle); 参数: nIndex表示要设置的状态栏窗格的索引; nID用于为状态栏窗格指定新的ID; cxWidth表示窗格的像素宽度; nStyle表示窗格的风格类型,用于指定窗格的外观,例如SBPS_POPOUT表示窗格是凸起来的,状态栏窗格的风格类型如表2.7所示。 表2.7 状态栏窗格的风格类型 风格类型 含 义 SBPS_NOBORDERS 窗口周围没有3D边框 SBPS_POPOUT 反显边界以使文字“凸出来” SBPS_DISABLED 禁用窗格,不显示文本 SBPS_STRETCH 拉伸窗格,并填充窗格不用的空白空间。但状态栏只能有一个窗格具有这种风格 SBPS_NORMAL 普通风格,它没有“拉伸”,“3D边框”或“凸出来”等特性 (8)在上面的OnMouseMove(UINT nFlags,CPoint point)函数里添加: void CNnView::OnMouseMove(UINT nFlags, CPoint point) { … … //下面函数第一个参数表示状态栏窗格索引,第二个参数如表2.7所示 第2章 菜单、工具栏和状态栏的设计 pStatus->SetPaneStyle(1,SBPS_POPOUT); str.Format("X=%d,Y=%d",point.x,point.y); //格式化文本 pStatus->SetPaneText(1,str); //更新第2个窗格的文本 } 编译运行,见状态栏的第2个窗格凸起来了,如图2.27所示。 鼠标位置窗格凸起来了 鼠标位置窗格凹下去的 图2.26 鼠标的位置显示在状态栏上 图2.27 改变状态栏的风格 [例2.9]用与例2.8不同的方法,在状态栏的最右边两个窗格中显示出当前鼠标在窗口客户区的位置 (1)创建一个单文档的应用程序,名为:状态栏风格。 (2)将项目工作区窗口切换到ResourceView选项卡,双击String Table项的 String Table图标,则在主界面的右边出现字符串编辑器。在字符串列表的最后一行的 空项上双击,弹出一个对话框,如图2.28所示。 图2.28 “字符串属性”对话框 (3)在该对话框中,用户可以指定相应的ID和字符串值,这里加入两个ID和其字符串资源,即:ID_LEFT(在Caption处写:X=999)和ID_RIGHT(在Caption处写:Y=999),其字符的多少决定窗格的大小,其结果如图2.29所示。 (4)打开MainFrm.cpp文件,将原先的indicators数组修改如下: static UINT indicators[] = { ID_SEPARATOR, //第一个信息行窗格 ID_SEPARATOR, //第二个信息行窗格 ID_LEFT, //第三个窗格 第2章 菜单、工具栏和状态栏的设计 ID_RIGHT, //第四个窗格 }; 图2.29 添加的字符串资源 (5)由于ClassWizard不能组织相应的命令更新消息的映射,用户必须手工添加消息处理函数原型。打开CMyView.h文件,在AFX_MSG内增加消息处理语句,ClassWizard以后允许用户访问和编辑该代码。 protected: //{{AFX_MSG(CQqView) // NOTE - the ClassWizard will add and remove member functions here. afx_msg void OnUpdateXY(CCmdUI *pCmdUI); // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG 说明: CCmdUI没有基类,和CCmdTarget派生类的ON_UPDATE_COMMAND_UI句柄一起使用,当用户选择一个菜单,每个菜单项需要知道自己是显示成可以使用的还是不可以使用的,菜单项通过执行ON_UPDATE_COMMAND_UI局柄来实现它; 当菜单被选择时,框架寻找并且唤醒ON_UPDATE_COMMAND_UI句柄,每一个句柄调用CCmdUI的一个功能(如Enable and Check),然后框架就相应的现实每一个菜单项,一个菜单项不用改变实现ON_UPDATE_COMMAND_UI的代码就能够用按钮或快捷键代替。 (6)打开View.cpp文件,在其消息映射入口处添加消息映射宏函数: BEGIN_MESSAGE_MAP(CQqView, CView) //{{AFX_MSG_MAP(CQqView) // NOTE - the ClassWizard will add and remove mapping macros here. 第2章 菜单、工具栏和状态栏的设计 ON_UPDATE_COMMAND_UI(ID_LEFT,OnUpdateXY) ON_UPDATE_COMMAND_UI(ID_RIGHT,OnUpdateXY) // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP (7)在视图类CMyView.cpp文件的末尾,增加修改状态栏指示器的消息映射函数代码,当状态栏的窗格需要更新时,应用程序框架自动调用此函数(全用手写)。 void CMyView::OnUpdateXY(CCmdUI *pCmdUI) { pCmdUI->Enable(TRUE); } //使窗格文本能被更新 说明: CCmdUI类对象方法(成员函数也叫对象方法),如表2.8所示。 表2.8 CCmdUI类对象方法 对象方法 作 用 ContinueRouting() 告诉命令发送机构沿着handlers键继续发送当前的消息 Enable() 为该命令激活或关闭用户界面项 SetCheck() 为该命令设置用户界面项的核对状态 SetRadio() 类似SetCheck成员函数,但通过单选组操作 SetText() 为这个命令设置用于用户界面的文本 (8)用ClassWizard在视图类CMyView中加入WM_MOUSEMOVE(鼠标移动)消息处理函数,并添加下列代码。该函数先获得状态栏对象的指针,然后调用SetPaneText函数更新第3和第4窗格中的文本。 void CMyView::OnMouseMove(UINT nFlags,CPoint point) { CString str; CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd; //获得主窗口指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; //获得主窗口中的状态栏指针 if(pStatus) { str.Format("X=%d",point.x);//格式化文本 pStatus->SetPaneText(2,str);//更新第三个窗格的文本 str.Format("Y=%d",point.y); pStatus->SetPaneText(3,str);//更新第四个窗格的文本 } } (9)将MainFrm.h文件中的受保护变量m_wndStatusBar变成公共变量。 (10)在视图View.cpp文件的开始处增加语句:#include “MainFrm.h”。 (11)编译运行,如图2.30所示,鼠标位置就在状态栏里显示出来了。 (12)改变状态栏的风格:将例2.8中的鼠标移动函数OnMouseMove(UINT nFlags,CPoint point)代码修改为: 第2章 菜单、工具栏和状态栏的设计 void CQqView::OnMouseMove(UINT nFlags, CPoint point) { CString str; CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;//获得主窗口指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; //获得主窗口中的状态栏指针 if(pStatus) { pStatus->SetPaneStyle(2,SBPS_POPOUT);//第2个栏 pStatus->SetPaneStyle(3,SBPS_POPOUT);//第3个栏 str.Format("X=%d",point.x); //格式化文本 ………………………………… } CView::OnMouseMove(nFlags, point); } (13)编译运行,如图2.31所示,第2和第3窗格凸起来了。 鼠标位置窗格凸起来了 鼠标位置窗格凹下去的 图2.30 鼠标的位置显示在状态栏上 图2.31 设置状态栏的风格 2.4 交互对象的动态更新 用户交互对象是指可由用户操作而产生命令消息的对象,如:菜单项、工具条中的按钮和加速键,每个用户交互对象都有一个唯一的ID号,在发送消息时,该ID号被包含在WM_COMMAND消息中。特别是:菜单项可以有灰色显示,选中和未选中三种状态,而工具栏按钮则可以有禁止和选中状态等。 为了能使用户交互对象动态更新,MFC是通过ClassWizard直接映射交互对象的更新命令消息来实现的。它自动将用户交互对象的ID标识符与ON_UPDATE_COMMAND_UI宏相连接并产生处理更新消息的相应函数。为说明交互对象的动态更新,看例2.10。 [例2.10]交互对象的动态更新 (1)创建一个单文档的应用程序,名为:动态更新。 (2)在ResourceView页中打开Toolbar,选中IDR_MAINFRAME,按住Ctrl键,移动鼠标将IDR_MAINFRAME拖到Toolbar资源名上,这样就复制了工具栏IDR_MAINFRAME,复制后的资源标识,系统自动设为IDR_MAINFRAME1。 (3)右击IDR_MAINFRAME1,从弹出的快捷菜单中选择Properties命令,在弹出的属性对话框中将ID改为IDR_NEWBAR。 第2章 菜单、工具栏和状态栏的设计 (4)删除几个IDR_NEWBAR上的工具按钮,以与IDR_MAINFRAME有区别。 (5)在主框架CMainFrame类的头文件MainFrm.h的protected下,声明变量: CToolBar m_wndNewBar; BOOL m_bNewBar; (6)在CMainFrame::OnCreate中添加下列代码: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if(!m_wndNewBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE| CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY| CBRS_SIZE_DYNAMIC,CRect(0,0,0,0),AFX_IDW_TOOLBAR+10)|| !m_wndNewBar.LoadToolBar(IDR_NEWBAR)) { TRACE0("Failed to ctreate newbar\n"); return -1; //fail to create } … … m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); m_wndNewBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); DockControlBar(&m_wndNewBar); return 0; } (7)打开菜单资源Menu,双击IDR_MAINFRAME,在“查看”菜单下添加一个菜单项,名为:新工具栏(&N),ID标识符设定为ID_VIEW_NEWBAR。 (8)用MFC ClassWizard在CMainFrame类中添加菜单ID_VIEW_NEWBAR的COMMAND和UPDATE_COMMAND_UI两个消息映射,并在映射函数中添加下列代码: void CMainFrame::OnViewNewbar() { m_bNewBar=!m_bNewBar; ShowControlBar(&m_wndNewBar,m_bNewBar,FALSE);//显示或隐藏工具栏 } void CMainFrame::OnUpdateViewNewbar(CCmdUI* pCmdUI) { m_bNewBar=m_wndNewBar.IsWindowVisible(); //pCmdUI->SetRadio(m_bNewBar); //选中用(•) pCmdUI->SetCheck(m_bNewBar); //选中(√) } 说明: ① 代码中,OnUpdateViewNewbar是ID_VIEW_NEWBAR的更新命令消息的消息映射函数。该函数只有1个参数,它是指向CCmdUI对象的指针。CCmdUI类仅用于ON_UPDATE_COMMAND_UI消息映射函数,它的成员函数将对菜单项、工具栏按钮等用户交互对象起作用,如表2. 第2章 菜单、工具栏和状态栏的设计 9所示。 ② 调用Create时,还可以指定工具栏的风格,默认风格是: WS_CHILD|WS_VISIBLE|CBRS_TOP创建子窗口|窗口最初是可见的(见第4章表4.1)。 ③ 以WS_为开头表示窗口风格,以SW_开头表示窗口状态的改变(见第4章表4.3)。 (9)编译运行:打开“查看”菜单,可以看到“新工具栏”菜单前面有一个“√”,如图2.32所示。选择“新工具栏”命令,则新创建的工具栏不见了,而“新工具栏”菜单前面的标记√没有了。 若将代码中SetCheck改为SetRadio,则“√”变成了“· ”,这就是交互对象的更新效果。 选择后√消失,新工具栏不见了 新加菜单前带√ 新加工具栏 图2.32 查看下拉菜单 图2.33 选择“新工具栏”命令 表2.9 CComdUI类的成员函数对用户交互对象的作用 用户交互对象 Enable SetCheck SetRadio SetText 菜单项 允许或禁用 选中(√)或未选中 选定用(•) 设置菜单文本 工具栏按钮 允许或禁用 选定、未选定或不确定 同SetCheck 无效 状态栏窗格(PANE) 使文本可见或不可见 边框外凸或正常 同SetCheck 设置窗格文本 CDialogBar中的按钮 允许或禁用 选中或未选中 同SetCheck 设置按钮文本 CDialogBar中的控件 允许或禁用 无效 无效 设置窗口文本 2.5 章后实训 实训1 通用菜单 (1)创建一个单文档应用程序,名为:通用菜单。 (2)建立菜单:在顶层菜单“帮助”的后面连续建立名为:图形颜色(&C),图形边宽(&W),可选 第2章 菜单、工具栏和状态栏的设计 图形(&G),属性选为:√Pop Up的3个菜单,如图2.34所示。 建立的三个菜单 图2.34 建立的三个顶层菜单 图2.35 “图形颜色”下建的三个子菜单 (3)按表2.10在“图形颜色”菜单下面连续建立三个子菜单,如图2.35所示。 表2.10 “图形颜色”的子菜单 ID 标题(Caption) 提示(Prompt) ID_COLOR_RED 红色(&R) 你选择了红色 ID_COLOR_GREEN 绿色(&G) 你选择了绿色 ID_COLOR_BLUE 蓝色(&B) 你选择了蓝色 按表2.11在“图形边宽”菜单下面连续建立三个子菜单。 表2.11 “图形边宽”的子菜单 ID 标题(Caption) 提示(Prompt) ID_LINE_SINGE 单线宽(&S) 你选择了单线宽 ID_LINE_THREE 三线宽(&T) 你选择了三线宽 ID_LINE_FIVE 五线宽(&F) 你选择了五线宽 按表2.12在“可选图形”菜单下面连续建立三个子菜单。 表2.12 “可选图形”的子菜单 ID 标题(Caption) 提示(Prompt) ID_GRAPH_LINE 直线(&L) 你选择了直线 ID_GRAPH_CIRCLE 椭圆(&C) 你选择了椭圆 ID_GRAPH_RECTANGLE 矩形(&R) 你选择了矩形 (4)在主框架CmainFrame.cpp加入各菜单COMMAND(命令消息)和UPDATE_COMMAND_UI(更新消息)处理函数: 1)加入“图形颜色”菜单下面三个子菜单: ID_COLOR_RED,ID_COLOR_GREEN,ID_COLOR_BLUE的COMMAND命令消息和UPDATE_COMMAND_UI更新命令消息。 第2章 菜单、工具栏和状态栏的设计 2)加入“图形边宽”菜单下面三个子菜单: ID_LINE_SINGLE,ID_LINE_THREE,ID_LINE_FIVE的COMMAND命令消息和UPDATE_COMMAND_UI更新命令消息。 3)加入“可选图形”菜单下面三个子菜单: ID_GRAPH_LINE,ID_GRAPH_CIRCLE,ID_GRAPH_RECTANGLE的COMMAND命令消息和UPDATE_COMMAND_UI更新命令消息。 (5)在CmainFrame类添加成员变量:在项目工作区的ClassView选项卡中右击CmainFram,在弹出的快捷菜单中选择Add Member Variable命令,弹出Add Member Variable对话框,在Variable Type处写:COLORREF,在Variable Name处写:m_Color。接着按此方法添加:int m_Thickness 、UINT m_Graph 、UINT m_TagColor三个变量。 (6)在CmainFrame.cpp的构造函数中,为以上添加的4个成员变量赋初值: CMainFrame::CMainFrame() { m_Color=RGB(255,0,0);//初始颜色为红 m_TagColor=ID_COLOR_RED;//初始颜色ID标识符为红 m_Graph=ID_GRAPH_LINE;//初始图形的ID标识符为直线 int m_Thickness=1; //画图形时线的宽度 } (7)创建浮动菜单:选择Insert->Resource命令,弹出Insert Resource对话框,选中“Menu”,单击New按钮,如图2.36所示,双击右边的空白菜单,弹出Menu Item Properties对话框,在General选项卡中选中“√Pop_Up”,“Caption”(标题)处写:浮动菜单。 (8)将该菜单项的标识符“ID_MENU1”改为:IDR_PopUpMenu。 (9)双击“Menu”下的标识符:IDR_MAINFRAME,右击“图形颜色”菜单,选择Cope命令。再双击“Menu”下的IDR_PopUpMenu菜单,右边出现“浮动菜单”,在“浮动菜单”下面空白子菜单上右击,在弹出的快捷菜单中选择Paste命令。用相同的方法将“图形边宽”和“可选图形”复制到“浮动菜单”的下一个子菜单下,如图2.37所示。 图2.36 插入的菜单项 图2.37 将加入的菜单复制到浮动菜单下 第2章 菜单、工具栏和状态栏的设计 (10)为第(4)步在CmainFrame类添加的各个菜单命令COMMAND消息和UPDATE_COMMAND_UI更新命令消息处理函数加代码: //选择"图形颜色"→"红色"命令后的消息处理函数 void CMainFrame::OnColorRed() { m_Color=RGB(255,0,0);//表示选择了红色 m_TagColor=ID_COLOR_RED;//设置颜色标志为ID_COLOR_RED } //选择"图形颜色"→"红色"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorRed(CCmdUI* pCmdUI) { //判颜色标识是否等于该命令对应的标识符ID_COLOR_RED,若相等则在该菜单命令左边显示"√" pCmdUI→SetCheck(m_TagColor==ID_COLOR_RED?1:0); } //选择"图形颜色"→"绿色"命令后的消息处理函数 void CMainFrame::OnColorGreen() { m_Color=RGB(0,255,0); //表示选择了绿色 m_TagColor=ID_COLOR_GREEN; //设置颜色标志为ID_COLOR_GREEN } //选择"图形颜色"→"绿色"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorGreen(CCmdUI* pCmdUI) {//判颜色标识是否等于该命令对应标识符ID_COLOR_GREEN,若相等则在该菜单命令左边显示"√" pCmdUI->SetCheck(m_TagColor==ID_COLOR_GREEN?1:0); } //选择"图形颜色"→"蓝色"命令后的消息处理函数 void CMainFrame::OnColorBlue() { m_Color=RGB(0,0,255); //表示选择了蓝色 m_TagColor=ID_COLOR_BLUE; //设置颜色标志为ID_COLOR_BLUE } //选择"图形颜色"→"蓝色"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorBlue(CCmdUI* pCmdUI) { //判颜色标识是否等于该命令对应标识符ID_COLOR_BLUE,若相等则在该菜单命令左边显示"√" pCmdUI->SetCheck(m_TagColor==ID_COLOR_BLUE?1:0); } //选择"图形边宽"→"单线宽"命令后的消息处理函数 void CMainFrame::OnLineSingle() { m_Thickness=1;//设置为单线宽 } //选择"图形边宽"→"单线宽"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateLineSingle(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于1,若相等则在该菜单命令左边显示"√" 第2章 菜单、工具栏和状态栏的设计 pCmdUI->SetCheck(m_Thickness==1?1:0); } //选择"图形边宽"→"三线宽"命令后的消息处理函数 void CMainFrame::OnLineThree() { m_Thickness=3; //设置为三线宽 } //选择"图形边宽"→"三线宽"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateLineThree(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于3,若相等则在该菜单命令左边显示"Ö" pCmdUI->SetCheck(m_Thickness==3?1:0); } //选择"图形边宽"→"五线宽"命令后的消息处理函数 void CMainFrame::OnLineFive() { m_Thickness=5; //设置为五线宽 } //选择"图形边宽"→"五线宽"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateLineFive(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于5,若相等则在该菜单命令左边显示"Ö" pCmdUI->SetCheck(m_Thickness==5?1:0); } //选择"可选图形"→"直线"命令后的消息处理函数 void CMainFrame::OnGraphLine() { m_Graph=ID_GRAPH_LINE;//设置图形标识为ID_GRAPH_LINE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.MoveTo(100,310); cdc.LineTo(500,310); cdc.SelectObject(oPen); } //选择"可选图形"→"直线"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphLine(CCmdUI* pCmdUI) { //判图形标识是否等于该命令对应标识符ID_GRAPH_LINE,若相等则在该菜单命令左边显示"√" pCmdUI->SetCheck(m_Graph==ID_GRAPH_LINE?1:0); } 第2章 菜单、工具栏和状态栏的设计 //选择"可选图形"→"椭圆"命令后的消息处理函数 void CMainFrame::OnGraphCircle() { m_Graph=ID_GRAPH_CIRCLE;//设置图形标识为ID_GRAPH_CIRCLE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Ellipse(100,100,200,200); cdc.SelectObject(oPen); } //选择"图形颜色"→"椭圆"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphCircle(CCmdUI* pCmdUI) {//判图形标识是否等于该命令对应标识符ID_GRAPH_CIRCLE,若相等则在该菜单命令左边显示"√" pCmdUI->SetCheck(m_Graph==ID_GRAPH_CIRCLE?1:0); } //选择"可选图形"→"矩形"命令后的消息处理函数 void CMainFrame::OnGraphRectangle() { m_Graph=ID_GRAPH_RECTANGLE; //设置图形标识为ID_GRAPH_RECTANGLE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Rectangle(350,100,450,200); cdc.SelectObject(oPen); } //选择"可选图形"→"矩形"命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphRectangle(CCmdUI* pCmdUI) {//判图形标识是否等该命令对应标识符ID_GRAPH_RECTANGLE,若等则在该菜单命令左边显示"√" pCmdUI->SetCheck(m_Graph==ID_GRAPH_RECTANGLE?1:0); } 第2章 菜单、工具栏和状态栏的设计 (11)在视图类(CMyView)中加入WM_RBUTTONDOWN(鼠标右键按下)的消息处理函数,并添加代码: //在视图窗口中右击时的消息处理函数的定义 void CMyView::OnRButtonDown(UINT nFlags, CPoint point) { CMenu popMenu; //声明菜单类对象 //LoadMenu成员函数从一个执行文件中获取一个菜单资源,并把它分配给CMenu对象,该操作若成 //功则继续,否则抛出异常 if(!popMenu.LoadMenu(IDR_PopUpMenu)) ::AfxThrowResourceException(); //GetSubMenu成员函数返回一个指向CMenu对象的指针,若给定位置不存在浮动菜单,返回NULL CMenu * pPopUpMenu=popMenu.GetSubMenu(0); //若不存在浮动菜单,将抛出异常 if(pPopUpMenu==NULL) ::AfxThrowResourceException(); this->ClientToScreen(&point); //TrackPopupMenu成员函数在确定位置显示悬浮式弹出菜单,并跟踪此菜单的项目选择过程 pPopUpMenu->TrackPopupMenu(TPM_CENTERALIGN|TPM_RIGHTBUTTON, point.x,point.y,::AfxGetMainWnd()); CView::OnRButtonDown(nFlags, point); } (12)编译运行,如图2.38和图2.39所示。在显示图形时,视图窗口背景色被设置为灰色。在视图窗口中右击,则出现浮动菜单,其实现的结果与顶层菜单是一样的。 图2.38 在窗口中右击出现浮动菜单 图2.39 选择“可选图形”→“椭圆” 实训2 多信息状态栏 (1)打开上面的“实训1—通用菜单”程序,在该程序基础上继续操作。 (2)选择View→Resource Symbols(资源符号)命令,弹出Resource Symbols对话框,单击New按钮,弹出New Symbol对话框,在Name文本框中输入:ID_STATUSPART1→系统自动在Value(值)文本框中加入一个值,如图2.40所示。按同样方法,依次加入资源符号名为:ID_STATUSPART2、ID_STATUSPART3和IDS_STATUS_TIMER。 第2章 菜单、工具栏和状态栏的设计 图2.40 添加资源符号 图2.41 添加串属性表 (3)在项目工作区的Resource View选项卡中展开String Table,双击标识符String Table,打开字符串编辑窗口,双击最后一个空白行,弹出String Properties对话框,在ID文本框中输入:ID_STATUSPART1,在Caption文本框中输入:ztguang,如图2.41所示。用同样的办法,依次添加表2.13所列出的串属性。 表2.13 串属性表 Control IDs 标 题 ID_STATUSPART2 ztguang ID_STATUSPART3 ztguang IDS_STATUS_TIMER 00:00 (4)在主框架头文件CMainFrm.h的public下定义变量和声明函数。 public: CString m_StatusPart1; //存放颜色红、绿、蓝 CString info1; //在状态栏显示红、绿、蓝 CString m_StatusPart2; //存放单线宽、三线宽、五线宽 CString info2; //在状态栏显示单线宽、三线宽、五线宽 CString m_StatusPart3; //存放图形直线、椭圆、矩形 CString info3; //在状态栏显示直线、椭圆、矩形 UINT m_nIDTimer; //每秒钟发送一个消息到应用程序的消息队列,该变量存放时间消息队列 CStatusBar m_StatusBar; //声明类CStatusBar对象 //添加TimerProc()函数的声明 static void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT uIDEvent,DWORD dwTime); //添加ShowStatusBarInfo()函数的声明 void ShowStatusBarInfo(); //显示状态条信息 //在消息映射处加入新的消息映射函数声明 protected: //{{AFX_MSG(CMainFrame) 第2章 菜单、工具栏和状态栏的设计 afx_msg void OnUpdateTime(CCmdUI *pCmdUI);//声明状态条时间显示函数 //}}AFX_MSG (5)在主框架执行文件CMainFrm.cpp中添加代码: 1)消息映射开始处添加代码: BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_UPDATE_COMMAND_UI(IDS_STATUS_TIMER,OnUpdateTime)//状态条时间显示 //}}AFX_MSG_MAP END_MESSAGE_MAP() 2)修改状态栏内容的代码如下: static UINT indicators[] = { ID_SEPARATOR,//在状态条左边生成空的状态栏目,用于菜单命令和工具条命令提示信息的显示 ID_STATUSPART1,//显示选择的图形颜色 ID_STATUSPART2,//显示选择的图形边宽 ID_STATUSPART3,//显示选择的图形 ID_INDICATOR_CAPS,//显示键盘键Caps Lock键的状态 ID_INDICATOR_NUM,//显示键盘键NumLock键的状态 ID_INDICATOR_SCRL,//显示键盘键ScrollLock键的状态 IDS_STATUS_TIMER,//显示系统时间 }; 3)在构造函数中为变量赋初值的代码如下: CMainFrame::CMainFrame() { m_Color=RGB(0,0,0);//m_Color=RGB(255,0,0);//颜色 m_Thickness=0;//m_Thickness=1;//线宽 m_Graph=0;//m_Graph=ID_GRAPH_LINE;//图形ID m_TagColor=0;//m_TagColor=ID_COLOR_RED;//颜色ID m_StatusPart1="ztguang";//颜色栏初值 m_StatusPart2="ztguang";//线宽栏初值 m_StatusPart3="ztguang";//图形栏初值 } 4)在析构函数中加清除(应用程序消息队列)函数: CMainFrame::~CMainFrame() { ::KillTimer(NULL,m_nIDTimer);// 清空时间消息队列 } (6)修改主框架类(CMainFrame)OnCreate(LPCREATESTRUCT lpCreateStruct)消息处理函数: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; //Create()建立一状态条子窗口,并将它和CstatausBar对象联系在一起,同时按默认 第2章 菜单、工具栏和状态栏的设计 //值设定状态栏的字模和高度 if (!m_StatusBar.Create(this) || //Setlndicators()按照数组indicators中的对应元素的值设定标识符ID值,加载由每 //个ID所指定的字符串资源,并把字符串设置为标识符的文字 !m_StatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } //发送消息,以便更新状态条时间,该语句每秒钟发送一个消息到应用程序的消息队列,当应用程序 //清空其消息队列时,时间显示将被更新 m_nIDTimer = ::SetTimer(NULL, 0, 1000, TimerProc); //可将原代码全注释掉 return(); } (7)为主框架执行文件CMainFrm.cpp中的各个菜单命令函数添加代码: void CMainFrame::OnColorRed() {//当选择"图形颜色"→"红色"命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart1="红色"; ShowStatusBarInfo(); m_Color=RGB(255,0,0);//颜色 m_TagColor=ID_COLOR_RED;//颜色的ID } void CMainFrame::OnColorGreen() { //当选择"图形颜色"→"绿色"命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart1="绿色"; ShowStatusBarInfo(); m_Color=RGB(0,255,0); m_TagColor=ID_COLOR_GREEN; } void CMainFrame::OnColorBlue() { //当选择"图形颜色"→"蓝色"命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart1="蓝色"; ShowStatusBarInfo(); m_Color=RGB(0,0,255); m_TagColor=ID_COLOR_BLUE; 第2章 菜单、工具栏和状态栏的设计 } void CMainFrame::OnGraphLine() { //当选择"可选图形"→"直线"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart3="直线"; ShowStatusBarInfo(); m_Graph=ID_GRAPH_LINE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.MoveTo(100,310); cdc.LineTo(500,310); cdc.SelectObject(oPen); } void CMainFrame::OnGraphCircle() { //当选择"可选图形"→"椭圆"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart3="椭圆"; ShowStatusBarInfo(); m_Graph=ID_GRAPH_CIRCLE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Ellipse(100,100,200,200); cdc.SelectObject(oPen); } void CMainFrame::OnGraphRectangle() { //当选择"可选图形"→"矩形"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart3="矩形"; ShowStatusBarInfo(); 第2章 菜单、工具栏和状态栏的设计 m_Graph=ID_GRAPH_RECTANGLE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Rectangle(350,100,450,200); cdc.SelectObject(oPen); } void CMainFrame::OnLineSingle() {//当选择"可选图形"→"单线宽"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart2="单线宽"; ShowStatusBarInfo(); m_Thickness=1; } void CMainFrame::OnLineThree() { //当选择"可选图形"→"三线宽"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart2="三线宽"; ShowStatusBarInfo(); m_Thickness=3; } void CMainFrame::OnLineFive() { //当选择"可选图形"→"五线宽"命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值 m_StatusPart2="五线宽"; ShowStatusBarInfo(); m_Thickness=5; } (8)在主框架执行文件CMainFrame.cpp中,加入自定义函数及代码(全用手写): // ShowStatusBarInfo()显示状态栏内容函数:将CString类对象m_StatusPart信息格式化 //后放入CString对象info中,调用SetPaneText()函数,用info的内容设置状态条中由第一 //个参数指定的位置 void CMainFrame::ShowStatusBarInfo() { info1.Format("%s",m_StatusPart1);// m_StatusPart1颜色 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART1),info1); 第2章 菜单、工具栏和状态栏的设计 info2.Format("%s",m_StatusPart2);// m_StatusPart2线宽 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART2),info2); info3.Format("%s",m_StatusPart3);// m_StatusPart3图形 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART3),info3); } void CMainFrame::OnUpdateTime(CCmdUI *pCmdUI) //状态条时间显示函数 { CTime timer = CTime::GetCurrentTime(); char szTimer[6]; int mHour=timer.GetHour(); int mMinute=timer.GetMinute(); //如要按12小时制显示,请去掉下面两条语句的注释 //if (nHour > 12) //nHour = nHour - 12; wsprintf(szTimer, "%i:%02i", mHour, mMinute); //把时间写到Pane m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex (IDS_STATUS_TIMER), LPCSTR(szTimer)); pCmdUI->Enable(); } void CALLBACK CMainFrame::TimerProc(HWND hwnd, UINT uMsg, UINT uIDEvent,DWORD dwTime) { CMainFrame *pMainWnd = (CMainFrame *)AfxGetApp()->m_pMainWnd; ASSERT(uIDEvent == pMainWnd->m_nIDTimer); CCmdUI cui; cui.m_nID = IDS_STATUS_TIMER; cui.m_nIndex = 4; cui.m_pMenu = NULL; cui.m_pOther = &pMainWnd->m_StatusBar; pMainWnd->OnUpdateTime(&cui);//调用OnUpdateTime()函数,更新时间显示 } (9)编译运行,如图2.42~图2.44所示。 图2.42 状态栏的初始状态 图2.43 选择菜单后的状态栏 第2章 菜单、工具栏和状态栏的设计 图2.44 选择“可选图形”的“矩形” 实训3 自定义工具条 自定义工具条,工具条由3种颜色按钮组成,还能使其放在窗口中的任何位置,并能随时关闭。 (1)创建一个单文档的应用程序,名为:浮动工具条。 (2)添加工具条资源:选择Insert->Resource菜单,选中Toolbar单击New按钮,将新工具条ID改为:IDR_COLORTOOLBAR。 (3)单击出现的第1个工具条按钮,用画刷将其涂成红色,接着将出现的第2个按钮涂成绿色,第3个按钮涂成蓝色。 (4)将3个工具条按钮的ID分别改为:ID_BUTTONRED,ID_BUTTONGREEN,ID_BUTTONBLUE并将它们的COMMAND消息分别映射到主框架类CMainFrame中。 (5)创建显示/隐藏工具条、显示/隐藏状态栏和显示新建工具条的菜单:打开Menu下的IDR_MAINFRAME,在“帮助”菜单的前面建一个菜单:观察(&V),选中Pop up,双击下面的子菜单,ID处写:ID_VIEW_TOOLBAR,Caption处写:工具条(&T),Prompt处写:显示或隐藏工具栏\n显隐工具栏。 设置下一个子菜单,ID处写:ID_VIEW_STATUS_BAR,Caption处写:状态栏(&S),Prompt处写:显示或隐藏状态栏\n显隐状态栏。 再设置下一个子菜单,ID处写:ID_VIEWCOLORTOOLBAR,Caption处写:新建颜色工具条(&N)。 (6)在主框架类CMainFrame.h头文件的public下添加成员变量: CToolBar *m_pColorToolbar; (7)在主框架类CMainFrame.cpp文件的构造函数中置初值: CMainFrame::CMainFrame() :m_pColorToolbar(0) { } (8)用ClassWizard在主框架类中创建自定义菜单命令ID_VIEWCOLORTOOLBAR的消息映射,并添加下列代码: void CMainFrame::OnViewcolortoolbar() { if(0==m_pColorToolbar) { m_pColorToolbar=new CToolBar; if(0==m_pColorToolbar->Create(this)) { MessageBox("创建失败"); return ; } m_pColorToolbar->LoadToolBar(IDR_COLORTOOLBAR);//获得工具条资源 第2章 菜单、工具栏和状态栏的设计 //设置工具条出现时的位置,这里是可将工具条放在任一侧 m_pColorToolbar->EnableDocking(CBRS_ALIGN_ANY); DockControlBar(m_pColorToolbar); } else if(m_pColorToolbar->IsWindowVisible()==TRUE)//该函数是获得给定窗口的可视状态 ShowControlBar(m_pColorToolbar,FALSE,FALSE); else ShowControlBar(m_pColorToolbar,TRUE,TRUE); } (9)编译运行,打开“观察”菜单,如图2.45所示,选择“新建颜色工具条”命令,出现如图2.46所示,可见“新建颜色工具条”菜单前面的“√”不见了(如图2.45所示)。将工具条拖到窗口中,如图2.47所示,选择“工具栏”命令,“工具栏”前的“√”不见了,工具条也不见了,如图2.48所示,选择“状态栏”命令和单击拖到窗口的工具条右上角上的“x”情况如何,读者可试试看。 注意:新建的工具条在左上角,鼠标要放在工具条按钮的边缘上,按下左键才能拖动,否则若将鼠标放在工具条按钮上是拖不动的。 图2.45 打开“观察”菜单 图2.46 选择“新建颜色工具条”命令 此处的原工具条消失了 图2.47 将工具条拖到窗口中 图2.48 选择“工具栏”命令 第2章 菜单、工具栏和状态栏的设计 说明: 如果在一个框架窗口中存在多个标准或浮动工具条时,需要利用函数 void DockControlBar(CControlBar *pBar,UINT nDockBarID=0,LPCRECT lpRect= NULL)来确定要控制停靠位置的工具条,它也是CFrameWnd类的成员函数。 参数: pBar用来指向被控制停靠位置的工具条对象指针。 nDockBarID用来确定工具条停靠在框架窗口的哪条边上,其控制风格的具体取值为: AFX_IDW_DOCKBAR_TOP 停靠在框架窗口的顶部; AFX_IDW_DOCKBAR_BOTTOM 停靠在框架窗口的底部; AFX_IDW_DOCKBAR_LEFT 停靠在框架窗口的左边; AFX_IDW_DOCKBAR_RIGHT 停靠在框架窗口的右边。 当参数nDockBarID的取值为0时,则工具条可以停靠在框架窗口中的任何一个可停靠的边上,其默认的初始位置为窗口顶部。 应用程序中的工具栏的停靠或浮动性有: CBRS_ALIGN_TOP 允许停靠客户区顶部; CBRS_ALIGN_BOTTON 允许停靠客户区底部; CBRS_ALIGN_LEFT 允许停靠客户区左侧; CBRS_ALIGN_RIGHT 允许停靠客户区右侧; CBRS_ALIGN_ANY 允许停靠客户区任意一侧; CBRS_FLOAT_MULTI 允许多个控制条在一个单一的小框架窗口中浮动。 第3章 对话框与控件 第3章 对话框与控件 本章将了解如何应用Visual C++6.0创建并使用对话框,如何在对话框中添加控件,并将控件连接到代码中;以及如何查找、设置对话框和控件的各种属性等。 对话框是一种弹出式的特殊窗口,几乎所有的Windows应用程序都要借助于对话框来和用户打交道;它主要用来实现应用程序和用户之间的信息交互。对话框上必须有相应的控件,通过控件,对话框可以收集用户的信息或向用户提供信息显示。对话框是一种非常有用的资源,它的主要功能有两个,一是发送消息,二是接收输入;对话框分为两类,一是模式对话框,二是非模式对话框;另外还可以直接建立基于对话框的应用程序。 3.1 对话框的使用 Visual C++6.0提供的对话框编辑器能“可视”地进行设计、编辑对话框,并可用ClassWizard为对话框从CDialog基类中派生一个类;MFC的CDialog类封装了用于对话框的显示、关闭等操作的许多功能函数,例如:DoModal函数用来显示模式对话框并返回用户操作的结果。在Visual C++6.0应用程序中,使用一个对话框的一般过程是: (1)添加对话框资源; (2)设置对话框的属性; (3)添加和布局控件; (4)创建对话框类; (5)添加对话框代码; (6)在程序中调用对话框。 [例3.1]创建一个模式对话框 所谓模式对话框是指当对话被弹出时,用户必须在对话框中进行相应的操作,在退出对话框之前,对话框所在的应用程序不能继续执行。平常所见到的对话框大多是模式对话框。建立模式对话框的步骤有以下几步。 (1)创建一个SDI(单文档)应用程序,名为:模式对话框。 (2)创建对话框模板的过程是:在顶层菜单中选择Insert→Resource命令,选中Dialog,单击New按钮;在页面右边就显示出了对话框模板。再在控件工具栏上用鼠标单击静态文本(Aa)控件不放,移动到对话框上的合适位置,放开左键,静态文本(Aa)控件就添加到对话框上了。右击这个静态文本控件,便弹出快捷菜单,选择properties(属性)命令,弹出属性对话框,在Caption(标题)框中写:“新建模式对话框”,关闭属性对话框,“新建模式对话框”字样就显示在这个静态文本控件上。 第3章 对话框与控件 (3)修改对话框ID:右击对话框的任何位置,弹出快捷菜单,选择properties(属性)命令,弹出Dialog properties(属性)对话框,将对话框的ID设置为IDD_MYDIALOG,Caption(标题)框中写:模式对话框。 (4)给对话框创建类:双击对话框的任何位置,弹出Adding a Class对话框,选中Create a new class单选按钮,单击OK按钮,弹出New Class对话框,在Name文本框中输入类名:CMyDlg,单击OK按钮,这样就建成了对话框类:CMyDlg。 (5)创建一个顶层菜单,ID为ID_PUPDIALOG,标题为:模式对话框(&A)的菜单命令。 (6)将菜单(ID_PUPDIALOG)命令映射到主框架类CMainFrame中,并添加代码: void CMainFrame::OnPupdialog() { CMyDlg MyDlg; // CMyDlg是CDialog的派生类,这里是定义对话框类对象 MyDlg.DoModal(); //DoModal()是CDialog类成员函数,调用该函数来显示对话框 } (7)在CMainFrame.cpp文件的前面加文件,包含: #include "MyDlg.h" (8)编译运行,如图3.1所示。只能在对话框中进行相应的操作,不能进行其他操作。 图3.1 模式对话框 图3.2 无模式对话框 [例3.2]创建一个无模式对话框 无模式对话框在弹出后,可一直保留在屏幕上,用户可继续在应用中进行其他操作或启动其他应用程序,当需要使用对话框时,只需像激活一般窗口一样激活对话框即可。 (1)创建一个SDI(单文档)应用程序,名为:无模式对话框。 (2)按上例步骤创建对话框模板,拖出一个静态文本,其属性对话框上的ID不动,在标题框写:新建无模式对话框。 (3)为对话框创建类,类名为:CDlg。 (4)创建菜单,菜单名字为:无模式对话框(&W),ID为ID_DLG。 (5)将菜单消息(ID_DLG)映射到视图类(CMyView)中。 (6)在视图类头文件CMyView.h的前面加文件包含:#include “Dlg.h”。 在public:下加:CDlg *dlg;//定义对话框类指针对象 在视图类执行文件CMyView.cpp的菜单命令函数中添加下列代码: CMyView::OnDlg() 第3章 对话框与控件 { dlg=new CDlg(this); //对话框类的首地址 dlg->Create(IDD_DIALOG1); //从资源中创建一个非模式对话框,Create是基类Cdialog成 //员函数 //下面参数SW_RESTORE 是用原来的大小和位置显示一个窗口,同时令其进入活动状态 dlg->ShowWindow(SW_RESTORE); //显示窗口 } (7)编译运行,结果如图3.2所示。对话框弹出后,一直保留在屏幕上,用户可继续在应用程序中进行其他操作。在应用程序菜单上多次选择“无模式对话框”命令,将会在文档窗口弹出多个对话框;还可以拖动这些对话框。 说明: 程序中ShowWindow(SW_RESTORE))是显示窗口函数,其参数宏和其他参数宏的意义如下: SW_RESTORE 用原来的大小和位置显示一个窗口,同时令其进入活动状态; SW_HIDE 隐藏窗口,活动状态给另一个窗口,(激活另外一个窗口,当前窗口就跑到那个窗口后面了); SW_MINIMIZE 最小化窗口,活动状态给另一个窗口; SW_SHOW 用当前的大小和位置显示一个窗口,同时令其进入活动状态; SW_SHOWMAXIMIZED 最大化窗口,并将其激活; SW_SHOWMINIMIZED 最小化窗口,并将其激活; SW_SHOWMINNOACTIVE 最小化一个窗口,同时不改变活动窗口; SW_SHOWNA 用当前的大小和位置显示一个窗口,不改变活动窗口; SW_SHOWNOACTIVATE 用最近的大小和位置显示一个窗口,同时不改变活动窗口; SW_SHOWNORMAL 与SW_RESTORE相同。 模式对话框和无模式对话框在用资源编辑器设计和使用MFC ClassWizard创建用户对话框类的方法是一致的,但在对话框的创建和退出的方式是不同的。 (1)在创建时,模式对话框由系统自动分配内存空间,因此,在退出对话框时,对话框对象会自动删除;而无模式对话框则需要用户来指定内存,退出时还需用户自己来删除对话框对象。 (2)在退出时,两种对话框所使用的终止函数不一样。模式对话框通过调用CDialog::EndDialog来终止;而无模式对话框通过调用CWnd::DestroyWindow来终止。 3.2 资源与资源标识 资源是Windows编程中不可缺少的重要组成部分。Visual C++6.0将Windows应用程序中经常用到的菜单、加速键、工具栏、对话框、图标、光标、字符串等都视为“资源”,并将其单独存放在一个资源文件中。每个资源都由相应的标识符来区分,并且可以像变量一样进行赋值。 1. 资源的分类 创建一个单文档应用程序,名为:对话框,然后将项目工作区切换到ResourceView选项卡,展开所有的节点,如图3.3所示。可以看出,一个单文档应用程序所使用的资源可分为以下7类 第3章 对话框与控件 。 图3.3 单文档程序资源视图 (1)Accelerator(快捷键列表):快捷键列表为一系列组合键的集合,被应用程序用于引发一个动作。该列表一般与菜单命令相关联,用于代替鼠标操作。 (2)Dialog(对话框):为含有按钮、列表框、编辑框等各种控件的窗口。 (3)Icon(图标):图标是代表应用程序显示在Windows桌面上的位图,它同时有32像素×32像素和16像素×16像素两种规格。 (4)Menu(菜单):用户通过菜单可以完成应用程序的大部分操作。 (5)String Table(字串表):是应用程序使用的全局字符串或其他标识符。 (6)Toolbar(工具栏按钮):是由一系列具有相同尺寸的位图组成的,它通常与一些菜单命令相对应,用于提高用户的工作效率。 (7)Version(版本信息):版本信息包含应用程序的版本、用户注册码等相关信息。 除了上述常用资源类别外,Visual C++6.0应用程序中还可有鼠标、HTML等,也可以自己添加新的资源类别。 2. ID资源标识符 从图3.3可以看到每一个资源类别都有一个或多个相关资源,每一个资源均是由标识符来定义的。当添加或创建一个新的资源或资源对象时,系统会为其提供默认的名称并赋值,如:IDR_MAINFRAME等,该定义保存在Resource.h文件中;当然,用户也可重新为标识符命名。在Visual C++6.0中,源程序引用资源和其他用户定义的对象是通过标识符来进行的。资源标识符的名称规则和其他标识符相同,但要注意,资源标识符不区分大小写字母,字符个数不得超过247个。一般情况下,不同的资源使用不同的资源标识符前缀。表3.1列出了资源标识符定义的常用前缀。 表3.1 资源标识符定义的常见前缀 标识符前缀 含 义 IDR_ 快捷键或菜单及相关资源 IDD_ 对话框资源 IDC_ 光标资源或控件 IDI_ 图标资源 IDB_ 位图资源 IDM_ 菜单项 第3章 对话框与控件 续表 标识符前缀 含 义 ID_ 命令项 IDS_ 字符表中的字符串 IDP_ 消息框中使用的字符串 在Visuan C++中,资源标识符都有一个整数与之对应,取值范围为0~65 535。系统为创建的标识符自动赋值,用户也可以修改这些值。在同一个程序项目中,资源标识符名称不能一样,不同的标识符的值也不能一样。如下代码所示,“模式对话框”程序的Resource.h文件中定义的6个资源标识符,其中标识符IDR_MAINFRAME的值是128。 //Resource.h // Used by 模式对话框.rc #define IDD_ABOUTBOX 100 #define IDR_MAINFRAME 128 #define IDR_MYTYPE 129 #define IDD_MYDIALOG 130 //是新设置的模式对话框的ID值 #define ID_MENUITEM32771 32771 #define ID_PUPDIALOG 32773 选择顶层菜单的View→Resource Symbols命令,弹出Resource Symbols对话框,如第2章图2.6所示。其中列出了已被使用和未被使用的标识符名称及其对应的值,从中可以新建一个标识符:在资源标识符浏览器中单击New按钮,屏幕弹出New Symbol对话框,输入标识符名称和数值即可,这样就可以在程序中处理这个标识符,如第2章例2.3,就是这个问题的实例。 3.3 创建对话框及添加控件 在模式对话框和非模式对话框的两节中,已经知道了创建对话框和添加控件的方法,这里再做进一步的介绍。 1. 创建对话框 创建一个单文档应用程序,名为:创建对话框。插入一个对话框,其步骤是:选择Insert→Resource命令,选中Dialog单击New按钮,便在此程序中创建了一个对话框(当然重复此步骤,可以创建多个对话框)。从中可以看出以下两点。 (1)系统为这个对话框自动赋予一个默认的标识符,名为:IDD_DIALOG1,对话框的默认标题为Dialog,有OK和Cancel两个按钮,这两个按钮的ID标识符分别为IDOK和IDCANCEL。 (2)对话框模板资源所在的窗口称为“对话框资源编辑器”,在这里可以通过“控件工具栏”和“布局工具栏”向对话框添加和布局控件,并可设置对话框的属性。 2. 添加和布局控件 将控件工具栏(如图3.4所示)上的某几个控件拖到对话框上,这里拖入3个静态文本控件和1个按钮控件,如图3.6所示。用控件布局工具栏(如图3.5所示)可以对这些控件进行布局、排序、大小调整、上下、左右对齐、测试等。 第3章 对话框与控件 图3.4 各个按钮所对应的控件类型 图3.5 控件布局工具栏 第3章 对话框与控件 图3.6 添加对话框资源后的开发环境界面 说明: (1)添加控件的方法 1)左键单击控件栏中某控件不放,移动到对话框的某位置,放开鼠标,控件就出现在对话框中。 2)左键单击控件栏中某控件之后松开,移动鼠标到对话框上某位置(鼠标箭头变成“+”字形状)上单击,控件就出现在这个位置上。 (2)控件的选取 控件的删除、复制和布局操作一般都要先选取控件。 1)单击某个控件,则某个控件被选取。 2)先在对话框内按住鼠标左键不放,移动鼠标,拖出一个大的方虚框,然后释放鼠标,则被该虚框所包围的控件都将被选取。 3)先按下Shift键,然后用鼠标选取控件,直到所需要的多个控件被选取之后,再放开Shift键,则这些控件被选取。 4)控件被选取时,鼠标单击对话框,则取消控件的选取。若多个控件被选取,再选取其中一个,则其中一个保留选取,其他控件被取消选取。 5)一旦单个控件被选取后,其四周由选择框包围着,选择框上还有几个(一般是8个)蓝色实心小方块,拖动它可改变控件的大小;多个控件被选取后,其中只有一个控件的选择框有几个蓝色实心小方块,这个控件称为主要控件,如图3.6的Button1(按钮控件)。而其他控件选择框的小方块是空心的,如图3.6所示的Static(3个静态控件)。多个控件的上下、左右对齐、间隔相等及大小一致等,都以主控件为准。 (3)控件的移动、复制和删除 1)当单个控件或多个控件被选取后,按方向键或用鼠标拖动控件的选择框可移动控件。 2)若在鼠标拖动过程中还按住Ctrl键,则复制控件。 第3章 对话框与控件 图3.7 Layout菜单命令项 3)按Del键可将选取的控件删除。 (4)测试对话框 Layout菜单下的Test 命令或布局工具栏上的测试按钮,是用于模拟所编辑的对话框的运行情况,帮助用户检验对话框是否符合用户的设计要求以及空间功能是否有效等。 与布局工具栏相对应的菜单命令在Layout(编排)菜单下,而且大部分命令均有相应的快捷键,如图3.7所示。表3.2还列出了Layout菜单命令的快捷键与功能描述。Layout菜单不是在Visual C++6.0开发环境一开始就出现的,而是随着对话框编辑器的打开而显示的。 表3.2 Layout菜单命令的快捷键及功能描述 菜单命令 快捷键 功能描述 Align — 对全控件 Space Evenly — 空间分布 Make Same Size — 使多个控件具有相同尺寸 Arrange Button — 按钮布置 Center in Dialog — 在对话框内居中 Size to Content Shift+F7 按内容定义尺寸 Auto Size — 自动大小 Flip — 翻转 Tab Order Ctrl+D 设置Tab键次序 Guide Setings — 网格、标尺等辅助工具的设置 Test Ctrl +T 测试对话框性能 3. 设置对话框的属性 打开前面创建的“创建对话框”应用程序:① 选择顶层菜单的View→Properties命令;② 按Alt+Enter组合键;③ 右击对话框模板,在弹出的快捷菜单中选择Properties命令;这3种方式都能弹出对话框的属性框,如图3.8所示。 第3章 对话框与控件 图3.8 对话框属性界面 属性框中有:General(一般属性)、Styles(风格)、More Styles(更多风格)、Extended Styles(扩展风格)、More Extended Styles(更多扩展风格)等部分。下面介绍常用的General属性,如表3.3所示。将新加的对话框的属性(General)进行以下修改。 (1)改对话框ID标识符为:IDD_MYDIALOG。 (2)改对话框标题(Caption)为:我的对话框。 (3)单击Font(字体)按钮,将字体改为“宋体,9”,以使自己的对话框和Windows中的对话框保持外观上的一致。 表3.3 对话框的General属性 项 目 说 明 ID框 修改或选择对话框的标识符名称(如改成:IDD_MYDIALOG) Caption(标题栏) 输入对话框的标题名称,中英文均可(如:我的对话框) Font(字体按钮) 单击此按钮可选择字体的种类(如宋体)以及字体的大小(如9号) Xpos/Ypos 对话框左上角在父窗口中的X、Y坐标,都为0时表示居中 Menu(菜单框) 默认值为无,当对话框需要菜单时,输入或选择指定的菜单资源 Class name 默认值为无,它提供C/C++语言编程时所需要的对话框类名,对MFC类库的资源文件来说,该项不被激活 4. 为对话框添加类 对准对话框的任意非控件位置双击,将弹出如图3.9所示的对话框,询问是否为对话框资源创建一个新类,选中Crealte a new class单选按钮,单击OK按钮,弹出如图3.10所示的对话框,写入一个新类的名字如:CMyDlg(注意:类名应以大写的“C”开头),下面的基类Base class和ID标识符内容一般不改。 第3章 对话框与控件 图3.9 Adding a Class对话框 图3.10 New Class对话框 5. 添加映射消息 选择View→ClassWizard命令,弹出MFC ClassWizard对话框,在Class name处选择CMyDlg,在Object IDs里选择IDC_BUTTON1,选中BN_CLICKED(单击该按钮)消息,依次单击Add Function→ OK→Edit Code按钮。 6. 添加代码 在MyDlg.cpp文件的OnButton1()(按钮消息映射)函数中写: void CMyDlg::OnButton1() { MessageBox("欢迎进入对话框的设计!"); } 这时运行还不见对话框被弹出,因为还需要进行下一步。 7. 在程序中调用对话框 由于对话框的代码是以类为模块来设计的,使用时需要在程序中加入该类的头文件,并定义一个类对象,然后就可以使用该类的相关成员。 打开项目工作区的FileView选项卡,打开应用程序的执行文件“创建对话框.cpp”,在其前面加:#include “MyDlg.h” 。 在InitInstance函数体中的return TRUE语句之前添加下列代码: BOOL CMyApp::InitInstance() { …… …… CMyDlg dlg; // 定义对话框类对象 // DoModal()函数是负责对话框的显示和终止 dlg.DoModal(); return } 8. 编译运行 图3.11 弹出对话框并单击按钮 对话框被弹出,单击Buttonl按钮控件,弹出一个小对话框,上面有“欢迎进入对话框的设计!”字样,如图3.11所示。 注意:本例建的CMyDlg类及以后在各个项目中建立的类,在文档、视图、主框架类中都可同样使用。将上面添加的按钮(Button1)控件拉得长一些,以能显示出写入的全部信息。 第3章 对话框与控件 9. 在按钮控件上直接显示信息 用ClassWizard在CMyDlg类中,加WM_INITDIALOG映射消息,步骤是: 选择View→ClassWizard命令,在Class name下拉列表框中选择CMyDlg,在Messages框选择WM_INITDIALOG,单击AddFunction按钮后再单击Edit Code按钮,便在CMyDlg.cpp文件中建立了BOOL CMyDlg::OnInitDialog()函数,在此函数中添加代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); CStatic *pWnd=(CStatic*)GetDlgItem(IDC_BUTTON1);//得到IDC_BUTTON1控件句柄 pWnd->SetWindowText("直接在按钮上显示文字! ");//将这些字符显示在Button1控件上 } 10. 编译运行 图3.12 在按钮上直接显示信息 如图3.12所示,在按钮控件(Button1)上看到“直接在按钮上显示文字”字样,而且再单击按钮控件,又弹出一个小对话框,上面写着“欢迎进入对话框的设计!”。 如果像“模式对话框”和“非模式对话框”那样,用编辑器直接在控件的属性标题处写:“直接在按钮上显示文字”,就简单多了。而像这种用程序语句将文字写控件上,比上面做法要复杂得多,因此一般不用这种方法。 说明: (1)CStatic是MFC的静态文本控件类; (2)GetDlgItem是CWnd的成员函数,通过该函数可以得到对话框中某ID号对应的控件的句柄,参数是控件的ID标识符,这里的ID标识符是BUTTON1(该对话框上的按钮标识符); (3)SetWindowText:是CWnd的一个成员函数,用于设置窗口(控件)的文本内容,由于控件类是CWnd的子类(派生类),因此可以使用基类的SetWindowText来改变按钮控件的显示内容。 11. 对话框资源类型 打开前面的“创建对话框”应用程序,选择顶层菜单的Insert(插入)→Resource命令,或按Ctrl+R组合键,弹出Insert Resource对话框,选中Dialog项,单击其左边的“+”号,展开对话框资源的不同类型,如图3.13所示,表3.4列出了各种类型对话框资源的不同用途。 第3章 对话框与控件 图3.13 对话框资源类型 其中,New(新建)按钮用于创建一个由“资源类型”列表中指定类型的新资源,Import(导入)按钮是用于将外部已有的位图、图标、光标或其他定制的资源添加到当前应用程序中,Custom(定制)按钮用于创建“资源类型”列表中没有的新的类型资源。 表3.4 对话框资源类型 类 型 说 明 IDD_DIALOGBAR 对话条,往往和工具条放在一起 IDD_FORMVIEW 一个表状风格的对话框,用于无模式对话框或视图类 IDD_OLE_PROPPAGE_LARGE 一个大的OLE属性页 IDD_OLE_PROPPAGE_SMALL 一个小的OLE属性页 IDD_PROPPAGE_LARGE 一个大属性页,用于属性对话框 IDD_PROPPAGE_MEDIUM 一个中等大小的属性页,用于属性对话框 IDD_PROPPAGE_SMALL 一个小的属性页,用于属性对话框 3.4 控件的创建和使用方法 控件是Windows图形用户界面的主要组成部分之一,用户通过操作控件对象完成与应用程序之间的交互。控件的使用集中体现了Windows系统面向对象的特点。在应用程序中调用控件不仅简化了编程,还能完成常见的各种功能。为了更好地发挥控件的作用,用户还必须理解和掌握控件的属性、消息以及创建和使用方法。控件的外观和功能各不相同,但它们都是以子窗口的形式存在的,在控件创建、消息处理和控制方面有许多相似之处。 3.4.1 控件的创建方法 控件的创建方法有两种: (1)在对话框模板中用编辑器指定控件,也就是说,将控件的父窗口指定为对话框,如前面 第3章 对话框与控件 “创建对话框”应用程序的Button1按钮,是直接用鼠标拖过来的办法添加的; (2)将控件看作任意一窗口的子窗口,并通过调用相应的Create函数来创建。 该方法是用程序语句来创建一个控件,比较复杂,且不能发挥对话框编辑器的可视化的优点,因此一般不用。但为了了解该创建方法,下面举一个例子。 [例3.3]通过调用Create函数来创建一个控件 (1)创建一个单文档的应用程序,名为:创建控件。 (2)插入(Insert Resource)一个对话框,并为这个对话框建类,名为:CMyDlg。 (3)在应用程序的执行文件(创建控件.cpp)头部加下列代码: #include "MyDlg.h" 并在该文件InitInstance()函数的return TRUE;语句前加下列代码: BOOL CMyApp::InitInstance() { … … CMyDlg dlg; dlg.DoModal(); //DoModal()函数负责对话框的显示和终止 return TRUE; } (4)在对话框CMyDlg类的头文件MyDlg.h的 public下添加一个按钮类的指针变量: CButton *m_btnWnd; (5)为CMyDlg类加WM_INITDIALOG映射消息,并添加代码: BOOL CMyDlg::OnInitDialog() { ----- //控件是作为对话框的一个子窗口来创建的 m_btnWnd=new CButton(); //构造按钮控件的指针,CButton首地址送给指针变量 m_btnWnd->Create("你好",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, CRect(20,20,120,60),this,201); //创建按钮 CFont *font=this->GetFont(); //获取对话框的字体 m_btnWnd->SetFont(font); //设置控件上的字体 return TRUE; } 图3.14 用编程方法创建的控件 (7)编译并运行,结果如图3.14所示,对话框上出现一个按钮控件,上面写着“你好”,它就是用程序语句在对话框上加的按钮控件。 说明: ① 由于OnInitDialog函数在对话框初始化时被调用,因此将对话框中的一些初始化代码都添加在此函数中; ② 代码中Create函数用于创建一个控件,该函数的第1个参数用于指定该控件的标题,这里是“你好”,第2个参数用于指定该控件的风格,第3个参数用于指定该控件在父窗口的位置和大小,第4个参数用于指定父窗口的指针,这里是“this”,第5个参数是指定该控件的标识值,这里是“201”; ③ 控件的风格有WS_CHILD、WS_VISIBLE和BS_PUSHBUTTON,都是Visual C++6.0系统内部定义的标识符,可以直接引用,由于按钮是作为对话框的一个子窗口来创建的,因此其风格必须有(见第四章表4.1) 第3章 对话框与控件 ① WS_CHILD创建子窗口;② WS_VISIBLE使控件可见;③ BS_PUSHBUTTON表示创建的是按键按钮。 3.4.2 基于对话框的应用程序 可以直接利用MFC AppWizard应用程序向导,创建一个基于对话框的应用程序,这种程序运行后首先出现一个对话框。 (1)创建一个基于对话框的应用程序,名为:控件的创建。过程如下: 打开VC系统选择File→New命令,选择MFC AppWizard(exe),在Location处写路径名,在Project name处写文件名:控件的创建单击OK按钮,选中Dialog based单选按钮,如图3.15所示,单击Finish按钮,再单击OK按钮。这就建立了一个基于对话框的程序。 (2)将项目工作区切换到ClassView选项卡,展开CMyDlg类,右击CMyDlg,便弹出如图3.16所示的快捷菜单,从快捷菜单中选择Add Member Variable(添加成员变量)命令,弹出Member variable name对话框,在Variable Type处写:CButton,在Variable Name处写:m_btnWnd,单击OK按钮。通常以“m_”作为变量的开头,表示“成员”的意思,如图3.17所示。 加成员变量的快捷菜单 图3.15 创建一个基于对话框应用程序 图3.16 弹出加成员变量(函数)的快捷菜单 图3.17 添加成员变量 (3)还是在ClassView页面,展开CMyDlg类,双击OnInitDialog函数,在右侧便出现这个函数,在它的return TRUE语句前面加如下代码: BOOL CMyDlg::OnInitDialog() { … … … m_btnWnd.Create("你好", WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON, CRect(20,20,120,60),this,201); //创建控件 //获取对话框的字体 CFont *font=this->GetFont(); 第3章 对话框与控件 m_btnWnd.SetFont(font); //设置控件字体 return TRUE; } (4)编译运行,结果如图3.14所示一样。 3.4.3 控件的消息及消息映射 应用程序可以向控件发送消息,控件也可以向应用程序发送消息,这样的消息被称为通知消息。当控件的状态发生改变(例如用户利用控件进行输入)时,子窗口控件通过向其父窗口(应用程序窗口)发送WM_COMMAND消息进行通信。消息WM_COMMAND的lParam参数中包含了控件句柄,wParam参数的低字节中含有控件标识符,wParam参数的高字节则为通知代码。各控件通知代码不一样,例如,按钮被单击产生的通知代码是BN_CLICKED,用户在编辑框中更改了输入数据会产生通知代码EN_CHANGE等。 在3.3节的第5步,已为Button1按钮添加了映射控件消息,当单击Button1按钮时,系统会弹出一个小对话框。而在第9步则实现了直接将信息发送到Button1控件上。为了进一步掌握控件的消息及消息映射,再作以下讨论。 [例3.4]映射控件消息 (1)创建一个基于对话框的应用程序,名为:映射控件消息。 (2)将项目工作区窗口切换到ResourseView选项卡,打开Dialog,双击IDD_MY_DIALOG。 (3)删除对话框上面的一个原有控件“TODO……”,添加一个按钮控件Button1,保留其默认属性。 (4)选择View→ClassWizard命令,在弹出对话框的Class name处选择CMyDlg,在IDs列表中选择IDC_BUTTON1(这是添加按钮后,系统自动为这个按钮设置的默认标识符),在Messages框中选择BN_CLICKED消息,单击Add Function按钮或双击BN_CLICKED消息,弹出Add Member Function对话框,在这里可以输入成员函数的名称,取系统默认的函数名为:OnButton1,如图3.18所示。 第3章 对话框与控件 图3.18 添加按钮消息映射函数 说明: ① 不同资源对象(控件、菜单等命令)所产生的消息是不相同的。例如按钮控件IDC_BUTTON1的消息有两个——BN_CLICKED和BN_DOUBLECLICKED,分别表示当用户单击或双击该按钮时产生的消息; ② 一般不需要对对话框中的OK(确定)和Cancel(取消)进行消息映射,系统已自动设置了这两个按钮的动作,当用户单击这两个按钮都将自动关闭对话框,而OK按钮动作还使得对话框数据有效。 (5)单击OK按钮,在MFC ClassWizard的Member functions列表中将列出新增加的成员函数,选择此函数,单击Edit Code按钮(或直接在函数名上双击),开发环境的文档窗口中将自动打开该函数所在的源代码文件,并定位到该函数的实现代码处。在此成员函数中添加下列代码: void CMyDlg::OnButton1() { MessageBox("你按下了\"Button1\"按钮!"); } 图3.19 映射控件对话框 (6)编译运行,单击按钮控件,便弹出一个小对话框,上面写“你按下了‘Button1’按钮”,如图3.19所示。 [例3.5]映射控件通用消息 上述过程是映射一个控件的某一个消息,事实上也可以通过WM_COMMAND消息映射来处理一个或多个控件的通用消息,具体步骤有以下几步。 (1)打开前面“映射控件消息”程序。 (2)在Class name列表中选择CMyDlg,在IDs列表框中也选择CMyDlg,在Messages列表框中选择OnCommand,单击Add Function和Edit Code按钮,这样在CMyDlg类中就建好了OnCommand消息函数,如图3.20 第3章 对话框与控件 所示。由于OnCommand函数是一个用于处理WM_COMMAND消息的虚函数,因此这里添加的OnCommand函数事实上是一个在类中实际调用的函数,可称为“实例函数”。这样的映射操作,可以称之为“对虚函数OnCommand的重载”。 图3.20 添加OnCommand函数重载 (3)在OnCommand函数中添加下列代码: BOOL CMyDlg::OnCommand(WPARAM wParam, LPARAM lParam) { WORD nCode=HIWORD(wParam);//用于获取32位数值中的高位,通知代码 WORD nID=LOWORD(wParam);//用于获取32位数值中的低位,控件的ID标识值 if((nID==201)&&(nCode==BN_CLICKED))//判别控件的ID标识符值与通知代码 MessageBox("你按下了\"你好\"按钮!"); if((nID==IDC_BUTTON1)&&(nCode==BN_CLICKED)) MessageBox("这是在OnCommand处理的结果!"); return CDialog::OnCommand(wParam, lParam); } (4)编译并运行,结果如图3.21和图3.22所示。 图3.21 第1次单击Button1按钮的结果 图3.22 再单击小对话框上的“确定”按钮的结果 注意:代码中if语句中的201是前面用Create创建按钮时指定的标识值。 说明: 第3章 对话框与控件 (1)WORD是16位无符号整数。 BN_CLICKED是当用户单击按钮产生的消息。 EN_CHANGE是编辑框中的文本被改变时发出的消息。 WPARAM wParam此参数所代表的是虚拟键码,此码可用于检测究竟是哪一个键被按下。 LPARAM lParam是由32位组合而成,除部分保留未使用外,可以将其他部分分成6个域予以说明,如图3.23所示。 图3.23 分域说明LPARAM IParam 1)重复次数:是指键盘被按下或是放开按键的次数。 2)OEM扫描码:是由键盘的硬件产生。 3)扩展键标志:如果按的是增强键,如Alt、Ctrl等标志值是1,一般Windows应用程序很少用此域。 4)内容码:主要用于检测是否按下Alt键。 5)转换状态:是指如果有按键(Down)发生,此域为0;如果是放开(Up)发生,则此域为1。 还有:由于该项目中Button1按钮的BN_CLICKED消息处理同时存在两种函数,即OnButton1和OnCommand,当单击Button1按钮时,系统先执行OnCommand函数代码,然后执行OnButton1函数代码。因OnCommand函数的最后一句代码有“return CDialog::OnCommand(wParam,lParam);”,它的作用是将控件的消息交由对话框其他函数处理。所以该例中,单击两次Button1按钮,就出现设置的BUTTON1的不同的两个信息。由于用Create创建的控件无法用MFC ClassWizard直接映射其消息,因此上述方法弥补了ClassWizard的不足,使用时要特别注意。 (2)除一些常用的数据类型外,Windows还提供一些宏来表示一些基本数据类型,如: LOBYTE、HIBYTE分别用于获取16位数值中的低位和高位字节; LOWORD和HIWORD分别用于获取32位数值中的低位和高位字; MAKEWORD是将2个16位无符号值结合成一个32位无符号值。 下面通知消息是所有Windows控件所共有的: NM_CLICK 在控件中单击按钮; NM_DBLCLK 在控件中双击按钮; NM_RDBLCLK 在控件中双击鼠标右键按钮; NM_RETURN 当控件具有输入焦点时按下Enter键; NM_SETFOCUS 控件得到输入焦点; NM_KILLFOCUS 控件失去输入焦点; NM_OUTOFMEMORY 由于没有足够的可用内存,控件无法完成一次操作。 第3章 对话框与控件 3.4.4 控件的数据交换和数据效验 为了能让用户直接方便地操作一个控件,MFC采用了独特的控件的数据交换(DDX)和数据效验(DDV)技术,DDX将数据成员变量与对话框模板内控件相连接,这样使得数据在控件之间很容易传输。DDV用于数据的校验,例如它能自动校验数据成员变量数值的范围,并发出警告。使用MFC ClassWizard可以很容易地定义一个控件的成员变量及其数据范围。 图3.24 添加成员变量界面 [例3.6]控件的数据传输 (1)打开上例“映射控件消息”程序:选择View→ClassWizard命令,在弹出的对话框中打开Member Variables选项卡,在Class name列表框中选择CMyDlg,然后在Control IDs列表中选择IDC_BUTTON1,单击Add Variable按钮(或双击),弹出Add MemberVariable对话框,在Member variable name处写好成员变量名:m_MyBtn,Variable type(变量类型)为:CButton,Category(种类)为:Control,如图3.24所示,单击OK按钮,如图3.25所示,就建好了成员变量。 (2)再向对话框添加一个编辑控件,ID默认,加成员变量为:m_strEdit,类型为CString ,在Maximum characters(最大字符串个数)框写:20,目的是在按钮BUTTON1和编辑EDIT1控件之间进行数据交换,交换的最大字符串个数是20。 (3)将CMyDlg::OnButton1()修改成: void CMyDlg::OnButton1() { UpdateData();//数据从控件向相关联的成员变量复制 //编辑框的内容在按钮上显示 图3.25 添加数据成员后的界面 m_MyBtn.SetWindowText(m_strEdit); //用下面语句代替上面语句也行,单击//Button1按钮,此按钮上直接显示的就是" //欢迎" //GetDlgItem(IDC_BUTTON1)→SetW//indowText("欢迎"); } (4)编译运行,当在编辑框输入“你好”后,单击Button1按钮,则该按钮的名称就变成了编辑框中的内容“你好”了(可将该程序中的OnCommand函数内容注释掉,不然得再单击一下 第3章 对话框与控件 “确定”按钮),如图3.26所示。 图3.26 控件的数据交换结果 说明: (1)UpdateData();默认参数值是TRUE(真),数据从控件向相关联的成员变量复制,若UpdateData(FALSE),数据由控件相关联的成员变量向控件传输。 (2)Category框内可选择Value或Control两种类型。Control所对应的变量类型就是MFC为该控件封装的控件类。Value所对应的是数值类型。不同控件所提供的关联的数值类型各不同,例如:对于编辑框来说,Variables type中的数值类型有:Cstring(字符串),int,UINT(32位无符号整数),long(32位带符号整数),DWORD(32位无符号整数,段地址和相关的偏移),float,double,BYTE(8位无符号整数),short,BOOL。 (3)在DDV/DDX技术中,允许用户为同一个控件关联多个变量,但必须保证这些变量名是互不相同的,且这些变量在同一个类型中不能有多个变量,即在Value和Control类型中各自只能有一个关联变量。 (4)如果添加的关联变量是一个数值类型,则MFC ClassWizard对话框的Member Variables选项卡下方还要求用户输入变量的范围,这就是控件的数据校验设置。例如本例的(2)添加编辑控件的成员变量之后,还在Maximum characters文本框输入:20。 (5)添加BUTTON1和EDIT1控件的成员变量后,打开“映射控件消息Dlg”类源文件,可以发现MFC ClassWizard对上述操作进行了以下3个方面的工作(带阴影部分)。 1)在“映射控件消息Dlg.h”文件中,系统已经添加了控件关联变量的声明,即: class CMyDlg : public CDialog { … … //{{AFX_DATA(CMyDlg) enum { IDD = IDD_MY_DIALOG }; CButton m_MyBtn; CString m_strEdit; //}}AFX_DATA } 2)在“映射控件消息Dlg.cpp”文件的构造函数里见到系统已经添加好的代码: CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/) : CDialog(CMyDlg::IDD, pParent) { //{{AFX_DATA_INIT(CMyDlg) m_strEdit = _T(""); //}}AFX_DATA_INIT … } 3)在“映射控件消息Dlg.cpp”文件的DoDataExchange函数体内,系统已经添加了控件的DDX/DDV代码,它们都是一些以DDV_或DDX_开头的函数调用: 第3章 对话框与控件 void CMyDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyDlg) // IDC_BUTTON1和其成员变量m_MyBtn进行数据交换 DDX_Control(pDX, IDC_BUTTON1, m_MyBtn); // IDC_EDIT1和其成员变量m_strEdit进行数据交换 DDX_Text(pDX, IDC_EDIT1, m_strEdit); DDV_MaxChars(pDX, m_strEdit, 20); //校验m_strEdit的最大字符个数不超过20 //}}AFX_DATA_MAP } 其中: ① GetDlgItem()是CWnd的成员函数,通过该函数可以得到对话框中某ID号对应的控件的句柄。参数是控件的ID标识符,这里是IDC_BUTTON1(该按钮的ID标识符); ② SetWindowText是CWnd的一个成员函数,用于设置窗口(控件)的文本内容。 3.4.5 控件的通用属性 图3.27 按钮控件的属性选项卡 在控件的属性对话框中含有许多属性:General(一般属性)、Styles(控件的风格)、Extended Styles(控件的扩展风格)。Styles和Extended Styles是用来设定控件的外观的、辅助功能的。不同控件具有不同的风格和扩展风格,但控件的一般属性基本相同,它通常有标识符、标题框等内容,表3.5列出了控件的通用General属性,图3.27是按钮控件(BUTTON)的属性对话框。 表3.5 控件的General通用属性 项 目 说 明 ID 控件的标识符,每种控件都有默认的ID,例如按钮控件为IDC_BUTTON1 Caption 控件的标题,大多数控件都有默认的标题,例如按钮控件为Button1 Visible 指定控件初始化时是否可见 Group 指定控件组中的第一个控件,如果该项被选中,则此控件后的所有控件均被看成一组,成组的目的是可以让用户键盘方向键在同一组控件中进行切换 Help ID 若该项被选中,则为该控件建立一个上下文相关的帮助标识符 Disabled 指定控件初始化时是否禁用 第3章 对话框与控件 Tab stop 若该项被选中,则用户可以按Tab键来选择控件 3.5 常用控件 根据控件的使用情况将控件分为两种类型:常用控件和高级控件。常用控件包括:静态控件、按钮、滚动条、编辑框、列表框和组合框6种控件,高级控件包括:上下控件、滑标控件、工具栏、状态栏、工具提示控件以及多文本编辑框等,这里重点介绍常用控件,表3.6列出了经常用到的控件类。图3.28用到的控件有静态图片控件、按钮、静态控件、编辑框、组合框、滚动条等。 图3.28 控件示例 3.5.1 静态控件 静态控件是用于显示一个字符、框、矩形、图标、位图或增强的图元文件,它可以用做标签、框或用于分隔其他的控件。一个静态控件一般不接受用户输入,也不产生通知消息。 在对话框编辑器的控件工具栏中,属于静态控件的有:静态文本(Static Text)、组框(Group Box)、图片控件(Picture)三种。静态图片控件的General和Style选项卡如图3.29和3.30所示。表3.7列出了其一般属性和风格的各个项的意义。可以选择Type(图片类型)、Image(图像资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控件中。另外,用户还可设置其风格来改变控件的外观以及图像在控件的位置等, 第3章 对话框与控件 如表3.8所示的Styles风格。 表3.6 常用控件类 控件名称 MFC类 功 能 描 述 静态控件 CStatic 用于向用户显示一些几乎固定不变的文字或图形描述 按钮 CButton 用于产生某些命令或改变某些选项设置 编辑框 CEdit 可完成文字的输入、输出双向操作,使用户能查看并编辑文字 列表框 CListBox 显示一个列表,让用户从中选取一个或多个项 组合框 CComboBox 它把列表框和编辑框有机地组合在一起,用户不仅能选择列表中已有的项,还能编辑出新的项 滚动条 CScrollBar 通过滚动块在滚动条上的移动来改变某些数值 进展条 CProgressCtrl 用于指示一个操作的进度 旋转按钮 CSpinButtonCtrl 又称“上下控制”,是一对箭头按钮,用户单击它们可以增加或减小某个值 滑动条 CSliderCtrl 是一个包含一个滑动块和可选的刻度线,用户可以用鼠标或方向键沿某个方向移动滑动块 旋转按钮 CSpinButtonCtrl 带有一对反向箭头的按钮,单击这对按钮可增大或减小某个值 日期时间 CDateTimeCtrl 用于选择指定的日期和时间 图像列表 CImageList 是一系列相同大小的图像的集合 标签控件 CTabCtrl 类似于一个笔记本的分割器或一个文件柜上的标签,使用它可以将一个窗口或对话框的相同区域定义为多个页面 列表控件 ClistCtrl 可以用“大图标”、“小图标”、“列表”或“报表”4种不同的方式来显示一组信息 树控件 CTreeCtrl 用树结构的形式显示一组信息,并能反映这些信息的层次关系 图3.29 静态图片控件的General(属性) 图3.30 静态图片控件的Style(风格) 表3.7 静态图片控件的General(属性) 项 目 说 明 Visible 指定控件初始化时是否可见 Disabled 指定控件初始化时是否禁用 Help ID 若该项被选中,则为该控件建立一个上下文相关的帮助标识符 Group 看成同一组,成组的目的是可以让用户键盘方向在同一组控件中进行切换 Tabstop 若该项被选中,则用户可以按Tab键来选择控件 Type 图片类型,用户可以从中选择Frame(框)、Rectangle(矩形区域)、Icon(图标)、Bitmap(位图)、Enhanced Metafile(增强图元文件,它是各种绘图命令的集合) Image 当图片类型为Icon或Bitmap时,通过此框可选择指定的资源ID号设置Frame和Rectangle的颜色,它可以是black(黑色)、white(白色)、gray(灰色)、或者是具有3D外观的etched 第3章 对话框与控件 (腐蚀色) Color 设置Frame和Rectangle的颜色,它可以是black(黑色)、white(白色)、gray(灰色)或者是具有3D外观的etched(腐蚀色) 表3.8 静态图片控件的Styles(风格) 项 目 说 明 Sunken 选中时,在控件的周围有下沉的边框 Notify 选中时,当用户单击或双击图片时会向其父窗口发出通知消息 Right justify 选中时,用户重置图片大小,图片右下角是固定不变的 Border 选中时,图片周围有边框 Center image 选中时,图片显示在控件中央,其余区域由图片左上角的像素颜色来填充 Real size image 选中时,按图片的原始大小来显示,超过控件区域的部分被裁剪 [例3.7]将一个.bmp图形显示在静态图片控件上 (1)创建一个单文档(SDI)应用程序,名为:图片控件。 (2)创建对话框模板:选择Insert→Resource命令,选中Dialog,单击New按钮。 (3)将图片控件Picture拖到对话框上,并拉大些,将OK和CANCEL拖到下面。 (4)向项目中插入一个 .bmp图片: 选择Insert→Resource命令,选中Bitmap,单击Import按钮,在弹出的Import Resource对话框中下拉出“所有文件(*.*)”,用“搜索”找到一个.bmp图形,将Import导入到此项目中。 (5)将这个图片放到图片控件上: 在对话框上选中这个图片控件,右击此图片控件,弹出Picture Propertiec对话框(图片控件属性框),在Type下拉列表框中选择Bitmap,在image下拉列表框中选择IDB_BITMAP1,关闭属性对话框,就把图片加到了图片控件上。 (6)为对话框添加类:双击新建对话框的任何位置,单击OK按钮,类名写:CMyDlg。 (7)创建一个菜单项,用来显示对话框: 在ResourceView选项卡打开Menu文件夹,选择IDR_MAINFRAME,双击空白菜单,取消选中Pop-up(将其√去掉,不选中),在ID处写:ID_DLG,菜单名写:显示图片对话框(&S),关闭。 (8)在视图类的头文件CMyView.h的前面加:#include “MyDlg.h”。 在该头文件的public下加:CMyDlg *dlg;。 (9)将菜单命令(ID_DLG)映射到视图类CMyView中去,在该消息映射函数中添加代码: CMyView::OnDlg() { dlg=new CMyDlg(this); dlg->Create(IDD_DIALOG1); dlg->ShowWindow(SW_RESTORE); } 说明:SW_RESTORE是用原来的大小和位置显示一个窗口,同时令其进入活动状态。 第3章 对话框与控件 (10)编译运行,如图3.31所示。 如果直接建立一个对话框应用程序,在其上加一个图片控件,再通过选择Insert→Resource命令,选择Bitmap,单击Import按钮,插进一个位图(.bmp)图形,通过图片控件的属性将这个图形加在这个图片控件上,运行程序,在对话框上就能直接显示了。 图3.31 显示图片控件 3.5.2 按钮控件 按钮控件在进行可视化程序设计中经常用到,它是实现一种开与关的输入。 1. 常见的三类按钮控件 (1)按键按钮:按键按钮通常可以立即产生某个动作,执行某个命令,因此也常被称为命令按钮。如果将其属性设定为Default button,这时它就有个黑边框,随之称为默认按钮,如图3.32所示的“默认按钮”,表示其已接受到键盘的输入焦点,只要按回车键就能按下这个按钮。一般只把最常用的按键设定为默认按键按钮。 图3.32 三种不同类型的按钮 (2)单选按钮:其外形为按钮文本和其左侧一个小圆圈,当它被选中时,就标上一个黑点,它可分为一般和自动两种类型。若是自动类型,选中同组按钮的某个单选按钮,则其余的单选按钮的选中状态就会清除,保证多个选项始终只有一个被选中,如图3.32上的Radio1所示。 (3)复选框:其外形是在文本前有一个空心方框,当它被选中时,就加上一个“√”标记,通常复选框只有选中和未选中两种状态,若其中有一个灰色“√”,则这样的复选框是三态复选框,如图3.32所示的Check3,它表示复选框的选择状态是“不确定”。设定三态复选框的方法是在其属性的Style页面选中“Tri-state(状态)”项。 2. 按钮的消息 常见的按钮映射消息有两个:① BN_CLICKED(单击按钮);② BN_DOUBLECLICKED(双击按钮)。 3. 按钮选中操作 最常用的按钮操作是设置或获取一个按钮或多个按钮的选中状态。CButton类的以下两个成员函数原型如下: void SetCheck(int nCheck); //设置指定按钮的选中状态 int GetCheck()const; //获取指定按钮的选中状态 其中,参数nCheck和函数GetCheck返回的值可以是:0表示不选中,1表示选中,2表示不确定(仅用于三态按钮)。 而对于同组多个单选按钮的选中状态的设置或获取,需要使用CWnd类的成员函数:CheckRadioButton和GetCheckedRadioButton,它们的原型如下: 第3章 对话框与控件 void CheckRadioButton(int nIDFirstButton,int nIDLastButton,int nIDCheckButton); int GetCheckedRadioButton(int nIDFirstButton,int nIDLastButton); 参数: nIDFirstButton和nIDLastButton分别指定这组单选按钮的第一个和最后一个按钮ID值; nIDCheckButton用于指定要设置选中状态的按钮ID值。 4. 按钮控件的应用举例 [例3.8]使用复选框 (1)创建一个基于对话框的应用程序,名为:复选框。 (2)将3个复选框和1个编辑框拖到对话框上,将编辑框拉长一些,并摆放整齐。对话框编辑器会将复选框的题注默认为Check1、Check2、Check3,如图3.33所示。 (3)用ClassWizard在CMyDlg类为编辑框IDC_EDIT1加String类型的成员变量:m_text。 (4)将这3个复选框的映射消息加到类CMyDlg,并添加代码: void CMyDlg::OnCheck1() { m_text = "选中复选框 1"; UpdateData(false); } void CMyDlg::OnCheck2() { m_text = "选中复选框 2"; UpdateData(false); } void CMyDlg::OnCheck3() { m_text = "选中复选框 3"; UpdateData(false); } (5)编译运行,如图3.34所示。可见,复选框被单击之后,自动地在被选中和不选中之间切换(在编辑框显示程序中写入变量m_text中的信息),能够在任何时候决定复选框的状态,方法是将一个成员变量连接到复选框并使用GetCheck对象方法。 图3.33 控件对齐 图3.34 运行结果 第3章 对话框与控件 [例3.9]使用单选按钮 (1)创建一个基于对话框的应用程序,名为:单选按钮。 (2)拖过3个单选按钮和1个编辑框,把编辑框拉长一些,并摆放整齐。对话框编辑器也将给定单选按钮题注Radio1、Radio2、Radio3,当然可以更改它们。 (3)用ClassWizard为编辑框IDC_EDIT1加一个成员变量:m_text1,并将Radio1、Radio2、Radio3映射到CMyDlg中,并添加代码: void CMyDlg::OnRadio1() { m_text1 = "你选择了第一个单选按钮"; UpdateData(false); } void CMyDlg::OnRadio2() { m_text1 = "你选择了第二个单选按钮"; UpdateData(false); } void CMyDlg::OnRadio3() { m_text1 = "你选择了第三个单选按钮"; UpdateData(false); } 图3.35 选中一个单选按钮 (4)编译运行,如图3.35所示。 注意:当选中其中一个单选按钮时,没有办法再选中其他单选按钮,这是因为这3个单选按钮已经被当作了一个整体,它们都位于同一个窗口里,当单击了一个单选按钮之后,程序将自动将其他的单选按钮改为不选。 如果想在同一窗口的多个单选按钮中,选中一个后还可以选中其他,那就得将多个单选按钮分成组,每一组的单选按钮用组框围起来,然后将每组的第一个单选按钮的属性的Group选中,置成“√”,这样就可以在同一窗口的不同单选按钮中,同时选中多个。步骤再从(5)往下做。 (5)再为对话框添加3个单选按钮(Radio4、Radio5、Radio6)和1个编辑框(EDIT2),然后拖过一个组框(Group Box),将这3个单选按钮和编辑框用组框围起来。打开组框的属性对话框,将标题写成“第二组单选按钮”。再拖过一个组框(Group Box),将原来的3个单选按钮(Radio1、Radio2、Radio3)和一个编辑框(EDIT1)用组框围起来。打开组框的属性对话框,将标题写成“第一组单选按钮”。 (6)打开第一组第一个单选按钮Radio1的General属性,单击Group将其置成“√”。打开第二组第一个单选按钮Radio4的General属性,单击Group将其置成“√”。 (7)为编辑框EDIT2用ClassWizard添加一个成员变量:m_text2。 (8)为3个单选按钮IDC_RADIO4、IDC_RADIO5、IDC_RADIO6添加映射消息,并 第3章 对话框与控件 添加下列代码: void CMyDlg::OnRadio4() { m_text2 = "你选中了单选按钮4"; UpdateData(false); } void CMyDlg::OnRadio5() { m_text2 = "你选中了单选按钮5"; UpdateData(false); } void CMyDlg::OnRadio6() { m_text2 = "你选中了单选按钮6"; UpdateData(false); } 图3.36 同一窗口分组选中单选按钮 (9)编译运行,分别选中一组和二组中的单选按钮,出现如图3.36所示的结果。 [例3.10]复选框和单选按钮混用 为花店设计一个小程序,允许顾客选择某一行花或者该行花中的许多支花。当用户选中一个相应于特殊排列的单选按钮时,程序将显示该排列里面各种各样的花(复选框)来供顾客继续选择,并且将在一个文本框里显示花的价格;当顾客选中另一单选按钮,程序应显示新一排列类型中的花,并给出该排列花的价格。 (1)创建一个基于对话框的应用程序,名为:鲜花选择。 (2)向对话框添加两个组合框,如图3.37所示。组合框能可视并有效地安排控件,特别是同一组框中的单选按钮具有一致的功能,因此能在程序中安排许多相互独立的单选按钮组。 第3章 对话框与控件 图3.37 在对话框中添加两个组框 (3)右击组框,在弹出的快捷菜单中选择Properties(属性)命令,然后在Caption框中输入新的题注;这里将左边的组框设为:花的排列;右边的组设为:花的名字。 (4)向“花的排列”组中添加4个单选按钮,分别将题注改为:一排、二排、三排、四排;向“花的名字”组中添加4个复选框,分别将题注改为:山茶花、玫瑰花、百合花、腊梅花,如图3.38所示。能让顾客使用单选按钮选取一个花排列,并使用复选框指出哪些花位于哪个排列中。 图3.38 向组框中添加单选按钮及复选框 第3章 对话框与控件 (5)用ClassWizard在CMyDlg类中为4个复选框添加BOOL类型的成员变量,如图3.39所示:IDC_CHECK1为m_check1,CHECK2为m_check2,IDC_CHECK3为m_check3,IDC_CHECK4为m_check4,如图3.40所示。 图3.39 为复选框添加BOOL类型的成员变量 图3.40 复选框的成员变量 (6)向对话框中添加一个编辑框EDIT1,并用ClssWizard为其添加一个String类型的成员变量m_text。 (7)用ClassWizard将映射消息IDC_RADIO1、IDC_RADIO2、IDC_RADIO3、IDC_RADIO4连接到CMyDlg类的单选按钮控件。并在void CMyDlg::OnRadio1()前面定义整型变量n,再为各单选按钮消息添加代码: int n=0; // n用于确定是第几排的 void CMyDlg::OnRadio1() { n=1; m_check1 = true; m_check2 = true; m_check3 = true; m_check4 = true; m_text = " 有4 种花出售 "; UpdateData(false); void CMyDlg::OnRadio2() { n=2; m_check1 = true; m_check2 = false; m_check3 = true; m_check4 =false; m_text = " 有2种花出售 "; UpdateData(false); } void CMyDlg::OnRadio3() 第3章 对话框与控件 { n=3; m_check1 = false; m_check2 = true; m_check3 = false; m_check4 = true; m_text = " 有2种花出售"; UpdateData(false); } void CMyDlg::OnRadio4() { n=4; m_check1 = true; m_check2 = true; m_check3 = true; m_check4 = false; m_text =" 有3种花出售 "; UpdateData(false); } (8)编译运行,选中“一排”单选按钮,有4种花出售,编辑框写:有4种花出售,如图3.41所示。选中“二排”单选按钮,有两种花出售,如图3.42所示。还有三排、四排可试试看。 图3.41 选中“一排”,有4种花出售 图3.42 选中“二排”,有两种花出售 (9)再用ClassWizard将映射消息IDC_CHECK1、IDC_CHECK2、IDC_CHECK3、IDC_CHECK4连接到CMyDlg类的复选框控件,并添加代码: void CMyDlg::OnCheck1() { if(n==3||n==0) { m_text =" "; 第3章 对话框与控件 m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 100.00 "; m_check1 = true; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck2() { if(n==2||n==0) { m_text =" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 110.00 "; m_check1 = false; m_check2 = true; m_check3 = false; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck3() { if(n==3||n==0) { m_text =" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 130.00 "; 第3章 对话框与控件 m_check1 = false; m_check2 = false; m_check3 = true; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck4() { if(n==4||n==2||n==0) { m_text=" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 150.00 "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = true; UpdateData(false); } (10)编译运行,选中某一排后再选中每一种花,每一种花的价钱都显示在编辑框里;选中“二排”只有“山茶花”、“百合花”,分别选中它们,出现各自的价钱;而选中“玫瑰花”、“腊梅花”却不出现它们的价钱,因为在这一排没有这两种花,这就是程序中变量n所起的作用。例如:在void CMyDlg::OnRadio3()函数中定义n=3,那么当选中“三排”(Radio3)时,“山茶花”和“百合花”就不被选中,也就是没有这两种花,而选中“山茶花”(OnCheck1),就到消息函数CMyDlg::OnCheck1()中,其中第一条语句就判别n是否等于3,如果等于3 ,将m_text变量置空返回,不执行下面语句,也就是不显示价钱;在CMyDlg::OnCheck3()中也是这样判别的,因为这两种花在三排里也没有,所以都不显示价钱。CMyDlg::OnCheck2()、CMyDlg::OnCheck4()函数中的if判别都是这个道理。 3.5.3 编辑框控件 编辑控件(Edit Box)的主要作用在于接收用户键盘输入,通过它可以很方便地输入各种文本,数字或者口令,也可以用它编辑和修改文本的内容。编辑框里的文本可以是单行,也可以是多行,由其属性Multiline决定。多行编辑具有简单文本编辑器的常用功能,例如它可以有滚动条,换行用Ctrl+Enter组合键,还可以进行复制、粘贴等操作。单行编辑仅用于单行文本的显示和操作。当编辑框控件被激活并具有输入焦点时,会出现一个闪动的插入符,表明当前插入点的位置。 第3章 对话框与控件 1. 编辑框的属性 编辑框的属性Styles(风格)选项卡如图3.43所示,表3.9列出了Styles的各项含义。 图3.43 编辑框的属性Styles选项卡 表3.9 编辑框的Styles属性 项 目 说 明 Align text 各行文本对齐方式:Left、Center、Right,默认时为Left Multiline 选中时为多行编辑框,否则为单行编辑框 续表 项 目 说 明 Number 选中时控件只能输入数字 Horizontal scroll 水平滚动,仅对多行编辑框有效 Auto HScroll 当用户在行尾输入一个字符时,文本自动向右滚动 Vertical scroll 垂直滚动,仅对多行编辑器有效 Auto VScroll 当用户在最后一行按Enter键时,文本自动向上滚动一页,仅对多行编辑框有效 Password 选中时,输入编辑框的字符都将显示为“*”,仅对单行编辑框有效 No hide selection 通常情况下,当编辑框失去键盘焦点时,被选择的文本仍然反色显示。选中时,则不具备此功能 OEM convert 选中时,实现对特定字符集的字符转换 Want return 选中时,用户按Enter键,编辑框中就会插入一个回车符 Border 选中时,在控件的周围存在边框 Uppercase 选中时,输入在编辑框的字符全部转换成大写形式 Lowercase 选中时,输入在编辑框的字符全部转换成大写形式 Read-only 选中时,防止用户输入或编辑文本 2. 编辑框的通知消息 当编辑框的文本修改或者被滚动时,会向其父窗口发送一些通知消息,如表3.10所示。 表3.10 编辑框的通知消息 通知消息 说 明 EN_CHANGE 当编辑框中的文本已被修改,在新的文本显示之后发送此消息 第3章 对话框与控件 EN_HSCROLL 当编辑框的水平滚动条被使用,在更新显示之前发送此消息 EN_KILLFOCUS 编辑框失去键盘输入焦点时发送此消息 EN_MAXTEXT 文本数目到达了限定值时发送此消息 EN_SETFOCUS 编辑框得到键盘输入焦点时,发送此消息 EN_UPDATE 编辑框中的文本已被修改,新的文本显示之前发送此消息 EN_VSCROLL 当编辑框的垂直滚动条被使用,在更新显示之前发送此消息 3. 编辑框的基本操作 为了能让编辑框控件允许不同类型数据的输入和读取,用户需要使用DDV和DDX技术。DDX将控件的成员变量同对话框类控件相连接,这样使得数据在控件之间很容易传输;DDV用于数据的校验。 (1)密码设置 密码设置就是把编辑框设置成一个可输入密码的输入框。这时,用户输入的每个字符都被一个特殊的字符代替显示,这个特殊的字符称为密码字符。打开编辑框的属性对话框的Styles选项卡,选中Password复选框就将编辑框设置成一个可输入密码的输入框。默认的密码字符是“*”,应用程序可以使用成员函数CEdit::SetPassWordChar来定义自己的密码字符,该函数原型如:void SetPasswordChar(TCHAR ch)。 参数:ch表示设定的密码字符,当ch=0时,编辑框控件内将显示实际字符。 [例3.11]设置密码 1)创建一个多文档(或单文档)应用程序,名为:设置密码。 图3.44 密码设置界面 2)插入一个对话框,将ID改为:IDD_PASSWORD_DIALOG,Caption(标题)写:设置密码,将OK和CANCEL改为确定和取消。 3)按图3.44的布局布置控件。 ① 拖入一个静态文本(Static Text),标题写:请输入用户名,并拖入一个编辑控件,将其ID改为:IDC_PASSWORD_EDIT1,在Styles中选中PassWord属性。 ② 再拖入一个静态文本,标题写:请输入密码,拖入一个编辑控件,将其ID改为:IDC_PASSWORD_EDIT2,在Styles中选中PassWord属性。 4)创建对话框类,名为:CPassWordDialog。 5)加成员变量:为对话框类(Class name处为CPassWordDialog)的编辑框控件IDC_PASSWORD_EDIT1和IDC_PASSWORD_EDIT2加String类型的成员变量m_password1和m_password2,在 Maximum Characters处写20(字符串个数最多为20)。 6)在应用程序“设置密码.cpp”的InitInstance()函数中添加代码: Bool CMyApp::InitInstance() { ----- m_pMainWnd->ShowWindow(SW_SHOW); 第3章 对话框与控件 m_pMainWnd->UpdateWindow(); CPassWordDialog CDlg; if(CDlg.DoModal()==IDOK) //用户单击了"确定"按钮 { if(CDlg.m_password1!="nanshan") //用户名 { AfxMessageBox("用户名错误,确定后将退出程序"); return FALSE; } if(CDlg.m_password2!="1949") //密码 { AfxMessageBox("密码错误,确定后将退出程序"); return FALSE; } } else //如果单击"取消"按钮程序也结束 return FALSE; return TRUE; } 7)在应用程序“设置密码.cpp”头部加:#include “PassWordDialog.h”。 8)编译运行,结果如图3.44所示。 (2)选择文本 用户可以在程序运行过程中选择编辑框中的文本,以便对选择的文本进行处理。这时用户可以用鼠标或键盘来选择文本。用鼠标选择文本的方法是:在要选择的文本的一端按住鼠标左键并拖动鼠标,到另一端释放鼠标键;用键盘选择文本的方法是:在按光标方向移动键的同时按住Shift键。在应用程序中也可以通过编程选择文本,这时需要通过调用成员函数CEdit::SetSel来实现。这个函数确定了编辑框内文本的选择范围,与该函数相应的还有CEdit::GetSel和CEdit::ReplaceSel,它们分别用来获取编辑框控件中前一选择的开始和结束的位置以及替代被选择的文本。 (3)只读设置 只读设置就是把编辑框设置成一个只读的编辑框,这时该编辑框只能读,不能输入和修改。设置的方法是打开编辑框的属性对话框的Styles选项卡,选中Read-only复选框就将编辑框设置成一个只读的编辑框。也可以通过编程来实现编辑框的只读设置,需要调用成员函数CEdit::SetReadOnly来实现,该函数的原型:BOOL SetReadOnly(BOOL bReadOnly = TRUE);。 图3.45 选择颜色后的结果 参数:bReadOnly为TRUE,就将编辑框设置为只读;否则该编辑框可读可写。 [例3.12]绘制一个矩形,该矩形的颜色是由用户的选择来确定的。 (1)创建一个基于对话框的应用程序,名为:颜色矩形。 (2)删除对话框上的“确定”和“删除 第3章 对话框与控件 ”按钮,参照图3.45向对话框添加控件,按表3.11设置控件属性和设置关联变量。 表3.11 添加的控件和关联的成员变量 控件的名称 ID值 Caption 成员变量 静态文本 IDC_STATIC 红色 静态文本 IDC_STATIC 绿色 静态文本 IDC_STATIC 蓝色 编辑框 ID_RED int m_red 编辑框 ID_GREEN int m_green 编辑框 ID_BLUE int m_blue 按钮 IDC_APPLY 应用 按钮 EXITBUTTON 退出 (3)在“颜色矩形Dlg.h”文件的public下,添加公有成员变量: int m_change; //修改标志 在“颜色矩形Dlg.cpp”文件的构造函数中设置初值: m_change = 1; //表示根据选择的颜色画矩形 (4)添加消息映射函数及其代码: 右击对话框的“应用”按钮,在弹出的快捷菜单中选择ClaasWizard命令,弹出如图3.46所示的对话框。将IDC_APPLY消息映射到CMyDlg类的“应用”控件。并添加如下代码: void CMyDlg::OnApply() { m_change = 1; //使用户视图区无效,以清除上 //一次画的图 Invalidate(); } 说明:m_change = 1 说明用户单击了“应用”按钮,函数Invalidate发出WM_PAINT消息,从而触发视图类的OnPaint函数,进而重绘要更新的区域。 用同样方法,为IDC_EXITBUTTON添加BN_CLICKED消息映射函数OnExitbutton,并添加如下代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } 第3章 对话框与控件 图3.46 加映射消息对话框 (5)在颜色矩形Dlg.cpp文件的OnPaint函数中添加画矩形的代码: void CMyDlg::OnPaint() { … … else { if(m_change==1) {//用屏幕上的当前值更新控制变量 UpdateData(); CPaintDC dc(this); //定义一个画刷类对象 CBrush mybrush; mybrush.CreateSolidBrush(RGB(m_red,m_green,m_blue));//创建画刷 CBrush *pOldbrush = (CBrush*)dc.SelectObject(&mybrush); dc.Rectangle(180,20,280,120); //画一个矩形,这里如果矩形重叠在控件上, // 可将x的两个坐标数写大些,如(220,20,320,20)等 dc.SelectObject(pOldbrush);//恢复原来的画笔 } CDialog::OnPaint(); } } (6)编译运行,在红色编辑框里写“255”,再单击“应用”按钮,则旁边的矩形就呈现红色,若再在绿色编辑框里写“255”,再单击“应用”按钮,则旁边的矩形就呈现黄色。还可进行多个混合选择等,读者可自己尝试看看。结果见图3.45所示。关于绘图的知识,在后边的章节中介绍。 (7)设置静态文本和编辑框的风格: 第3章 对话框与控件 图3.47 静态文本和编辑框的风格 将“红色”静态文本属性对话框的Styles选项卡页面设置Center Verticall属性,并在Align text下拉列表框(Left、Center、Right)中选择Center属性,在Extended Styles选项卡中设置Cliente dge(深凹下框)。同样也为“绿色”设置Center Verticall,Center,而在Extended Styles选项卡中设置Static edge(浅凹下框)。为蓝色设置Center Verticall,Center,而在Extended Styles选项卡中设置Modal Frame(凸起框)属性。而三个编辑框的Expand Styles页面风格都设置Client edge、Static edge和Modal Frame属性,如图3.47所示。 注意:控件的风格要根据外观的需要来选择,而有些则要实现一定的功能,如“设置密码”程序中,在Styles选项卡页面设置PassWord属性。 [例3.13]创建“计算平均成绩”的应用程序 如图3.48所示,在数学、英语、计算机编辑框中输入学生成绩后,单击“计算平均分”按钮,将在静态文本控件上显示出这3个成绩的平均分。 (1)创建一个基于对话框的应用程序,名为:计算平均成绩。 (2)删除对话框上的Cancel按钮,参看图3.48的控件布局,用编辑器为对话框添加如表3.12所示的控件。 图3.48 运行结果 (3)打开ClassWizard的Member Variables标签,在Class name中选择CMyDlog,选中所需的控件ID号,双击或单击Add Variables按钮。依次为表3.13所列控件增加成员变量。 (4)在CMyDlog.cpp的OnInitDialog()函数中添加下列代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … … m_strAve="0.00"; //将成员变量数据传给控件,并在控件中显示 UpdateData(FALSE); return TRUE; } 说明:WM_INITDIALOG消息是当对话框显示前发送的,用户在此消息的映射函数中添加一些设置控件或其他的初始化代码,以便对话框创建时自动调用。 第3章 对话框与控件 表3.12 向对话框上添加的控件 控件 ID号 标题 属 性 静态文本 IDC_STATIC 显示平均分 默认 静态文本 IDC_AVERAGE 默认 Center,Center vertically,Static edge(浅凹) 组框 默认 成绩计算 默认 静态文本 默认 数学 默认 编辑框 IDC_EDIT1 默认 静态文本 默认 英语 默认 编辑框 IDC_EDIT2 默认 静态文本 默认 计算机 默认 编辑框 IDC_EDIT3 默认 按钮 IDC_BUTTON1 计算平均分 默认 按钮 IDOK 退出 Default button,其余默认 表3.13 为相关控件添加成员变量 控件ID号 变量类型 变量名 范围和大小 IDC_AVERAGE CString m_strAve 20 IDC_EDIT1 float m_nScore1 0.0~100.0 IDC_EDIT2 float m_nScore2 0.0~100.0 IDC_EDIT3 float m_nScore3 0.0~100.0 (5)用ClassWziard为按钮IDC_BUTTON1在CMyDlg类中添加BN_CLICKED的消息映射,并添加代码: void CMyDlg::OnButton1() { UpdateData(); //将控件显示的数据传给成员变量 double ave=(double)(m_nScore1+m_nScore2+m_nScore3)/3.0; m_strAve.Format("%6.2f",ave); UpdateData(FALSE); //将成员变量数据传给控件,并在控件中显示 } (6)编译运行,结果如图3.48所示。 说明:代码中,Format是CString类的一个经常使用的成员函数,它通过格式操作使任意类型的数据转换成一个字符串,其输出形式相当于C语言的printf()函数。 3.5.4 列表框 图3.49 列表框1运行结果 列表框(List Box)是一个允许用户从已有的项目中进行选择的控件。列表框中的项目数是灵活变化的,除了可以在资源编辑器中通过设置列表框的属性来增减列表框外 第3章 对话框与控件 ,还可以在程序运行时向列表框中添加或删除某些项,MFC的CListBox类封装了列表框控件的各种操作。下面先举例看一看列表框的应用情况。 [例3.14]创建“列表框”应用程序 如图3.49所示,在编辑框中输入一个 学生的名字,单击“增加”按钮,可将这个学生的名字加入到左边的列表框中;左边的列表框中选择某一个人的名字,单击“删除”按钮,可将这个选定的名字删除。 (1)利用MFC AppWizard[exe]向导,创建一个基于对话框的应用程序,名为:列表框。 (2)添加控件和关联的成员变量如表3.14所示,控件的布局如图3.49所示。 表3.14 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 列表框 IDC_NAMELLIST ClistBox m_NameList 静态文本 IDC_STATIC 请输入姓名 编辑框 IDC_NAME CString m_strName 按钮 IDC_ADD 增加 按钮 IDC_DELETE 删除 按钮 IDC_EXITBUTTON 退出 (3)添加映射消息函数 1)在CMyDlg类为按钮IDC_ADD添加BN_CLICKED映射消息,并添加下列代码: void CMyDlg::OnAdd() { UpdateData();//将控件显示的数据传给成员变量 if(m_strName.IsEmpty()) //判断字符串是否为空 { AfxMessageBox("请输入姓名! "); return; } m_strName.TrimLeft(); //去掉字符串左边的换行符号、空格和控制字符 m_strName.TrimRight(); //去掉字符串右边的换行符号、空格和控制字符 //下面语句是判断输入的字符串是否已在列表框中,即是否重复 if(m_NameList.FindString(-1,m_strName)!=LB_ERR) { AfxMessageBox("该姓名已存在,请重新输入! "); return; } int index; //将输入的字符串加到列表框中,并将索引号保存在index中 index = m_NameList.AddString(m_strName); //下面语句是将字符串与新增的列表项关联起来 m_NameList.SetItemDataPtr(index,new CString(m_strName)); 第3章 对话框与控件 } 2)在CMyDlg类,为按钮IDC_DELETE添加BN_CLICKED消息,并添加代码: void CMyDlg::OnDelete() { int index; index = m_NameList.GetCurSel();//获得当前列表项的索引 if(index !=LB_ERR) { //释放关联数据所占的内存空间 delete (CString *)m_NameList.GetItemDataPtr(index); m_NameList.DeleteString(index); //删除列表框当前选项 UpdateData(false); //在列表框中显示数据 } else AfxMessageBox("没有选择列表项或选择失败! "); } 3)在CMyDlg类,为按钮IDC_ EXITBUTTON添加BN_CLICKED映射消息,并添加代码: void CMyDlg::OnExitbutton() { OnOK(); // 退出 } (4)编译运行,结果如图3.49所示。 1. 列表框的基本操作 列表框中的列表项可以用每一项各自的文本字符串来标识,还可以通过每一列表项的索引来确定。索引表明项目在列表中的排列位置,它从0开始计算,即列表中第一项的索引是0,第二项的索引是1,以此类推。 (1)添加列表项 列表框创建时是一个空的列表,需要用户添加或插入一些列表项。CListBox类的成员函数AddString和InsertString分别用于添加列表项,其函数原型为: int AddString(LPCTSTR lpszItem); int InsertString(int nIndex,LPCTSTR lpszItem); 其中:列表项的字符串文本由参数lpszItem来指定,成功返回列表在列表框的索引,错误返回LB_ERR,空间不够返回LB_ERRSPACE;但InsertString函数不会将列表项进行排序,不管列表框控件是否具有sort(按字母顺序排列)属性,只是将列表项插在指定索引的列表项之前,若nIndex等于-1,则列表项添加在列表框末尾;而AddString函数在当列表框控件具有sort属性时会自动将添加的列表项进行排序;函数原型中,LPCTSTR类型用于表示一个常值字符指针,这里可以将其理解成是一个常值字符串类型。 以上两个函数只能将字符串增加到列表框中,但有时用户还会需要根据列表项,使用其他数据。这时,ListBox的SetItemData和SetItemDataPtr能有效解决这个问题,它们能使用户数据和某个列表项关联起来: int SetItemData(int nIndex,DWORD dwItemData); int SetItemDataPtr(int nIndex,void *pData); 第3章 对话框与控件 其中,函数SetItemData是将一个32位数与某列表项(由nIndex指定)关联起来,而 SetItemDataPtr函数可以将用户的数组、结构体等大量的数据与列表项相关联。若产生错误,它们都返回LB_ERR。 与上述两个函数相对应的两个函数是GetItemData和GetItemDataPar,它们分别用来获取相关联的用户数据。原型如下: DWORD GetItemData(int nIndex) const; void *GetItemDataPtr(int nIndex) const; (2)列表框的单项选择 当选中列表框中某个列表项时,用户可以使用CListBox::GetCurSel函数来获取当前选项中的列表项的索引值。与该函数相对应的CListBox::SetCurSel函数用来设定某个列表项呈选中状态(高亮显示)。 int GetCurSel() const; //返回当前选择项的索引 int SetCurSel(int nSelect); 参数:nSelect用来指定要设置的列表项的索引,错误时这两个函数都将返回LB_ERR。 若要获取某个列表项的字符串,可以用下列函数: int GetText(int nIndex,LPTSTR lpszBuffer) const; void GetText(int nIndex,CString &rString) const; 参数:nIndex用来指定列表项的索引,lpszBuffer和rString用来存放列表项的文本。 (3)查找列表框 为了保证列表项不会重复地添加到列表框中,需要对列表框进行查找,可用CListBox类的成员函数FindString和FindStringExact在列表框中查找与其所匹配的列表项,其中,FindStringExact函数的查找精度高。这两个函数的原型如下: int FindString(int nStartAfter,LPCTSTR lpszItem) const; int FindStringExact(int nIndexStart,LPCTSTR lpszFind) const; 参数:lpszItem和lpszFind指定要查找的列表项的文本值,nStartAfter和nIndexStart指定查找的开始位置,若为-1,则从头至尾查找。找到后,这两个函数都返回所匹配的列表项的索引,否则返回LB_ERR。 (4)删除列表项 利用CListBox类的成员函数DeleteString和ResetContent可以删除指定的列表项或列表框的所有表目,其函数原型如下: int DeleteString(UINT nIndex); void ResetContent(); 参数:nIndex指定要删除的列表项的索引。 注意:若在添加列表项时使用SetItemDataPtr函数,必须在进行删除操作时及时将关联数据所占的内存空间释放出来。 2. 列表框的风格 按性质来分,列表框有单选、多选、扩展多选以及非选四种类型,默认风格下的单选列表框让用户一次只能选择一个项,多选列表框可让用户一次选择几个项,而扩展多选列表框允许用户用鼠标拖动或其他特殊组合键进行选择,非选择列表框不提供选择功能。列表框还有一系列其他风格,用于定义列表框的外观及操作方式,这些风格可在 第3章 对话框与控件 如图3.50所示的列表框的Styles选项卡中设置。表3.15列出了Styles各项的含义。 图3.50 列表框的Styles选项卡 表3.15 列表框的Styles属性 项 目 说 明 Selection(选择) 指定列表框的类型:单选(Single)、多选(Multiple)、扩展多选(Extended)、不选(None) Owner draw(所有者) 自画列表框,默认为No Has strings 选中此复选框,在自画列表框的项目中含有字符串文本 Border 选中此复选框,使列表框含有边框 续表 项 目 说 明 Sort 选中此复选框,列表框的项目按字母顺序排列 Notify 选中此复选框,当用户对列表框操作就会向父窗口发送通知消息 Multi-column 选中此复选框,指定一个具有水平滚动的多列表框 Horizontal scroll 选中此复选框,在列表框中创建一个水平滚动条 Vertical scroll 选中此复选框,在列表框中创建一个垂直滚动条 No redraw 选中此复选框,列表框发生变化后不会自动重画 Use tabstops 选中此复选框,允许使用停止位来调整列表项的水平位置 Want key input 选中此复选框,当用户按键且列表框有输入焦点时,就会向列表框的父窗口发送相应消息 Disable no scroll 选中此复选框,即使列表框的列表项能全部显示,垂直滚动条也会显示,但此时是禁用的(灰显) No integral height 选中此复选框,在创建列表框的过程中,系统会把用户指定的尺寸完全作为列表框的尺寸,而不论是否有项目在列表框,也不能完全显示出来 3. 列表框的通知消息 当列表框中发生了某个动作,如用户双击选择了列表框中某一项时,列表框就会向其父窗口发送一条通知消息。常用的通知消息如表3.16所示。 第3章 对话框与控件 表3.16 列表框的通知消息 通 知 消 息 说 明 LBN_DBLCLK 用户双击列表框的某项字符串时发送此消息 LBN_KILLFOCUS 列表框失去键盘输入焦点时发送此消息 LBN_SELCANCEL 当前选择项被取消时发送此消息 LBN_SELCHANGE 列表框中的当前选项将要改变时发送此消息 LBN_SETFOCUS 列表框获得键盘输入焦点时发送此消息 图3.51 学生成绩登记 [例3.15]编一个学生成绩登记程序 如图3.51所示,在编辑框中输入学生姓名和3门课程的成绩;姓名被添加在列表框中,成绩与该列表项关联。任何时候选中列表中某个学生,相应的记录数据都被显示出来。 (1)创建一个基于对话框的应用程序,名为:成绩登记。 (2)删除原来的Cancel按钮,参看图3.51的控件布局,用编辑器为对话框添加如表3.17所示的控件。 (3)打开成绩登记Dlg.h头文件,在public:下添加数组,代码如下: struct SCORE { int scoreE; //英语成绩 int scoreM; //数学成绩 int scoreC; //计算机成绩 }; (4)打开ClassWizard的Member Variables选项卡,在Class name中选择CMyDlg,选中所需的控件ID号,双击或单击Add Variables按钮,依次为表3.18所示控件增加成员变量。 表3.17 向对话框添加的控件 控 件 ID号 标  题 属  性 组框 默认 学生成绩登记 默认 静态文本 默认 姓名 默认 编辑框 IDC_NAME 默认 静态文本 默认 英语 默认 编辑框 IDC_ENG 默认 静态文本 默认 数学 默认 编辑框 IDC_MAT 默认 静态文本 默认 计算机 默认 编辑框 IDC_COM 默认 第3章 对话框与控件 按钮 IDC_ADD 添加记录 默认 按钮 IDC_DEL 删除记录 默认 按钮 IDOK 退出 Default button,其余默认 列表框 IDC_LIST 默认 表3.18 为控件加成员变量 控件ID号 变量类型 变量名 范围和大小 IDC_LIST CListBox m_List — IDC_NAME CString m_strName 20 IDC_ENG float m_nEng 0.0~100.0 IDC_MAT float m_nMat 0.0~100.0 IDC_COM float m_nCom 0.0~100.0 (5)切换到ClassWizard的Message Maps选项卡,在CMyDlg类为按钮IDC_ADD(添加记录)添加BN_CLICKED的消息映射,并添加下列代码: void CMyDlg::OnAdd() { UpdateData(TRUE);//将控件值赋值给成员变量,UpdateData(FALSE)将成员变量值赋值给控件 //判断m_strName是否为空 if(m_strName.IsEmpty()) { MessageBox("姓名不能为空!"); return; } m_strName.TrimLeft();//删除字符串m_strName左边的空格及控制字符 m_strName.TrimRight();//删除字符串m_strName右边的空格及控制字符 if((m_List.FindString(-1,m_strName))!=LB_ERR) // FindString()在 ListBox 的项中搜索所有搜索文本的实例,即查找有无重复项 { MessageBox("列表框中已有相同姓名,不能添加!"); return; } //向列表框添加学生姓名,并将该学生姓名在列表框中的位置即索引号赋给nIndex,nIndex是索引 //号。 int nIndex=m_List.AddString(m_strName); SCORE data; //定义3门成绩的结构体变量 data.scoreE=m_nEng;// 英语成绩赋予结构体中的变量scoreY data.scoreM=m_nMat;// 数学成绩赋予结构体中的变量scoreM data.scoreC=m_nCom;// 计算机成绩赋予结构体中的变量scoreJ m_List.SetItemDataPtr(nIndex,new SCORE(data)); } 第3章 对话框与控件 说明: ① SetItemDataPtr()或SetItemData()是将一个32位的指针(或一个DWORD的值)同列表框中的一个条目联系起来,并且在设置后可以通过调用GetItemDataPtr()或GetItemData()而获取,这样做的目的是可以将列表框中的条目同外部数据建立联系,这里是姓名和他的成绩联系起来。 ② DWORD表示32位无符号整数,段地址和相关的偏移地址。 (6)用ClassWizard在CMyDlg类为按钮IDC_DEL(删除记录)添加BN_CLICKED的消息映射,并添加下列代码: void CMyDlg::OnDel() { int nIndex=m_List.GetCurSel(); //获得当前选项的索引 if(nIndex!=LB_ERR) { m_List.DeleteString(nIndex); //删除当前选择项 m_strName.Empty(); m_nEng=m_nMat=m_nCom=0; UpdateData(FALSE); //将成员变量值赋值给控件,将编辑框的成绩充0 } else MessageBox("当前没有选择项或列表框操作失败!"); } 说明: ① m_List.GetCurSel();获取当前选择项基于零的索引,若有,则在单选列表框中,如果nIndex不是指定的有效索引,则返回LB_ERR,参数nIndex 指定获取的字符串的基于零的索引; ② if(nIndex!=LB_ERR)表示如果当前无选择项或列表框是多选框时,为LB_ERR。 (7)用ClassWizard在CMyDlg类为按钮IDC_LIST添加LBN_SELCHANGE(当前选择项发生改变)的映射消息,并添加下列代码: void CMyDlg::OnSelchangeList1() { int nIndex=m_List.GetCurSel(); //获得当前项索引 if(nIndex!=LB_ERR) //有效索引,则 { m_List.GetText(nIndex,m_strName); //获得索引号对应的姓名 SCORE *data=(SCORE *)m_List.GetItemDataPtr(nIndex);//获得索引号对应的数据 m_nEng=data→scoreE; //将输入到编辑框的英语成绩赋给对应的成员变量 m_nMat=data→scoreM; //将输入到编辑框的数学成绩赋给对应的成员变量 m_nCom=data→scoreC; //将输入到编辑框的计算机成绩赋给对应的成员变量 UpdateData(FALSE); //再将成员变量值赋给编辑框,用作显示 } } (8)用ClassWizard在CMyDlg类为对话框添加WM_DESTROY消息映射,并添加代码: void CMyDlg::OnDestroy() { for(int nIndex=m_List.GetCount()-1; nIndex>=0; nIndex--) 第3章 对话框与控件 { //删除所有与列表相关联的SCORE结构数据,并释放内存 delete(SCORE *)m_List.GetItemDataPtr(nIndex); } CDialog::OnDestroy();//关闭对话框 } (9)编译运行,结果如图3.51所示。 说明: ① getCount():返回编辑器内容的字数统计。 nIndex =m_List.GgetCount(int nType);如果有参数,这是唯一的参数,是一个表示获取字数统计的类型,数字型。其值必须是下面三种之一:0:表示统计英文字数;1:表示统计中文字数;2:表示统计中文和英文的字数。 ② 对话框被清除时发送WM_DESTROY消息。用户在此消息的映射函数中添加一些对象删除代码,以便在对话框清除前有效地释放内存空间。 3.5.5 组合框 组合框(Combo Box)控件是一个列表框和一个编辑框(或静态控件)结合而成的特殊控件,因此,组合框控件可分为编辑区和列表区两个部分。用户当前在列表区里选中的选项将被显示在控件的编辑区中。其中,组合框控件的列表区可以始终处于打开显示状态,也可以仅当用户单击组合框控件旁边的下拉箭头按钮时才被打开。下面先举一简单实例,看一看组合框的应用。 图3.52 运行结果 [例3.16]创建一个“组合框”应用程序 这个程序的功能是:如果在组合框中选择“可视化教室”所在的房间,则在组合框下方显示用户的选择。例如,在组合框中选择了“20号楼202房间”,则在组合框下方显示“可视化教室位于20号楼202房间”,如图3.52所示。 (1)利用MFC AppWizard[exe]向导创建一个基于对话框的应用程序,名为:组合框。 (2)参照图3.52的控件布局,向对话框添加表3.19所示的控件和关联的成员变量。 表3.19 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 静态文本 IDC_STATIC 可视化教室位于 组合框 IDC_ROOM CComboBox m_Room 按钮 IDC_EXITBUTTON 退出 (3)在“组合框Dlg.h”头文件public下添加如下成员变量: 第3章 对话框与控件 CString msg; int m_change; 在“组合框Dlg.cpp”实现文件的构造函数中为如上成员变量赋初值: CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/) : CDialog(CMyDlg::IDD, pParent) { ……………… msg = " "; m_change =0; } (4)右击组合框控件(IDC_ROOM),在Data属性中输入: 20号楼201房间、20号楼202房间、20号楼203房间、20号楼204房间、 20号楼205房间、20号楼206房间 注意:输入时,一定要每个房间号占一行,如“20号楼201房间”要占一行,按Ctrl+Enter组合键换行后再写20号楼202房间…… (5)添加消息映射函数 1)在CMyDlg 类为退出按钮控件(IDC_EXITBUTTON),添加BN_CLICKED消息映射,并添加代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } 2)在CMyDlg 类为组合框控件(IDC_ROOM),添加CBN_SELCHANGE消息映射,并添加代码: void CMyDlg::OnSelchangeRoom() { CString roomname; int index = m_Room.GetCurSel(); //获得当前选项的索引 if(index == CB_ERR) { AfxMessageBox("出现错误"); return; } m_Room.GetLBText(index,roomname); //获得当前选项的文本→ roomname(教室编号) msg = "可视化教室位于"; msg += roomname; m_change = 1; Invalidate(); //使用户视图区无效,以清除上一次画的图 } (6)在实现文件CMyDlg.cpp的OnPaint函数中添加显示文本的代码: void CMyDlg::OnPaint() { if (IsIconic()) { … … … } else { if(m_change == 1) 第3章 对话框与控件 { CPaintDC dc(this); // 设置透明模式 dc.SetBkMode(TRANSPARENT); dc.TextOut(50,100,msg); // 输出文本 } CDialog::OnPaint(); } } (7)编译运行,结果如图3.53所示。 说明: ① 当视图变得无效时(包括大小的改变,移动,被遮盖等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC类的dc对象来响应该消息并调用视图的 OnDraw 成员函数。通常不必编写重写的 OnPaint处理成员函数; ② CPaintDC类是一个来自CDC的设备环境类。它在构造期间执行CWnd::BeginPaint,在析构期间执行CWnd::EndPaint,一个CPaintDC对象只在响应一个WM_PAINT消息的时候被使用,通常是在OnPaint消息处理成员函数中; ③ SetBkMode(OPAQUE)用当前的背景颜色填充背景同,或者SetBkMode(TRANSPAARENT)使背景保持不变,这两种方法都可以设置背景模式。 1. 组合框常见操作 MFC的CComboBox类封装了组合框控件的各种操作,组合框的操作可以调用CComboBox的成员函数来实现。其操作大致分为两类,一类是对组合框中的列表框进行操作,另一类是对组合框中的编辑框进行操作,如表3.20所示。由于组合框的一些编辑操作与编辑框CEdit的成员函数相似,如:GetEditSet,SetEditSel等,因此这些成员函数没有在上述表中列出。组合框的操作与列表框的操作类似。 表3. 20 CComboBox类常用成员函数 成员函数 作 用 说 明 int AddString(LPCTSTR lpszString); 向组合框添加字符串 错误返回CB_ERR,空间不够时,返回CB_ERRSPACE int DeleteString(UINT nIndex); 删除指定的索引项 返回剩下的列表项总数,错误时返回CB_ERR int InsertString(int nIndex, LPCTSTR lpszString); 在指定的位置处插入字符串,若nIndex=-1时,向组合框尾部添加 返回插入字符的索引,错误时返回CB_ERR;空间不够时,返回CB_ERRSPACE void ResetContent(); 删除组合框的全部项和编辑文本 int FindString(int nStartAfter, LPCTSTR lpszString)const; 查找字符串 参数1=搜索起始项的索引,-1时从头开始,参数2=被搜索字符串 int FindStringExact(int nIndexStart, LPCTSTR lpszFind) 精确查找字符串 返回匹配项的索引,错误时返回CB_ERR 第3章 对话框与控件 const; int SelectString(int nStartAfter, LPCTSTR lpszString); 选定指定字符串 返回选择项的索引,若当前选择项没有改变,则返回CB_ERR int GetCurSel()const; 获得当前选择项的索引 当没有当前选择项时,返回CB_ERR int SetCurSel(int nSelect); 设置当前选择项 参数为当前选择项的索引,-1时没有选择项。错误时返回CB_ERR int GetCount(); 获取组合框的项数 错误时返回CB_ERR int SetDroppedWidth(UINT nWidt); 设置下拉组合框的最小像素宽度 成功时返回新的组合框宽度,否则返回CB_ERR int SetItemData(int nIndex, DWORD dwItemData); 将一个32位值和指定列表项关联 错误时返回CB_ERR int SetItemDataPtr(int nIndex, void* pData); 将一个值的指针和指定列表项关联 错误时返回CB_ERR DWORD GetItemData(int nIndex)const; 获取和指定列表项关联的一个32位值 错误时返回CB_ERR void *GetItemDataPtr(int nIndex)const; 获取和指定列表项关联的一个值的指针 错误返回-1 int GetLBText(int nIndex, LPTSTR lpszText); 获取指定项的字符串 返回字符串的长度,若第1个参数无效时返回CB_ERR 续表 成员函数 作 用 说 明 int GetLBText(int nIndex, CString&rString) int GetLBTextLen(int nIndex)const; 获取指定项的字符串长度 若参数无效时返回CB_ERR 2. 组合框的风格 按照组合框的主要风格特征,可把其分为3类:简单组合框、下拉式组合框和下拉式列表框。简单组合框和下拉式组合框都包含列表框和编辑框,但是简单组合框中的列表框不需要下拉,是直接显示出来的,而当用户单击下拉式组合框中的下拉按钮时,下拉的列表框才被显示出来。图3.53是组合框属性对话框的Styles(风格)选项卡,表3.21列出了其各项含义。 第3章 对话框与控件 图3.53 Styles选项卡 表3.21 组合框的Styles属性 项 目 说 明 Type 设置组合框的类型:Simple(简单)、Dropdown(下拉)、Drop List(下拉列表框) Owner draw 自画组合框,默认为No Has strings 选中此复选框,在自画组合框中的项目中含有字符串文本 Sort 选中此复选框,组合框的项目按字母顺序排列 Vertical scroll 选中此复选框,在组合框中创建一个垂直滚动条 No integral height 选中此复选框,在创建组合框的过程中,系统会把用户指定的尺寸完全作为组合框的尺寸,而不管是否会有项目在组合框的列表中不能完全显示出来 OEM convert 选中此复选框,实现对特定字符集的字符转换 Auto HScroll 当用户在行尾输入一个字符时,文本自动向右滚动 Disable no scroll 选中此复选框,即使组合框的列表项能全部显示,垂直滚动条也会显示,但此时是禁用的(灰色的) Uppercase 选中此复选框,输入在编辑框的字符全部转换成大写形式 Lowercase 选中此复选框,输入在编辑框的字符全部转换成大写形式 3. 组合框的通知消息 在组合框通知消息中,有的是操作列表框发出的,有的是操作编辑框发出的,如表3.22所示。 表3.22 组合框的通知消息 通知消息 说 明 CBN_CLOSEUP 当组合框的列表关闭时发送此消息 CBN_DBLCLK 用户双击组合框的某项字符串时发送此消息 CBN_DROPDOWN 当组合框的列表打开时发送此消息 CBN_EDITCHANGE 同编辑框的EN_CHANGE消息 CBN_EDITUPDATE 同编辑框的EN_UPDATE消息 CBN_SELENDCANCEL 当前选择项被取消时发送此消息 CBN_SELENDOK 当用户选择一个项并按Enter键或单击下拉箭头隐藏列表框时发送此消息 CBN_KILLFOCUS 组合框失去键盘输入焦点时发送此消息 CBN_SELCHANGE 组合框中的当前选择项将要改变发送此消息 CBN_SETFOCUS 组合框获得键盘输入焦点时发送此消息 第3章 对话框与控件 3.5.6 旋转按钮控件 旋转按钮控件(Spin)又称为上下控件(Up Down Control)或微调控件,其主要功能是利用一对标有相反方向箭头的小按钮,通过单击它,在一定范围内改变当前的数值。旋转按钮控件的当前值通常显示在一个称为伙伴窗口(Buddy Window)的控制中,可以是一个编辑框等。旋转按钮控件的各种操作封装在MFC的CSpinButtonCtrl类中。下面先看看旋转按钮控件的用法。 [例3.17]创建一个“旋转按钮”应用程序 根据选择的长和宽的值,单击“应用”按钮后在对话框的右边画一个矩形。例如长选180,宽选160,单击“应用”按钮后的对话框如图3.54所示。 (1)创建一个基于对话框的应用程序,名为:旋转按钮。 (2)参照图3.54的控件布局,按表3.23所示添加控件和在CMyDlg类添加关联的成员变量。为了把旋转按钮和编辑框关联在一起,需要设置如图3.55所示的TabOrder顺序,并且需要设置两个旋转按钮的Vertical、Right、Auto buddy、Sel buddy integer、Wrap和Arrow keys属性。 图3.54 运行结果 图3.55 对话框控件的TabOrder顺序 表3.23 添加的控件和关联的成员变量 控件名称 ID值 Caption 成员变量 静态文本 IDC_STATIC 矩形长 编辑框 IDC_LENGTH int m_Length 旋转按钮 IDC_SPIN1 CSpinButtonCtrl m_LengthSpin 静态文本 IDC_STATIC 矩形宽 编辑框 IDC_WIDTH int m_Width 旋转按钮 IDC_SPIN2 CSpinButtonCtrl m_WidthSpin 按钮 IDC_APPLY 应用 CButton m_Room 第3章 对话框与控件 按钮 IDC_EXITBUTTON 退出 (3)在CMyDlg.cpp实现文件的OnInitDialog函数中,添加如下代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); m_WidthSpin.SetRange(10,100); //设置旋转按钮的范围10~100 m_WidthSpin.SetPos(0); //设置旋转按钮的当前位置0 m_LengthSpin.SetRange(10,100);//设置旋转按钮的范围10~100 m_LengthSpin.SetPos(0); //设置旋转按钮的当前位置0 return TRUE; // return TRUE unless you set the focus to a control } (4)在CMyDlg类为IDC_APPLY的BN_CLICKED消息添加映射函数,并添加代码: void CMyDlg::OnApply() { UpdateData(); //将控件显示的数据传给成员变量 Invalidate(); //使用户视图区无效,以清除上一次画的图 UpdateWindow(); //刷新用户视图区域 CClientDC dc(this); dc.Rectangle(180,10,180+m_Length,10+m_Width); //画矩形 } (5)为IDC_EXITBUTTON的BN_CLICKED消息添加映射函数,并添加代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } 说明: 在对话框上的排序(Tab Order),对于旋转按钮控件非常重要,例如,该例旋转按钮的排序为3,而左边是编辑控件(矩形长)排序为2,只有这两个控件紧靠在一起,旋转按钮的增减才对编辑控件的数值起作用,从旋转按钮设置的属性“Right”也可看出是位于伙伴控件(编辑框)的右边。 1. 旋转按钮控件的基本操作 旋转按钮控件的基本操作包括基数、范围、位置的设置和获取等。 (1)基数的设置和获取 CSpinButtonCtrl类的成员函数SetBase用来设置基数,这个基数值决定了窗口显示的数字是十进制数还是十六进制数。如果成功则返回先前的基数值;如果给出一个无效的基数则返回0,该函数的原型如下: int SetBase(int nBase); 参数:nBase表示控件的新基数,可以取10或16,分别用于设置基数为十进制或十六进制。 与SetBase函数相应的函数是GetBase,它用于获取旋转按钮控件的基数,该函数的原型为:UINT GetBase()const;。 (2)范围及位置的设置和获取 默认时,旋转按钮控件的最大值是100,最小值是0,可用成员函数SetRange或SetRange32来 第3章 对话框与控件 设置旋转按钮的范围,SetRange32函数是设置旋转按钮的32位范围,这两个函数的原型如下: void SetRange(int nLower,int nUpper); void SetRange32(int nLower,int nUpper); 参数:nLower和nUpper表示控件的上限和下限,任何一个界限值不能大于0x7fff或小于-0x7fff。 与SetRange和SetRange32函数相应的函数是GetRange和GetRange32,这两个函数的原型如下: DWORD GetRange()const; void GetRange(int &lower,int &upper)const; void GetRange32(int &lower,int &upper)const; 参数:lower和upper表示旋转按钮控件的下限和上限。 成员函数SetPos用来设置旋转按钮控件的当前位置,函数的原型为:int SetPos(int nPos);。 参数:nPos表示控件的新位置,它必须在控件的上限和下限指定的范围内。 与SetPos函数相应的函数是GetPos,函数的原型为:int GetPos()const;。 2. 旋转按钮的通知消息 旋转按钮控件的通知消息只有一个:UDN_DELTAPOS,它是在控件的当前数值将要改变时向其父窗口发送的。 3. 旋转按钮控件的常用风格 旋转按钮的风格可以通过其属性对话框进行设置,如图3.56所示,其各项含义见表3.24所示。 图3.56 旋转按钮的Styles选项卡 表3.24 旋转按钮控件的Styles属性 项  目 说  明 Orientation 控件放置方向:Vertical(垂直),Horizontal(水平) Alignment 控件在伙伴窗口的位置安排:Unattached(不相干),Right(右边),Left(左边) Auto buddy 选中此项,自动选择一个Z-order中的前一个窗口作为控件的伙伴窗口 Set buddy integer 选中此项,使控件设置伙伴窗口数值,这个值可以是十进制或十六进制 No thousands 选中此项,不在每隔三个十进制数字的地方加上千分隔符 Wrap 选中此项,当增加或减小的数值超出范围时,则从最小值或最大值开始回绕 Arrow keys 选中此项,当按下向上和向下方向键时,也能增加或减少 Hot track 选中此项,当光标移过控件时,突出显示控件的上下按钮 第3章 对话框与控件 [例3.18]学生成绩输入 (1)创建一个单文档的应用程序,名为:输入成绩。 (2)添加一个新的对话框资源,将ID号改为IDD_INPUT,标题为:学生成绩输入,将OK和Cancel按钮标题改为“确定”和“取消”,并将其移到下边。 (3)参照图3.57的控件布局,向对话框添加如表3.25所示的控件。 (4)选择Layout→Tab Order命令或按Ctrl+D组合键,此时每个控件的左上方都有一个数字,表明了当前Tab键的次序,单击对话框中的控件,重新设置控件的Tab键次序,以保证旋转按钮控件的Tab键次序在相对的编辑框(伙伴窗口)之后,结果如图3.58所示。 图3.57 “学生成绩输入”对话框 图3.58 改变控件的Tab键次序 (5)双击对话框模板空白处,为该对话框模板创建一个对话框类CInputDlg。 (6)在ClassWizard的Member Variables选项卡的Class name中选择CInputDlg,选中所需的控件ID号,双击或单击Add Variables按钮,依次按表3.26所示控件增加成员变量。 (7)在MFC ClassWizard的Message Maps选项卡中,为CInputDlg添加:WM_INITDIALOG消息映射,并添加下列代码: BOOL CInputDlg::OnInitDialog() { CDialog::OnInitDialog(); m_spinSeng.SetRange(0,100); m_spinSmat.SetRange(0,100); m_spinScom.SetRange(0,100); return TRUE; } (8)用MFC ClassWizard为CinputDlg类添加IDC_SPIN_SENG控件的:UDN_DELTAPOS消息映射(用来处理浮点数),并添加代码: void CInputDlg::OnDeltaposSpinSeng(NMHDR* pNMHDR, LRESULT* pResult) { NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; //将控件的内容保存到变量中 UpdateData(TRUE); //将控件显示的数据传给成员变量 第3章 对话框与控件 m_fSeng +=(float)pNMUpDown→iDelta * 0.5f; if(m_fSeng<0.0) m_fSeng=0.0f; if(m_fSeng>100.0) m_fSeng=100.0f; UpdateData(FALSE); // 将成员变量的内容显示到控件上 *pResult = 0; } 表3.25 添加的控件 添加的控件 ID号 标 题 属 性 组框 默认 学生成绩输入 静态文本 默认 姓名 编辑框 IDC_EDIT_NAME 静态文本 默认 学号 编辑框 IDC_EDIT_NO 静态文本 默认 英语 编辑框 IDC_EDIT_SENG 旋转按钮 IDC_SPIN_SENG Auto buddy,Right 静态文本 默认 数学 编辑框 IDC_EDIT_SMAT 旋转按钮 IDC_SPIN_SMAT Auto buddy, Set butty integer, Right 静态文本 默认 计算机 编辑框 IDC_EDIT_SCOM 旋转按钮 IDC_SPIN_SCOM Auto buddy,Set buddy integer, Right 表3.26 添加成员变量 控件ID号 变量类别 变量类型 变量名 范围和大小 IDC_EDIT_NAME Value CString m_strName 20 IDC_EDIT_NO Value CString m_strNo 20 续表 控件ID号 变量类别 变量类型 变量名 范围和大小 IDC_EDIT_SENG Value float m_fSeng 0.0~100.0 IDC_SPIN_SENG control CSpinButtonCtrl m_spinSeng IDC_EDIT_SMAT Value float m_fSmat 0.0~100.0 IDC_SPIN_SMAT control CSpinButtonCtrl m_spinSmat IDC_EDIT_SCOM Value float m_fScom 0.0~100.0 IDC_SPIN_SCOM control CSpinButtonCtrl m_spinScom 说明:程序代码中,NM_UPDOWN结构用于反映旋转按钮控件的当前位置(由成员iPos指定)和增量大小(由成员iDelta指定)。 第3章 对话框与控件 (9)打开项目工作区的ResourceView选项卡,打开Menu,双击IDR_MAINFRAME,添加顶层菜单项:测试(&T),在其下面添加一个子菜单项:学生成绩输入(&I),ID为ID_TEST_INPUT。 (10)用MFC ClassWizard为CMainFrame 类添加菜单项ID_TEST_INPUT的COMMAND消息映射,并添加下列代码: void CMainFrame::OnTestInput() { CInputDlg dlg; if(IDOK == dlg.DoModal()) { CString str; str.Format("%s, %s, %4.1f, %4.1f, %4.1f", dlg.m_strName, dlg.m_strNo, dlg.m_fSeng, dlg.m_fSmat, dlg.m_fScom); AfxMessageBox(str); //将对话框上的编辑框内输入的数据用消息对话框显示出来 } } 说明: 程序代码中,if语句是判断用户是否单击对话框的“确定”按钮,Format是CString类的一个经常使用的成员函数,它通过格式操作使任意类型的数据转换成一个字符串,该函数的第一个参数是带格式的字符串,其中的“%”就是一个格式符,每一个格式符依次对应于该函数的后面参数表中的参数项,例如格式字符串中第一个“%s”对应于Dlg.m_strName,CString类的Format和C语言中的库函数printf()十分相似。 (11)在文件MainFrm.cpp的前面将类CInputDlg类包含进来: #include "InputDlg.h" (12)编译并运行,如图3.57所示。单击“英语”的旋转按钮控件,将以0.5增量来改变它的伙伴窗口的数值。而“数学”、“计算机”的旋转按钮控件由于设置了Set buddy integer属性,因此它按默认增量1自动改变伙伴窗口的数值。按确定按钮出现图3.59所示的对话框。 图3.59 运行结果 3.5.7 进展条 进展条控件常用于向用户反映一个操作或任务的执行进度情况。进展条最初是一个空白的矩形区域,随着时间的推移、任务的执行,该矩形区域将会逐渐填入颜色块。直到任务执行完毕,进展条也被颜色块所填满。下面就是使用进展条的一个实例。 [例3.19]创建一个“进展条”应用程序 第3章 对话框与控件 用程序语句创建一个进展条和一个按钮,当单击按钮时进展条就向前推进。 (1)用AppWizard创建一个单文档的应用程序,名为:进展条。 (2)在视图类“进展条View.h”文件的public下,定义进展条类和按钮类的变量: CProgressCtrl m_progress; CButton m_progressBtn; (3)在视图类中,自行编写创建进展条的成员函数CreateProgressBar()步骤是:打开项目工作区的ClassView选项卡,右击CMyView,在弹出的快捷菜单中选择Add Member Function命令,在弹出的对话框的Function Type处写:void,在Function Declaration处写:CreateProgressBar,单击OK按钮,这样就将该函数加到了视图类中。 void CMyView::CreateProgressBar() { //创建进展条 m_progress.Create(WS_CHILD|WS_BORDER|WS_VISIBLE, CRect(100,30,400,60),this,IDC_PROGRESS); //设置属性 m_progress.SetRange(1,100); //设置进展条范围1~100之间 m_progress.SetStep(10); //设置进展条的步长10 m_progress.SetPos(0); //设置进展条的初始位置0 } (4)在视图类中,自行编写创建按钮的成员函数CreateButton()的步骤是:打开项目工作区的ClassView选项卡,右击CMyView,在弹出的快捷菜单中选择Add Member Function命令,在弹出的对话框的Function Type处写:void,在Function Declaration处写: CreateButton,单击OK按钮,这样就将该函数加到了视图类中。 void CMyView::CreateButton() { m_progressBtn.Create("Start",WS_CHILD|WS_BORDER|WS_VISIBLE, CRect(210,70,300,100),this,IDC_START); } (5)添加资源符号 在程序中所添加的进展条和按钮控件标识符必须加以说明,否则Visual C++将不予承认。 1)选择View→Resource Symbols命令,弹出如图3.60所示的Resource Symbols对话框。 2)单击New按钮,弹出New Symbol对话框,如图3.61所示。 3)在New Symbol对话框的Name栏中输入控件资源标志符:IDC_PROGRESS(默认Value为:101)。 第3章 对话框与控件 图3.60 Resource Symbols对话框 图3.61 New Symbol对话框 4)按如上3步将资源符号IDC_START(Value为:102)也加进去。 (6)因为希望在视图创建的同时创建控件,因此应当将添加控件的操作放置到视图类的OnCreate函数中去。使用ClassWizard为视图类的WM_CREATE消息添加处理函数OnCreate(),并将CreateProgressBar()、CreateButton()两个函数加入其中: int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct) //创建视图 { if (CView::OnCreate(lpCreateStruct) == -1) if (CView::OnCreate(lpCreateStruct) == -1) return -1; CreateProgressBar(); //创建进展条 CreateButton(); //创建"Start"按钮 return 0; } (7)当单击Start按钮时,Start按钮与进展条之间将进行通信,因此还需为按钮控件IDC_START编写消息处理函数OnStart()。 由于MFC没有设置该消息映射,因此需要自行添加,过程如下: 1)在视图类“进展条View.h”文件中添加: protected: //{{AFX_MSG(CMy1View) …… …… …… afx_msg void OnStart(); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG 2)在视图类“进展条View.cpp”文件的消息入口处添加: BEGIN_MESSAGE_MAP(CMy1View, CView) //{{AFX_MSG_MAP(CMy1View) ON_WM_CREATE() ON_COMMAND(IDC_START,OnStart) ON_WM_TIMER() 第3章 对话框与控件 //}}AFX_MSG_MAP 3)在视图类“进展条View.cpp”文件中添加函数(全用手写): void CMyView::OnStart() { SetTimer(IDC_START,1000,NULL); } 4)在按钮控件单击消息处理函数中调用了SetTimer函数创建了一个定时器,规定每隔1 s(1 000 ms)就触发定时器一次,执行定时例程OnTimer()。因此要为视图类WM_TIMER消息添加如下处理函数OnTimer(): void CMy1View::OnTimer(UINT nIDEvent) { m_progress.StepIt(); CView::OnTimer(nIDEvent); } (8)编译运行,如图3.62所示。 图3.62 运行结果 由此看出,每次触发定时器时,程序将调用StepIt函数使进度条向前推进。 1. 进展条的基本操作 进展条的基本操作是通过CProgressCtrl类的相关函数来实现的。 (1)Create函数 该函数用于创建新的进展条并将其与CProgressCtrl对象相联系,其格式为: BOOL Create(DWORD dwStyle,const RECT &rect,CWnd *pParentWnd,UINT nID); 参数: dwStyle参数用于指定进展条控件的风格; rect参数为一个CRect类型或RECT结构的量,用来指定进度条控件的大小和位置; pParentWnd参数为指向进度条控件父窗口的指针; nID参数指定进展条控件的ID值。 (2)SetRange函数 该函数用于设置进度条控件的最小范围和最大范围,格式为: void SetRange(int nLower,int nUpper); 参数: nLower参数用于指定进度条控件的下届; nUpper参数用于指定进度条控件的上届。 (3)SetPos参数 该函数用于为进度条控件设置新位置,格式为:int SetPos(int nPos); (4)OffsetPos函数 该函数用于将进度条控件的位置向前推进指定单位,格式为:int OffsetPos(int nPos); 参数:nPos参数用于指定推进的单位。 (5)SetStep函数 第3章 对话框与控件 该函数用于设置进度条空件的步长,格式为:int SetStep(int nStep); 使用此函数可以将进度条控件的步长设置为nStep个单位,以后当用户调用CProgessCtrl::StepIt函数时,进度条控件的位置将向前推进nStep个单位。 (6)StepIt函数 该函数用于根据进展条控件的步长将进度条的位置向前推进一步,格式为:int StepIt(); 2. 进展条的风格 打开进展条的Styles选项卡,如图3.63所示。可以看到它的风格属性并不多,其中,Border(边框)用来指定进展条是否有边框,Vertical(垂直)用来指定进展条是水平还是垂直的,不选中该属性,表示进展条从左到右水平显示。Smooth(平滑)表示平滑地填充进展条。若不选中则表示将用块来填充,如图3.64所示。 图3.63 进展条的Styles选项卡 图3.64 运行结果 [例3.20]创建一个“打开”应用程序 这个实用程序的功能是:当选择浮动菜单的打开项时,弹出如图3.64所示的对话框。程序的编写过程有以下几步。 (1)创建一个基于对话框的应用程序,名为:打开。 (2)参照图3.64的控件布局,按表3.27所示向对话框添加控件和关联的成员变量。 (3)添加一个浮动菜单资源。 1)选择ResourceView,右击最上面的“打开resource*”,在弹出的快捷菜单中选择Insert命令,弹出Insert Resource对话框,选择Menu,单击New按钮(默认的ID号为IDR_MENU1)。将此默认的菜单资源ID号改为IDR_MYFLOATMENU。 表3.27 添加的控件和关联的成员变量 控件名称 ID值 Caption 成员变量名 类型 静态文本 IDC_STATIC 进展条 IDC_PROGRESS m_progress CProgressCtrl 按钮 IDC_EXITBUTTON 退出 2)用菜单编辑器为该菜单资源中的顶层菜单的第一项加一任意标题:浮动菜单(实际上该标题无用),在此菜单下添加如表3.28所示的菜单项。 表3.28 浮动菜单项 菜单ID Caption 属性 第3章 对话框与控件 ID_OPENFLIE 打开文件(&O) ID_CLOSE 关闭文件(&C) ID_PRINT 打印文件(&P) 其他(&O) Pop_up (4)打开ClassWizard,将弹出一个对话框,询问是“选择一个已存在的类,还是创建一个新类”;选择“选择一个已存在的类(Select an existing class)”项并选定CMyDlg类。 (5)在CMyDlg类中,选择表3.28所示的菜单ID,分别双击COMMAND消息,增加处理菜单项的函数。这里仅为菜单ID_OPENFLIE添加消息映射函数,并添加下列代码: void CMyDlg::OnOpenflie() { m_progress.ShowWindow(SW_SHOW); //显示进展条空件 UpdateWindow(); //刷新 SetDlgItemText(IDC_STATIC,"正在打开文件..."); //设置静态文本的显示内容 for(int i=0;i<20;i++) { Sleep(100); //等待 m_progress.StepIt(); //填充一个蓝色块 } m_progress.ShowWindow(SW_HIDE); //隐藏进展条控件 SetDlgItemText(IDC_STATIC," "); //设置静态文本的显示内容 } (6)打开“打开文件Dlg.cpp”文件,在其OnInitDialog函数中添加如下代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … m_progress.ShowWindow(SW_HIDE); //隐藏进展条控件 m_progress.SetRange(1,200); //设置进展条范围 m_progress.SetStep(10); //设置进展条的步长 m_progress.SetPos(0); //设置进展条的初始位置 return TRUE; // return TRUE unless you set the focus to a control } (7)右击对话框的任何位置,便弹出刚才设计的浮动菜单,在CMyDlg类加入WM_CONTEXTMENU消息处理函数,添加代码: void CMainFrame::OnContextMenu(CWnd *pWnd,CPoint point) { CMenu menu; menu.LoadMenu(IDR_MYFLOATMENU);//刚才加上的菜单资源 menu.GetSubMenu(0)→TrackPopupMenu(TPM_LEFTALIGN| 第3章 对话框与控件 TPM_RIGHTBUTTON,point.x,point.y,this); } (8)为IDC_EXITBUTTON的BN_CLICKED消息添加消息映射函数,并添加代码: void CMyDlg::OnExitbutton() { OnOK(); } (9)编译运行,如图3.65所示,右击对话框的任何位置,弹出快捷菜单,如图3.66所示,选择“打开文件”命令,便出现图3.64所示的最终结果。 图3.65 运行后的对话框 图3.66 右击对话框 [例3.21]创建一个“使用进展条”的应用程序 用编辑器向对话框添加一个进展条和“继续”、“后退”两个按钮,当单击“继续”按钮时,进展条向前进,当单击“后退”按钮时,进展条向后退,对话框上的静态文本中还能显示出进展条的百分比。 图3.67 运行结果 (1)创建一个单文档的应用程序,名为:使用进展条。 (2)向应用程序添加一个对话框资源,ID为:IDD_PROGRESS,标题为:进展条对话框,为该对话框建类,类名为:CProgressDlg。 (3)删除原来的Cancel按钮,将OK按钮标题改为“退出”。 (4)参照图3.67的控件布局,用编辑器为对话框添加如表3.29所示的一些控件。 表3.29 添加的控件 添加的控件 ID号 标题 其他属性 静态文本 IDC_STATIC_TEXT 默认 AlignText 设为Center,其余默认 进展条 IDC_PROGRESS1 默认 第3章 对话框与控件 按钮(后退) IDC_BUTTON_BACK 默认 按钮(继续) IDC_BUTTON_GOON 默认 (5)为静态文本IDC_STATIC_TEXT控件添加一个CString类型的成员变量m_strPercent,为进展条IDC_PROGRESS1控件添加一个CProgressCtrl类型的成员变量m_Progress。 (6)打开项目工作区ClassView选项卡,右击对话框类CProgressDlg,从弹出的快捷菜单中选择Add Member Function命令,添加一个void类型的成员函数UpdatePercentText(),用来当进展条位置变化后更新静态文本控件显示的百分比,并添加代码: void CProgressDlg::UpdatePercentText() { int nPos = m_Progress.GetPos(); //获取进展条的当前位置 int nLow,nUp; //进展条起初和最末位置 m_Progress.GetRange(nLow,nUp); //获取进展条范围 m_strPercent.Format("%4.0f%%",(float)nPos/(float)(nUp-nLow)*100.0); UpdateData(FALSE); // 将成员变量的数据赋给控件 } (7)用MFC ClassWizard为CProgressDlg类添加WM_INITDIALOG消息映射,并添加下列代码: BOOL CProgressDlg::OnInitDialog() { CDialog::OnInitDialog(); m_Progress.SetRange(0,100); //设置进展条范围 m_Progress.SetStep(5); //设置进展条步长 m_Progress.SetPos(30); //设置进展条的当前位置 UpdatePercentText();//自定义函数,当进展条位置变化后更新静态文本控件显示的百分比 return TRUE; // return TRUE unless you set the focus to a control } (8)为按钮IDC_BUTTON_BACK添加BN_CLICKED的消息映射,并添加代码: void CProgressDlg::OnButtonBack() { int nPos=m_Progress.GetPos(); //获取进展条的当前位置 int nLow,nUp; m_Progress.GetRange(nLow,nUp); //获取进展条范围 nPos=nPos-5;//进展条当前位置-步长(后退) if(nPos255) m_RValue = 255; m_Scroll.SetScrollPos(m_RValue); } UpdateData(FALSE); //将成员变量数据传给控件 Draw(); CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (10)为第5步添加的Draw()函数,添加下列代码: void CMyDlg::Draw() { CWnd *pWnd=GetDlgItem(IDC_DRAW);//获得静态文本的起始地址,即ID标识 CDC *pDC=pWnd→GetDC(); //获得窗口当前的设备环境指针 CBrush drawBrush; //定义画刷变量 drawBrush.CreateSolidBrush(m_RValue);//建立画刷颜色值 CBrush *pOldBrush=pDC→SelectObject(&drawBrush); CRect rcClient; pWnd→GetClientRect(rcClient); pDC→Rectangle(rcClient); pDC→SelectObject(pOldBrush); } (11)编译运行,结果如图3.69所示。 说明:由于滚动条中间的滚动块在默认时是不会停止在用户操作的位置处的,因此需要调用SetScrollPos函数来进行相应位置的设定。 1. 滚动条(CScrollBar类)的操作函数 (1)GetScrollPos()函数,格式:int GetScrollPos() const; 该函数返回滚动框的当前位置,若操作失败则返回0。 (2)SetScrollPos()函数,格式:int SetScrollPos(int nPos,BOOL bRedraw = TRUE); 该函数将滚动框移动到指定位置。 参数: nPos指定了新的位置; bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘; 函数返回滚动框原来的位置,若操作失败返回0。 (3)GetScrollRange()函数,格式: 第3章 对话框与控件 void GetScrollRange(LPINT lpMinPos,LPINT lpMaxPos)const; 该函数对滚动条的滚动范围进行查询。 参数: lpMinPos和lpMaxPos分别指向滚动范围的最小和最大值。 (4)SetScrollRange()函数,格式: void SetScrollRange(int nMinPos,int nMaxPos,BOOL bRedraw=TRUE); 该函数用于指定滚动条的滚动范围。 参数: nMinPos和nMaxPos分别指定了滚动范围的最小和最大值,这两者指定的滚动范围不得超过32 767,当两者都为0时,滚动条将被隐藏; bRedraw 表示是否需要重绘滚动条,如果为TRUE,则重绘。 (5)GetScrollnfo()函数,格式: BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo,UINT nMask); 该函数用来获取滚动条的各种状态,包括滚动范围、滚动框的位置和页尺寸。 下面介绍其中各参数的含义。 1)nMask的意义与SCROLLINFO结构中的fMask相同,函数在获得有效值后返回TRUE,否则返回FALSE。 2)lpScrollInfo指向一个SCROLLINFO结构,该结构定义如下: Typedf struct tagSCROLLINFO { UINT cbSize; 结构的尺寸 UINT fMask; //说明结构中的哪些参数是有效的,可以是屏蔽值的组合,如:SIF_POS|SIF_PAGE若为SIF_ALL则整个结构都有效 int nMin; //滚动范围最大值,当fMask中包含SIF_RANGE时有效 int nMax; //滚动范围最小值,当fMask中包含SIF_RANGE时有效 UINT nPage; //页尺寸,用来确定比例滚动框的大小,当fMask中包含SIF_PAGE时有效 int nPos; //滚动框的位置,当fMask中包含SIF_POS有效 int nTrackPos; //滚动时滚动框的位置,当fMask中包含SIF_TRACKPOS时有效,该参数只能 //查询,不能设置,最好不要用该函数来查询拖动时滚动框的位置 }SCROLLINFO; typedef SCROLLINFO FAR *LPSCROLLINFO; (6)SetScrollInfo()函数,格式: BOOL SetScrollInfo(LPSCROLLINFO lpScrollnfo,BOOL bRedraw=TRUE); 该函数用于设置滚动条的各种状态,如设定页尺寸,从而实现比例滚动框。 参数: 1)lpScrollInfo指向一个SCROLLINFO结构; 2)bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘,若操作成功,返回TRUE,否则返回FALSE。 说明:CWnd类也提供了一些函数来查询和设置所属的标准滚滚动条。这些函数与CScrollBar类的函数同名,且功能相同,但每个函数都多了一个参数,用来选择滚动条。 第3章 对话框与控件 (7)GetScrollPos函数,格式:int GetScrollPos(int nBar)const; 参数: nBar用来选择滚动条,可以为下列值—— SB_CTL表示滚动条为控件滚动条; SB_HORZ指定水平滚动条; SB_VERT指定垂直滚动条。 (8)OnHScroll()和OnVScroll()函数:无论是标准滚动条,还是滚动条控件,滚动通知消息都是用WM_HSCROLL和WM_VSCROLL消息发送出去的。对这两个消息的默认处理函数是CWnd::OnHScroll和CWnd::OnVScroll,一般需要在派生类中对这两个函数从新设计,以实现滚动功能。这两个函数的声明格式为: afx_msg void OnHScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar); afx_msg void OnVScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar); 参数: 1)nSBCode是通知消息码,主要通知码如表3.31所示; 2)nPos是滚动框的位置,只有在nSBCode为SB_THUMBPOSITION或SB_THUMBTRACK时,该参数才有意义,如果通知消息是滚动条控件发来的,那么pScrollBar是指向该控件的指针,如果是标准滚动条发来的,则pScrollBar为NULL。 2. WM_HSCROLL和WM _VSCROLL消息 用户对滚动条进行操作时,滚动条就会向父窗口发送WM_HSCROLL或WM_VSCROLL消息,这些消息是通过ClassWizard在滚动条父窗口中进行映射的,并产生相应消息映射函数OnHScroll和OnVScroll。表3.31列出了滚动条控件的通知消息。 表3.31 滚动条控件的通知消息 通 知 消 息 说 明 SB_ENDSCROLL 结束滚动 SB_LEFT、SB_RIGHT 滚动到最左端或最右端 SB_LINELEFT、SB_LINERIGHT 向左或向右滚动一行(或一个单位) SB_PAGELEFT、SB_PAGERIGHT 向左或向右滚动一页 SB_THUMBPOSITION 滚动到某绝对位置 SB_TOP、SB_BOTTOM 滚动到最上端或最下端 SB_LINEUP、SB_LINEDOWN 向上或向下滚动一行(或一个单位) SB_PAGEUP、SB_PAGEDOWN 向上或向下滚动一页 SB_THUMBTRACK 拖动滚动块 3.5.9 滑动条 滑动条(Slider)也称滑块或游标,是包含一个滑动块和可选刻度线的窗口。当用鼠标或方向键拖动滑动块时,该控件会发送通知消息来表明这些改变,如图3.70所示的是水平滑动条和垂直滑动条的样子。 第3章 对话框与控件 滑动条是按照应用程序中指定的增量来移动的。例如:如果指定滑动条的范围为8,则滑动条只能有9个位置,既在滑动条最左边的一个位置和另外8个在此范围内每隔一个增量的位置。通常,这些位置都是由相应的刻度线来标识的。滑动条控件的各种操作封装在了MFC的CSliderCtrl类中。 [例3.23]设置对话框背景颜色 通过映射WM_CTLCOLOR(当子窗口将要绘制时发送的消息,以便能使用指定的颜色来绘制)消息达到改变背景颜色的目的。本例用三个滑动条来调整,即Visual C++所使用的RGB 颜色的三个分量:R(红色分量)、G(绿色分量)、B(蓝色分量),如图3.71所示。 (1)创建一个单文档应用程序,名为:滑动条。 (2)向应用程序中添加一个对话框,资源ID为IDD_COLOR,标题为:调整背景颜色,为此对话框建类,类名为:CBkColorDlg。 (3)删除原来的Cancel按钮,将OK按钮的标题改为“退出”。 (4)参照图3.71的控件布局,用编辑器为对话框添加表3.32所示的控件和成员变量。 (5)在对话框类CBkColorDlg.h文件中,添加一个画刷类成员变量:CBrush m_Brush;用来设置对话框背景所需要的画刷。 图3.70 垂直与水平滑动条 图3.71 “调整背景颜色”对话框 表3.32 添加的控件和关联的成员变量 控件 ID号 变量类别 变量类型 变量名 标题 静态文本 默认 — — — R(红色) 滑动条(红色) IDC_SLIDER_RED Control CSliderCtrl m_sliderRed 滑动条(红色) IDC_SLIDER_RED Value int m_nRed 静态文本 默认 — — — G(绿色) 续表 控件 ID号 变量类别 变量类型 变量名 标题 滑动条(绿色) IDC_SLIDER_GREEN Control CSliderCtrl m_sliderGreen 第3章 对话框与控件 滑动条(绿色) IDC_SLIDER_GREEN Value int m_nGreen 静态文本 默认 — — — B(蓝色) 滑动条(蓝色) IDC_SLIDER_BLUE Control CSliderCtrl m_sliderBlue 滑动条(蓝色) IDC_SLIDER_BLUE Value int m_nBlue (6)用MFC ClassWizard为对话框CBkColorDlg类添加WM_INITDIALOG消息映射,并添加下列代码: BOOL CBkColorDlg::OnInitDialog() { CDialog::OnInitDialog(); m_sliderRed.SetRange(0,255); //红色滑动条的最小和最大位置 m_sliderGreen.SetRange(0,255); //绿色滑动条的最小和最大位置 m_sliderBlue.SetRange(0,255); //蓝色滑动条的最小和最大位置 m_nRed=200; //最初红色滑动条的位置在200处 UpdateData(FALSE); //将成员变量数据传给控件 return TRUE; } (7)用MFC ClassWizard为对话框CBkColorDlg类添加WM_HSCROLL消息映射,并添加下列代码: void CBkColorDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int nID = pScrollBar→GetDlgCtrlID(); //获取对话框中控件的ID号 if(nID == IDC_SLIDER_RED) //如果操作的是红色滑动条 { switch(nSBCode) //按哪一种操作进行处理 { case SB_LINELEFT: m_nRed--; //向左滚动一行(或一个单位),如表3.32所示 break; case SB_LINERIGHT: m_nRed++; //向右滚动一行(或一个单位),如表3.32所示 break; case SB_PAGELEFT: m_nRed -=10; //向左滚动一页,如表3.32所示 break; case SB_PAGERIGHT: m_nRed +=10; //向右滚动一页,如表3.32所示 break; case SB_THUMBTRACK: m_nRed = nPos; //拖动滚动块,如表3.32所示 break; } if(m_nRed<0) m_nRed = 0; //红颜色值若小于零,当零处理 if(m_nRed>255) m_nRed = 255; //红颜色值若大于255,当255处理 m_sliderRed.SetScrollPos(0,m_nRed);//将红色滑块的初始位置定在m_nRed=200处 } 第3章 对话框与控件 Invalidate(); CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (8)用MFC ClassWizard为对话框CBkColorDlg类添加WM_CTLCOLOR(设置对话框的颜色)消息映射,并添加下列代码: HBRUSH CBkColorDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); UpdateData(TRUE); //将控件的数据传给成员变量 COLORREF color = RGB(m_nRed,m_nGreen,m_nBlue);//定义红、绿、蓝三种颜色 m_Brush.Detach(); //使画刷和对象分离 m_Brush.CreateSolidBrush(color);//建立COLOR颜色画刷(红、绿、蓝) pDC→SetBkColor(color); //设置COLOR背景颜色(红、绿、蓝) return (HBRUSH) m_Brush; //返回画刷句柄,以便系统使此画刷绘制对话框 // return hbr; } 说明:在上述代码中,COLORREF是用来表示RGB颜色的一个32位的数据类型,它是Visual C++中一种专门用来定义颜色的数据类型,至于画刷的用法以后还要讨论。 (9)打开菜单资源,在顶层菜单项中创建一个名为:“测试(&C)”的菜单,再在其下面创建一个名为:调整背景颜色(&O)的菜单,ID为ID_TEST_COLOR。 (10)用MFC ClassWizard为CMainFrame类添加菜单项ID_TEST_COLOR的COMMAND消息映射,取默认的映射函数名,并添加下列代码: void CMainFrame::OnTestColor() { CBkColorDlg dlg; dlg.DoModal(); } (11)在文件MainFrm.cpp的前面添加CBkColorDlg类的包含语句: #include "BkColorDlg.h" (12)编译运行,结果如图3.71所示。 1. 滑动条操作函数 (1)范围和位置的设置和获取 SetRange和SetPos函数分别用来设置滑块的范围和位置,其原型如下: void SetRange(int nMin,int nMax,BOOL Bredraw=FALSE); void SetPos(int nPos); 参数: 1)nMin和nMax表示滑块的最大和最小位置; 2)bRedraw是重画标志; 3)nPos表示新的滑块。 还可以使用SetRangeMax和SetRangeMin函数设置滑块的最大和最小位置,这两个函数的原型如下: 第3章 对话框与控件 void SetRangeMax(int nMax,BOOL bRederw=FALSE); void SetRangeMin(int nMin, BOOL bRedraw=FALSE); 参数: 1)nMax和nMin表示滑块的最大和最小位置; 2)bRedraw是重画标志。 与函数SetRange、SetPos、SetRangeMax和SetRangeMin相应的函数是GetRange、GetPos、GetRangeMax和GetRangeMin,这4个函数的原型如下: void GetRange(int &nMin,int &nMax) const; int GetPos() const; int GetRangeMin() const; int GetRangeMax() const; (2)选择范围的设置 SetSelection函数是设置一个滑动块控件中当前选择的开始和结束位置,其原型如下: void SetSelection(int nMin,int nMax); 参数:nMin和nMax分别表示滑块的开始和结束位置。 (3)刻度线尺寸的设置和清除 SetTic函数是设置滑块控件的一个刻度线的位置,函数成功调用后返回非零值,否则返回0,其原型:BOOL SetTic(int nTic);。 参数:nTic表示刻度线的位置。 SetTicFreq设置显示在滑块中刻度线的疏密程度,其原型如下:void SetTicFreq(int nFreq);。 参数:nFreq表示刻度线的疏密程度,例如,如果参数设置为1,则在滑块的范围内每一个增量显示一个刻度线,若使这个函数有效,必须在属性对话框中选中Auto ticks项。 ClearTics函数,用来从滑块控件中删除当前的刻度线,函数原型如下: void ClearTics(BOOL bRedraw = FALSE); 参数:bRedraw表示重画标志,若该参数为TRUE,则在选择被清除后重画滑动条。 2. 滑动条的通知消息 滑动条的通知消息有: TB_BOTTON、TB_ENDTRACK、TB_LINEDOWN、TB_LINEUP、TB_PAGEDOWN、 TB_PAGEUP、TB_THUMBPOSITION、TB_THUMBTRACK、TB_TOP,其各自的含义如表3.31所示。这些消息代码都来自于WM_HSCROLL或WM_VSCROLL消息。 注意:编写程序时不能用TB_,因为没定义,而与滚动条的通知消息一样用SB_就行了。 3. 滑动条的风格 滑动条控件有许多风格,它们都可以通过滑动条控件的属性对话框进行设置,如图3.72所示。表3.33列出了该属性对话框中Styles选项卡的各项含义。 第3章 对话框与控件 图3.72 滑动条属性的Styles选项卡 表3.33 滑动条控件的Styles属性 项 目 说 明 Orientation(方位) 控件放置方向:Vertical(垂直)、Horizontal(水平,默认) Point(点) 刻度线在滑动条控件中放置的位置:Both(两边都有)、Top/Left(水平滑动条的上边或垂直滑动条的左边,同时滑动块的尖头指向右刻度线的那一边)、Bottom/Right(水平滑动条的下边或垂直滑动条的右边,同时滑动块的尖头指向有刻度线的那一边) Tick marks 选中此项,在滑动条控件上显示刻度线 Auto ticks 选中此项,滑动条控件上的每个增量位置处都有刻度线,并且增量大小自动根据其范围来确定 Border(边框) 选中此项,控件周围有边框 Enable selection(允许选择) 选中此项,控件中供用户选择的数值范围高亮显示 图3.73 运行结果 [例3.24]创建一个“绘制矩形”的应用程序 用滑动条选择画线的宽度是2,用编辑框和旋转按钮选择矩形的长和宽是120、100,单击“应用”按钮后,在对话框的右边画一个矩形,如图3.73所示。 (1)创建一个基于对话框的应用程序,名为:绘制矩形。 (2)参照图3.73的控件布局,按表3.34所示,向对话框添加控件和关联的成员变量。 注意:对于滑动条(IDC_LINEWIDTHSLIDER)控件,需要设置Styles属性,选中Tick marks、Auto ticks、Enable selection,在Point下拉列表框中选择Botton/Right。对于旋转按钮Style属性(IDC_SPIN1,IDC_SPIN2),在Alignment下拉列表框中选择Right,并选中Auto buddy,Set buddy integer,Wrap。 (3)在“绘制矩形Dlg.cpp”实现文件中的OnInitDialog函数中添加如下代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … //设置旋转按钮的宽范围10~100 m_WidthSpin.SetRange(10,100); //设置旋转按钮的当前位置0 第3章 对话框与控件 m_WidthSpin.SetPos(0); //设置旋转按钮的长范围10~100 m_LengthSpin.SetRange(10,100); //设置旋转按钮的当前位置0 m_LengthSpin.SetPos(0); //设置滑动块的范围1~10 m_LineWidthSlider.SetRange(1,10); //设置滑动块的当前位置 m_LineWidthSlider.SetPos(0); m_LineWidthSlider.SetTicFreq(1);//设置滑动块刻度标尺,每1个单位1个标记 SetDlgItemText(IDC_LINEWIDTH,"线宽:1");//设置静态文本显示的内容 return TRUE; // return TRUE unless you set the focus to a control } 表3.34 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 静态文本 长 编辑框 IDC_LENGHT int m_Length 旋转按钮 IDC_SPIN1 CSpinButtonCtrl m_LengthSpin 静态文本 宽 编辑框 IDC_WIDTH int m_Width 旋转按钮 IDC_SPIN2 CSpinButtonCtrl m_WidthSpin 静态文本 IDC_LINEWIDTH(将该控件拉长一些) 滑动条 IDC_LINEWIDTHSLIDER int m_LineWidth 滑动条 IDC_LINEWIDTHSLIDER CsliderCtrl m_LineWidthSlider 按钮 IDC_APPLY 应用 CButton m_Room 按钮 EXITBUTTON 退出 (4)为按钮(应用)IDC_APPLY的BN_CLICKED消息添加消息映射函数,并添加代码: void CMyDlg::OnApply() { UpdateData(); //用屏幕上的当前值更新控制变量 Invalidate(); //使用户视图区无效,以清除上一次画的图 UpdateWindow(); //刷新用户视图区域 CClientDC dc(this); CPen mypen;//定义画笔类对象 mypen.CreatePen(PS_SOLID,m_LineWidth,RGB(255,0,0));//创建画笔 CPen *pOldpen=dc.SelectObject(&mypen);//将画笔选入设备环境 dc.Rectangle(70,10,150+m_Length,30+m_Width);//画一个矩形 dc.SelectObject(pOldpen);//恢复原先的画笔 } 第3章 对话框与控件 (5)为按钮IDC_EXITBUTTON的BN_CLICKED消息添加消息映射函数,并添加代码: void CMyDlg::OnExitbutton() { OnOK(); } (6)为对话框CMyDlg类添加WM_HSCROLL水平滚动消息的处理函数OnHScroll,当滑块的位置改变后,静态文本的内容随之改变。 void CMyDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { UpdateData(); // 将控件的数据传给成员变量 CString LineWidth; LineWidth.Format(" 线宽:%d",m_LineWidth); SetDlgItemText(IDC_LINEWIDTH,LineWidth);//设置静态文本的显示内容 CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (7)编译运行,结果如图3.73所示。 3.6 标签控件、图像列表、属性表及属性页 标签(Tab)控件也称为选项卡,是一个分割成多个页面的窗口,各页按顺序排列,每次只能看到一个页(当前页)中的控件,其他页只有标题可见。单击页的标题,可使相应的页可见(置为当前页),按Tab键可顺序显示各页内容。卡片式对话框适用于管理多内容多输入的信息。使用卡片式对话框比使用多个对话框来完成同样的功能编程接口更简洁,如MFC ClassWizard类向导对话框就是一个卡片式对话框。 编程时使用标签控件,用户可以在一个窗口的相同区域定义多个页面,每个页面上包括一些不同的控件,实现不同的功能。需要注意的是,标签作为一个控件使用,由于它不是对话框,因此不能直接在各个标签页上添加控件,只能在选中不同标签时,在相同位置显示含有不同控件的对话框。标签控件实现的是在不同对话框窗口之间的切换,而不只是标签页上控件的现实切换。下面通过一个例子,看看标签控件的应用。 3.6.1 标签控件 [例3.25]创建一个“标签控件”的应用程序 使用无模式对话框来构造标签页面,开始将所有的标签页面创建好,然后根据所有选择的当前标签选项决定哪个页面需要显示,哪个页面需要隐藏和禁用。图3.74是本程序的运行结果。 (1)用MFC AppWizard(exe)创建一个基于对话框的应用程序,名为:标签控件。 (2)参照图3.74(3)所示,添加一个默认的标签控件IDC_TAB1,调整其大小。 (3)为CMyDlg类添加标签控件(IDC_TAB1)所关联的CTabCtrl类型的成员变量m_Tab。 (4)添加3个对话框资源IDD_DIALOG1,IDD_DIALOG2,DIALOG3,分别设置这3个对话框的Styles属性为:Styles Child,Border None,并用ClassWizard依次为这3个对话框建类,类名分别为:CTab1Dlg、CTab2Dlg、CTab3Dlg。 第3章 对话框与控件 (1)基本信息 (2)成绩 (3)备注 图3.74 运行结果的三个页面 (5)将这3个对话框尽量缩小,参照图3.74和图3.75,按表3.35所示,分别向这3个对话框添加控件。 图3.75 设置对话框属性 表3.35 向3个对话框添加的控件 控件 ID号 标题 属性 对话框 静态文本 IDC_STATIC_NAME 姓名 默认 IDD_DIALOG1 编辑框 IDC_EDIT_NAME — 默认 静态文本 IDC_STATIC_SEX 性别 默认 单选按钮 IDC_RADIO_MAN 男 默认 单选按钮 IDC_RADIO_WOMAN 女 默认 静态文本 IDC_STATIC_ID 学号 默认 编辑框 IDC_EDIT_ID — 默认 静态文本 IDC_STATIC_ENG 英语 默认 IDD_DIALOG2 编辑框 IDC_EDIT_ENG — 默认 静态文本 IDC_STATIC_MAT 数学 默认 编辑框 IDC_EDIT_MAT — 默认 静态文本 IDC_STATIC_COM 计算机 默认 第3章 对话框与控件 编辑框 IDC_EDIT_COM — 默认 续表 控件 ID号 标题 属性 对话框 静态文本 IDC_STATIC_REMARK 备注 默认 编辑框 IDC_EDIT_REMARK — Multiline Vertical scroll IDD_DIALOG3 Horizontal scroll (6)打开项目工作区的ClassView选项卡,打开标签控件classes,右击CMyDlg,从弹出的快捷菜单中选择Add Member Variables命令,添加下列成员变量: public: Variable Type: CImageList Variable Name: m_ImageList Variable Type: CTab3Dlg Variable Name: *m_pTab3Dlg Variable Type: CTab2Dlg Variable Name: *m_pTab2Dlg Variable Type: CTab1Dlg Variable Name: *m_pTab1Dlg (7)打开项目工作区的ClassView选项卡,打开标签控件classes,右击CMyDlg,从弹出的快捷菜单中选择Add Member Function命令,添加成员函数:void SetDlgState(CWnd *pWnd,BOOL bShow)和void DoTab(int nTab),并添加代码: void CMyDlg::SetDlgState(CWnd *pWnd, BOOL bShow) { pWnd→EnableWindow(bShow); if(bShow) { pWnd→ShowWindow(SW_SHOW);//显示 pWnd→CenterWindow();//居中显示 } else pWnd→ShowWindow(SW_HIDE);//隐藏 } void CMyDlg::DoTab(int nTab) { if(nTab>2) nTab=2; //确定nTab值本能超过范围 if(nTab<0) nTab=0; BOOL bTab[3]; bTab[0]=bTab[1]=bTab[2]=FALSE; bTab[nTab]=TRUE; //切换对话框的显示和隐藏 SetDlgState(m_pTab1Dlg,bTab[0]); SetDlgState(m_pTab2Dlg,bTab[1]); SetDlgState(m_pTab3Dlg,bTab[2]); } 第3章 对话框与控件 (8)用ClassWizard为IDC_TAB1映射TCN_SELCHANGE消息,并添加代码: void CMyDlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) { int nSelect = m_Tab.GetCurSel();//获得当前选择的标签项 if(nSelect>=0) DoTab(nSelect); *pResult = 0; } (9)选择Insert→Resource命令,从弹出的对话框中选择Icon,单击Import按钮,向程序中添加3个图标文件(.ico),并取默认的图标ID号,即:IDI_ICON1、IDI_ICON2、IDI_ICON3。 (10)在CMyDlg::OnInitDialog中添加下列代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); m_pTab1Dlg=new CTab1Dlg();//为无模式对话框分配空间 m_pTab2Dlg=new CTab2Dlg(); m_pTab3Dlg=new CTab3Dlg(); //创建无模式对话框,指定标签控件为无模式对话框的父窗口 m_pTab1Dlg→Create(IDD_DIALOG1,&m_Tab); m_pTab2Dlg→Create(IDD_DIALOG2,&m_Tab); m_pTab3Dlg→Create(IDD_DIALOG3,&m_Tab); m_ImageList.Create(16,16,ILC_COLOR|ILC_MASK,3,0); //创建图像列表 m_ImageList.Add(AfxGetApp()→LoadIcon(IDI_ICON1)); //从图标加到图像列表中 m_ImageList.Add(AfxGetApp()→LoadIcon(IDI_ICON2)); //从图标加到图像列表中 m_ImageList.Add(AfxGetApp()→LoadIcon(IDI_ICON3)); //从图标加到图像列表中 m_Tab.SetImageList(&m_ImageList); //设置Tab控件所适用的图像列表 m_Tab.InsertItem(0, "基本信息", 0); m_Tab.InsertItem(1, "成绩", 1); m_Tab.InsertItem(2, "备注", 2); m_Tab.SetMinTabWidth(80); //设置标签的最小宽度 m_Tab.SetPadding(CSize(12,3));//设置标签项和图标周围的间隔 图3.76 中间结果 m_Tab.SetCurSel(0); DoTab(0); return TRUE; } 注意:该函数中原代码可全注释掉。 进行到这一步,可运行一下,中间结果如图3.76所示。 (11)用CassWizard为CMyDlg类添加WM_DESTROY的消息映射,当CMyDlg退出时,删除分配给无模式对话框的内存,代码如下: 第3章 对话框与控件 void CMyDlg::OnDestroy() { CDialog::OnDestroy(); if(m_pTab1Dlg) delete m_pTab1Dlg; if(m_pTab2Dlg) delete m_pTab2Dlg; if(m_pTab3Dlg) delete m_pTab3Dlg; } (12)用ClassWizard为CTab1Dlg类添加WM_INITDIALOG消息映射,并添加下列代码: BOOL CTab1Dlg::OnInitDialog() { CDialog::OnInitDialog(); CheckRadioButton(IDC_RADIO_MAN,IDC_RADIO_WOMAN,IDC_RADIO_MAN); return TRUE; } (13)编译运行,结果如图3.76所示。 通过使用标签控件,应用程序可以将一个窗口或对话框的相同区域定义为多个页面。每一页包含了一套信息或一组控件,当用户选择了相应的标签时,应用程序就会显示相应的信息或控件。 1. 标签控件的风格 标签控件有许多风格,这些风格用来定义标签控件的外观及操作,可以在标签控件属性对话框中进行设置,如图3.77所示,表3.36列出了该属性对话框的各项含义。 图3.77 标签控件的Styles选项卡 表3.36 标签控件的Styles属性 项 目 说 明 Alignment(对齐) 标签的调整方式:Right Justify(向右调整)、Fixed Width(宽度相同)、Ragged Right(不拉伸每一行标签来使之适合标签控件的整个宽度),当选择Fixed Width方式后,Force label left和Force icon left两个选项被激活 Focus(焦点) 焦点方式:Default(默认)、On Button Down(当点击一个标签时接受输入焦点)、Never(永远不会接收输入焦点) Buttons(按钮) 选中此复选框,标签看起来像按钮一样 ToolTips(工具提示) 选中此复选框,表示该标签控件具有一个与之关联的工具提示 Border(边框) 选中此复选框,控件周边有边框 MuItiline(使用多行标签) 选中此复选框,可使一个标签控件显示多行标签,因此所有的标签都可以同时显示(默认时,一个标签控件只显示一行标签) 第3章 对话框与控件 Owner draw fixed(所有者固定) 选中此复选框,使自画标签具有相同的高度 Force label left(强制标号左) 选中此复选框,左对齐图标和标签 Force icon left(强制图标左) 选中此复选框,将图标集中在左边,但标签仍然在中间(默认时,标签控件将图标和标签都放在中间,图标在标签的左边) 2. 标签控件的基本操作 MFC的CTabCtrl类封装了标签控件的各种操作,下面介绍其操作函数的原型。 (1)添加标签,CTabCtrl类成员函数InsertItem用来向标签控件中增加标签: BOOL InsertItem(int nItem,TCITEM *pTabCtrlItem); BOOL InsertItem(int nItem,LPCTSTR lpszItem); BOOL InsertItem(int nItem,LPCTSTR lpszItem,int nImage); BOOL InsertItem(UINT nMask,int nItem,LPCTSTR lpszItem,int nImage,LPARAM lParam); 下面介绍其中的参数。 nItem指定新标签的索引。 pTabCtrlItem是指定标签属性的TCITEM结构的指针,TCITEM结构的定义如下: typedef struct tagTCITEM { UINT mask; #if( _WIN32_IE >=Ox0300 DWORD dwState; DWORD dwStateMask; #else UINT lpReserved1; UINT lpReserved2; #endif LPTSTR pszText; int cchTextMax; int iImage; LPARAM lParam; } TCITEM,FAR *LPTCITEM; lpszItem 表示插入标签的指针。 nImage表示插入图像的索引。 nMask指定设置的TCITEM结构属性,它的取值可以是0或者下列值的组合: TCIF_TEXT、TCIF_IMAGE、TCIF_PARAM、TCIF_RTLREADING、TCIF_STATE lParam是与标签关联的应用程序定义的数据。 (2)成员函数SetItemSize、SetPadding及SetMinTabWidth分别用来设置某个项的宽度和高度、图标和标签周围的间隔以及设置标签项的最小宽度。而GetItemRect用来获取标签的边界大小,它们的原型如下: CSize SetItemSize(CSize size); void SetPadding(CSize size); 第3章 对话框与控件 int SetMinTabWidth(int cx); BOOL GetItemRect(int nItem,LPRECT lpRect)const; (3)成员函数DeleteItem表示在一个标签控件中删除某一标签项,而DeleteAlltems则删除所用的项。它们的原型如下: BOOL DeleteItem(int nItem); BOOL DeleteAllItem(); 参数:nItem用来指定一个标签索引(0表示第一个标签)。 (4)在标签控件中使用图像列表时,必须调用CTabCtrl::SetImageList函数来指定一个已创建的图像列表,函数原型如下: CImageList *SetImageLise(CImageList *pImageList); 参数:pImegeList用来表示一个图像列表指针,函数返回以前的图像列表指针,若以前没有图像列表指针,则返回NULL。 (5)成员函数SetCurSel和GetCurSel分别用来设置和获取当前选项的标签项,它们的原型如下: int SetCurSel(int nItem); int GetCurSel()const; 参数:nItem用来表示当前选项的索引,函数GetCurSel返回当前选项的标签的从零开始的索引,如果没有标签被选择,则返回-1。 (6)成员函数DeselectAll用来重新排列一个标签控件中的全部标签项,而函数HighlightItem是使一个标签项处于高亮(选择)状态。它们的原型如下: void DeselectAll(BOOL FExcludeFocus); BOOL HighlightItem(int idItem,BOOL fHighlight = TRUE); 参数: fExcludeFocus用来指定一个重排标志,如果这个参数被设置为FALSE,则所有的标签按钮都将被重新排列,如果被设置为TRUE,则除了当前选择的标签外,其他所有的标签都将被重印排列; idItem用来指定一个标签的索引; fHighlight指定要设置的高亮状态;如果这个值是TRUE,则该标签被选择,如果这个值是FALSE,则该标签被设置为默认状态。 3. 标签控件的通知消息 标签控件的通知消息常见的有:TCN_KEYDOWN,TCN_SELCHANGE和TCN_SELCHANGING,分别表示用户按下某键、当前标签选项已被改变和当前标签选项将要改变。 3.6.2 图像列表控件 图像列表控件常常用来有效地管理多个位图和标签。在MFC中,图像列表控件使用CImageList类来创建、显示或管理图像。 1. 图像列表的创建 图像列表的创建不像其他控件,它不能通过对话框编辑器来创建。因此,创建一个图像列表首先要声明一个CImageList对象,然后调用Create函数。由于Create函数的重载很多,故这里给出最常用的一个原型: 第3章 对话框与控件 BOOL Create(int cx,int cy,UINT nFlags,int nInitial,int nGrow); 参数: cx、cy用来指定图像的像素大小; nFlags表示要创建的图像类型,一般取其ILC_COLOR和ILC_MASK(指定屏蔽图像)的组合,默认的ILC_COLOR为ILC_COLOR4(16色),当然也可以是ILC_COLOR8(256色)、ILC_COLOR16(16位色)等; nInitial用来指定图像列表中最初的图像数目; nGrow表示当图像列表的大小发生改变时图像可以增加的数目。 2. 图像列表的基本操作 常见的图像列表的基本操作有:增加、删除和绘制等,其相关成员函数如下: int Add(CBitmap *pbmImage,CBitmap *pbmMask); int Add(CBitmap *pbmImage,COLORREF crMask); int Add(HICON hIcon); 上述函数用来向一个图像列表添加一个图标或多个位图。成功时返回第一个新图像的索引号,否则返回-1。 参数: pbmImage表示包含图像的位图指针; pbmMask表示包含屏蔽的位图指针; crMask表示屏蔽色; hIcon表示图标句柄。 BOOL Remove(int nImage); 该函数用来从图像列表中删除一个由nImage指定的图像,成功时返回非0,否则返回0。 BOOL Draw(CDC *pdc,int nImage,POINT pt,UINT nStyle); 该函数用来在由pt指定的位置处绘制一个图像。 参数: pdc表示绘制的设备环境指针; nImage表示要绘制的图像的索引号; nStyle用来指定绘制图像时所采用的方式。 HICON ExtractIcon(int nImage); 该函数用来将nImage指定的图像扩展为图标。 COLORREF SetBkColor(COLORREF cr); 该函数用来设置图像列表的背景色,它可以是CLR_NONE。成功时返回先前的背景色,否则为CLR_NONE。 图像列表控件的应用见例3.25第10步中的代码。 3.6.3 属性表及属性页 属性表(Property Sheet)也称为属性对话框,类似于标签对话框。一个属性表由一个CpropertySheet(属性表)类对象和多CpropertyPag(属性页)类对象构成。其中,CPropertySheet类或其派生类对象代表一个属性表,CPropertyPage派生类对象代表每个属性页。每个属性页对应一个对话框,用于进行数据的输入输出。CPropertyPage类派生于CDialog类,因此每个属性页就是一个对话框。属性表编程的主要步骤 第3章 对话框与控件 包括以下几步。 1)为每个属性页创建属性对话框。 2)为属性对话框添加控件。 3)为每个属性对话框创建CPropertyPage类的派生类,并添加成员变量和消息处理函数。 4)声明一个CPropertySheet(属性表)类对象和所有的CPropertyPage(属性页)派生类对象,调用函数AddPage将属性页加入到属性表,显示该属性表。 5)对于模式对话框属性表,调用CPropertySheet::Domodal函数来显示,对于非模式对话框属性表,调用CPropertySheet::Create函数来显示。 在资源编辑器中创建属性页类似于创建对话框资源模板,但它们在以下几个方面属性的设置是与对话框不同的。 ① Caption(标题)属性的值出现在选项卡上,而不是出现在对话框的标题上。 ② 属性页的Style属性必须被设置为Child(子类)。 ③ 页的Border(边框)属性必须被设置为Thin(细薄,无边框)。 ④ 属性页的Disable()复选框必须被选中。 说明:一般不必记住这些不同的风格,因为在如图3.78对话框中有3个不同的选项用于创建属性页,它们唯一的差别是属性单的初始大小不同。 [例3.26]创建属性表 (1)创建一个SDI(单文档)的应用程序,名为:创建属性表。 (2)添加菜单资源: 打开项目工作区的ResourceView选项卡,打开Menu,双击IDR_MAINFRAME,在“查看”下面添加菜单,名为:显示属性表(&X),其ID为ID_SHEET。 (3)添加属性页对话框和属性页对话框类,并生成相应的消息相应函数: 1)选择Insert→Resource,打开Dialog,选择IDD_PROPPAGE_MEDIUM[English](一个中等大小的属性页,用于属性对话框),如图3.78所示,单击New按钮,右击该对话框的标识符,在弹出的快捷菜单中选择Properties命令,将其ID改为: IDD_PROGRESSDLG1,Language属性改成:Chinese(P.RC),将Caption改成:步进页。将控件工具栏上的进展条(ID改为IDC_PROGRESS)和按钮“步进”(ID改为IDC_STEP)添加到该对话框上,如图3.79所示。 第3章 对话框与控件 图3.78 添加资源对话框 图3.79 控件布局 2)利用ClassWizard为IDD_PROGRESSDLG1对话框建立对应的类:Cpage1,基类为:CpropertyPage,同时增加与IDC_PROGRESS进展条对应的成员变量m_progress,类型为:CprogressCtrl。 3)利用ClassWizard为Cpage1加入WM_INITDIALOG映射消息,并添加代码: BOOL Cpage1::OnInitDialog() { CPropertyPage::OnInitDialog(); m_progress.SetRange(1,100); m_progress.SetStep(15); m_progress.SetPos(30); return TRUE; } 4)为Cpage1类添加单击“步进”按钮(ID_STEP)的消息映射BN_CLICKED,并添加代码: void CPage1::OnStep() { m_progress.StepIt( ); } 说明: ① void SetRange(int nLower,int nUpper);该函数用来设置进度条的范围。 参数:nLow和nUpper分别指定了最小值和最大值,默认时进度条的范围是0~100。 ② int SetPos(int nPos);用来设置进度条的当前进度函数返回的是进度条的前一个进度。 ③ int StepIt( );使进度增加一个步长,步长值是由SetStep函数设置的,默认的步长值是10,函数返回进度条的前一个进度。 ④ int SetStep(int nStep);用来设置步长值,函数返回原来的步长值。见第3.5.7节的进展条的基本操作。 5)用创建Cpage1属性页同样的方法创建Cpage2属性页。选择Insert→Resource命令,找开Dialog,选择IDD_PROPPAGE_MEDIUM[English],单击New按钮,将其ID改为:IDD_PROGRESSDLG2,并将其Language改成:Chinese(P.RC),将Caption改成:颜色选择页,将控件工具栏上的Group Box(组框)拖到该对话框上,Caption(标题)写:颜色选择,将Radio Botton(单选按钮)拖到对话框的组框里,设置属性:Caption:Red(红),ID为:IDC_RED,属性选中Group。接着再将两个单选按钮拖到对话框的组框里,Caption分别写:Green(绿)ID为IDC_GREEN、Blue(蓝)ID为IDC_BLUE,如图3.80所示。再为该对话框建类,类名为:Cpage2,基类为CpropertyPage。 图3.80 添加的对话框模板 6)在Cpage2类中为IDC_RED建立成员变量,名为:m_red,类型为:int。 (4)创建一个属性表类来显示已创建的属性页。 1)打开ClassWizard选项卡,并单击Add Class按钮 第3章 对话框与控件 ,一个小菜单将出现在按钮下方,选择New命令,弹出New Class对话框。在Name框中输入类名为:CSheet,基类设置:CPropertySheet,再单击OK按钮,属性表类(CSheet)就建立了。 2)打开CSheet类的头文件(CSheet.h),在其前面添加: #include "Page1.h" #include "Page2.h" 3)通过Class View标签在CSheet类中添加成员变量: Cpage1 m_page1; Cpage2 m_page2; 4)展开CSheet类,在它的两个构造函数中分别加入以下代码: AddPage(&m_page1);//增加第一页到属性单中 AddPage(&m_page2); //增加第一页到属性单中 (5)在视图类(CMyView)中加入前面第(2)步设置好的菜单项(ID_SHEET)的command消息映射函数OnSheet(),并添加下列代码: void CMyView::OnSheet() { CSheet sheet("PropertySheet",this ,0); sheet.m_page2.m_red=1; (1)选择步进页 (2)选择颜色选择页 图3.81 运行结果 sheet.DoModal(); } (6)在CMyView.cpp前面加入: #include "Sheet.h" (7)编译运行,结果如图3.81所示。 选择“查看”→“显示属性表”命令,便出现图3.81(1)“步进页”,再单击“颜色选择页”标签,便出现图3.81(2)“颜色选择页”,再单击“步进页”标签,又回到步进页,可循环显示。 3.7 向导对话框 向导对话框实际上是一种特殊的属性表,和属性表不同的是,它能使用按钮来完成从一页到另一页的移动,而不是使用选项卡。用MFC AppWizard(exe)来建立新的程序时,就是用向导对话框完成的整个过程,图3.82就是这个过程的第一步,单击Next按钮到下一步。 由于向导是一种特殊的属性表,因此与其对应的MFC类也是属性表类CpropertySheet和属性页类CpropertyPage,在向导程序中,属性表的创建方法没有改变,只是在调用DoModel函数之前应调用SetWizardMode(设置向导模式)函数,将属性表的操作方式设置为向导模式。 第3章 对话框与控件 在向导模式下,每一个属性页都有Back、Next、Cancel按钮,由于每次显示向导的特定页时,MFC自动调用OnSetActive()(设置活动页函数)函数,因此可以重载OnSetActive函数,在此函数中利用SetWizardButton(设置属性页按钮显示方式)设置希望出现的按钮,在 图3.82 建立新项目过程的第一步 向导程序的最后一页,可以通过用SetFinishText(设置Finish命令按钮中的文本)设置Finish按钮所显示的文字,同时隐藏“Back(上一步)”按钮和“Next(下一步)”按钮。 [例3.27]创建向导对话框 (1)创建一个基于对话框的应用程序,名为:向导对话框。 (2)创建属性页对话框和属性页对话框类并添加相应的消息映射函数。 图3.83 第一个对话框控件布局 1)在Insert Resource对话框中打开Dialog,选择IDD_PROPPAGE_MEDIUM,单击New按钮,创建了第一个对话框,将其ID改为:IDD_STEP1,将其Language属性设置为Chinese[P.R.C]。并为该对话框建类,类名为:Cpage1,基类为:CpropertyPage。 2)删除对话框上的原有控件,在控件工具栏中拖到对话框一个静态文本,静态文本的属性Caption处写:第一个对话框,再拖过一个静态图片控件,将一个.bmp图形Import(导入)到程序中,再把这个图形加到这个图片控件上,如图3.83所示。 利用ClassWizard为类Cpage1增加消息映射函数OnSetActive,在此函数中激活Next按钮,代码如下: BOOL Cpage1::OnSetActive() {CPropertySheet *pSheet=(CPropertySheet *)GetParent(); 第3章 对话框与控件 ASSERT_KINDOF(CPropertySheet,pSheet); pSheet→SetWizardButtons(PSWIZB_NEXT); return CPropertyPage::OnSetActive(); } 说明: ① GetParent()函数是获得一个指定子窗口的父窗口句柄; ② ASSERT_KINDOF(类名,指向CObject派生类对象的指针)这个宏用来验证指向CObject派生类对象的指针是否从某个特殊类中派生,在调用它之前先调用ASSERT_VALID宏。只有在很特殊的场合下才用得到,如检测编译器可能错过的对象类型问题。 此外,还有两个没有正式文件的ASSERT宏的变种:ASSERT_POINTER(指针,指针类型)和ASSERT_NULL_OR_POINTER(指针,指针类型)。 3)用创建第一个对话框的方法,创建第二、第三个对话框,ID分别为:IDD_STEP2、IDD_STEP3。分别将Language属性设置为Chinese[P.R.C]。并建类,类名分别为:Cpage2、Cpage3。分别添加一个按钮控件和一个静态图片控件,各自向项目中导入(Import)一个.bmp图形,并分别把这个图形加到静态图片控件上。 4)利用ClassWizard为类Cpage2、Cpage3各自增加消息映射函数OnSetActive,在这两个函数中激活Next和Back及Finish按钮,代码如下: BOOL CStep2::OnSetActive() { CPropertySheet *pSheet = (CPropertySheet *)GetParent(); //构造属性单的指针 ASSERT_KINDOF(CPropertySheet,pSheet);//测试获取的指针 pSheet→SetWizardButtons(PSWIZB_NEXT| PSWIZB_BACK); return CPropertyPage::OnSetActive(); } BOOL CStep3::OnSetActive() { CPropertySheet *pSheet = (CPropertySheet *)GetParent(); //构造属性单的指针 ASSERT_KINDOF(CPropertySheet,pSheet);//测试获取的指针 pSheet→SetWizardButtons(PSWIZB_FINISH| PSWIZB_BACK); return CPropertyPage::OnSetActive(); } (3)创建属性表的步骤有以下几步。 1)选择View→ClassWizard,单击Add Class按钮,选择New命令,类名写:CWizardSheet,基类选:CPropertySheet。 2)打开CWizardSheet.h文件,在该文件的前面加下列包含代码: #include "page1.h" #include "page2.h" #include "page3.h" 3)通过Class View标签在CWizardSheet类中添加成员变量: Cpage1 m_page1; Cpage2 m_page2; 第3章 对话框与控件 Cpage3 m_page3; 4)在WizardSheet.cpp的两个构造函数中,都加入下列代码: AddPage(&m_page1);//增加一页到属性单中,引用&m_step1是增加到属性单中的页 AddPage(&m_page2); //增加一页到属性单中,引用&m_step2是增加到属性单中的页 AddPage(&m_page3); //增加一页到属性单中,引用&m_step3是增加到属性单中的页 5)修改应用程序类:向导对话框.cpp的成员函数InitInstance(),删除该函数中以下代码: BOOL CMyApp::InitInstance() { AfxEnableControlContainer(); … … … //删除以下代码 CMyDlg dlg; //定义本项目的对象 m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) // 在删除的位置处添加以下代码 CWizardSheet dlg("wizard sheet"); //自己加的属性单类(也称一组标签对话框)的对象 // "wizard sheet"是属性单中的标题字符串, 可以为空,但必须要有" " m_pMainWnd=&dlg; dlg.SetWizardMode();//显示模式属性单 int nResponse=dlg. DoModal(); if(nResponse==ID_WIZFINISH)//如果单击了结束按钮的话,就作结束处理 … … … } 6)在“向导对话框.cpp”文件的前面,将属性表类包含进来:#include "WizardSheet.h"。 7)编译运行,结果如图3.84所示。 图3.84 程序运行后的三个页面 3.8 设置对话框和控件的背景颜色以及在控件上绘图 第3章 对话框与控件 每个控件在显示之前都会立刻向其父对话框发送WM_CTLCOLOR(改变背景颜色)消息,对话框本身也会发送该消息。如果在派生对话框类中对WM_CTLCOLOR消息进行了映射,就可以设定文本的前景色和背景色,同时还可为控件或对话框的非文本区域选择一个刷子。下面是OnCtrlColor函数的一个例子,它将所有编辑控件的背景颜色都设为粉色。 [例3.28]设置编辑框背景颜色为粉色 (1)创建一个基于对话框应用程序,名为:控件背景颜色。 (2)往对话框上拖入4个编辑控件,再向本对话框类CMyDlg类添加WM_CTLCOLOR消息映射函数,并添加代码: HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if(nCtlColor==CTLCOLOR_EDIT) { pDC→SetBkColor(RGB(255,0,255));//红、蓝混合为粉色 return m_hYellowBrush; } return CDialog::OnCtlColor(pDC,pWnd,nCtlColor); } (3)在对话框类CMyDlg.h文件中加成员变量:HBRUSH m_hYellowBrush;。 (4)在对话框类CMyDlg.cpp的构造函数中,对m_hYellowBrush初始化: 图3.85 编辑控件背景颜色 CBrush m_hYellowBrush(RGB(0,0,0)); (5)编译运行,结果如图3.85所示。 说明:OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)是设置对话框或控件背景。 参数: pWnd指定了特定的控件; nCtlColor指明了控件类型。 [例3.29]设置对话框背景色 将对话框背景色设置为红色,将对话框上的控件设置为绿色。 (1)创建一个基于对话框的应用程序,名为:设置背景颜色。 (2)向对话框上拖入静态文本、复选框、编辑框和单选按钮控件各一个。 (3)将WM_CTLCOLOR消息映射到CMyDlg 类中,并添加代码: HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); COLORREF backColor = RGB(255,0,0); //将COLORREF类变量backColor设为红色 //pDC→SetBkMode(TRANSPARENT); //设置控件背景透明 // 判断是不是要设置色的控件ID if(pWnd→GetDlgCtrlID()==IDC_STATIC||pWnd→GetDlgCtrlID()==IDC_CHECK1|| 第3章 对话框与控件 pWnd→GetDlgCtrlID()==IDC_EDIT1||pWnd→GetDlgCtrlID() ==IDC_RADIO1) { pDC→SetBkColor(RGB(0, 255, 0)); } //创建背景刷子 return CreateSolidBrush(backColor); //创建一个具有指定颜色的逻辑刷子,将对话框设为 //该色 //return hbr; } 图3.86 设置对话框及控件背景色 (4)编译运行,结果如图3.86所示。 如果在某个控件窗口内绘图,则应避免重复绘制对话框的元素,要利用ClassWizard往对话框类中加入OnPaint成员函数。该函数必须将静态控件的ID转化成CWnd指针,然后再得到它的设备环境。这里面的一个技巧就在于既要在控件窗口内绘图,又要防止Windows对它进行重复绘制。可以通过Invalidate/UpdateWindow这一调用序列来达到这一效果。下面给出了一个OnPaint函数,它可以在一个静态控件中绘制一个红色小方块。 [例3.30]在静态文本控件上绘制一个红色方块 (1)创建一个基于对话框的应用程序,名为:在控件中绘图。 (2)往对话框上拖入一个静态文本,稍放大一些,设置属性:在Extended Styles选项卡中选中Static edge,使控件窗口下凹。 (3)在CMyDlg.cpp文件中的OnPaint函数最后面添加下列代码: void CMyDlg::OnPaint() { … … … CWnd *pWnd=GetDlgItem(IDC_STATIC); CDC *pControlDC=pWnd→GetDC(); pWnd→Invalidate(); pWnd→UpdateWindow();//使窗口立即重绘 CBrush grid(RGB(255,0,0)); 图3.87 在静态文本上绘制红色方块 pControlDC→SelectObject(grid); pControlDC→Rectangle(20,0,100,35); pWnd→ReleaseDC(pControlDC); } (4)编译运行,结果如图3.87所示。 第3章 对话框与控件 说明: ① GetDlgItem(IDC_STATIC):获取对话框中子窗口控件(这里是静态文本)的句柄; ② GetDC(HWND hWnd):该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄,以后可以在GDI函数中使用该句柄在设备上下文环境中绘图。 参数: hWnd:设备上下文环境被检索的窗口的句柄,如果该值为NULL,GetDC则检索整个屏幕的设备上下文环境。 ③ Invalidate():是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其他窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。它和 UpdateWindow()的区别在于:UpdateWindow()的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其他消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。 ④ SelectObject(grid):选择一对象到指定设备上下文环境中,该新对象替换先前相同类型的对象。 ⑤ Rectangle(20,0,100,35):画一个矩形,用当前的画笔画矩形轮廓,用当前画刷进行填充。 函数原型:BOOL Rectangle(HDC hdc,int nLeftRect,int nTopRect,int nRightRect,int nBottomRect); 参数: hdc是设备环境句柄; nLeftRect用来指定矩形左上角的逻辑X坐标; nTopRect用来指定矩形左上角的逻辑Y坐标; nRightRect用来指定矩形右下角的逻辑X坐标; nBottomRect用来指定矩形右下角的逻辑Y坐标。 返回值:如果函数调用成功,返回值非零,否则返回值为0。 ⑥ ReleaseDC(pControlDC):释放设备上下文环境(DC)供其他应用程序使用。函数的效果与设备上下文环境类型有关。它只释放公用的和设备上下文环境,对于类或私有的则无效。 像所有的窗口一样,如果对话框中的任何部分变为无效的话,对话框的OnPaint函数都会被自动调用。可以通过Invalidate()函数,在对话框的其他成员函数中强行调用OnPaint函数。 3.9 通用对话框和消息对话框 通过对话框编辑器可以设计出界面美好的对话框模板,而使用ClassWizard可以让用户的对话框类具有高效、紧凑的程序代码。除了这些优点外,在Visual C++6.0中还提供了一些通用对话框以及消息对话框,更加方便用户的程序开发。 第3章 对话框与控件 3.9.1 通用对话框 在Windows系统中提供了一些通用对话框,如页面设置对话框、查找对话框等。这些通用对话框极大地减轻了程序设计的复杂度。同时,MFC还用类封装了这些通用对话框。一般来讲,使用这些通用对话框不需要派生新的类,因为MFC提供的基类已经具有了常用的功能。而且在对话框结束后,可以通过成员函数得到用户在对话框中的选择。所有通用对话框对话框类都是从一个公共的基类CCommonDialog派生而来的,表3.37列出了MFC的通用对话框。 这些对话框都有一个共同特点:它们都从用户获取信息,但并不对信息进行处理。例如文件对话框可以帮助用户选择一个用于打开的文件,但它们实际上只是给程序提供了一个文件路径名,用户的程序必须调用相应的成员函数才能打开文件;类似地,字体对话框只是填充一个描述字体的逻辑结构,但它并不创建字体。 表3.37 MFC的通用对话框 对 话 框 用 途 CColorDialog 颜色对话框,允许用户选择或创建颜色 CFileDialog 文件对话框,允许用户打开或保存一个文件 CFindReplaceDialog 查找替换对话框,允许用户查找或替换指定字符串 CPageSetupDialog 页面设置对话框,允许用户设置页面参数 CFontDialog 字体对话框,允许用户从列出的可用字体中选择一种字体 CPrintDialog 打印对话框,允许用户设置打印机的参数及打印文档 图3.88 选择“打开文件”弹出的对话框 可以直接在程序中使用这些通用对话框,例如下面例题运行后将弹出图3.88所示的“打开”对话框。选定一个文件后,单击“打开”按钮,就会弹出一个消息对话框,显示该文件的全路径名称。 [例3.31]显示文件的全路径名称 (1)创建一个单文档的应用程序,名为:通用对话框。 (2)添加一个菜单资源:ID为ID_FILE_OPEN,名为:打开文件。 (3)将ID_FILE_OPEN消息映射添加到主框架类CMainFrm中,并添加代码: void CMainFrame::OnFileOpen() {CString filter; filter="文本文件(*.txt)|*.txt|C++文件(*.h,*.cpp)|*.h;*.cpp||"; CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,filter); if(dlg.DoModal()==IDOK) 第3章 对话框与控件 { CString str=dlg.GetPathName(); AfxMessageBox(str); } } (4)编译运行,选择“打开文件”命令,弹出图3.88所示的对话框,在该对话框中选中ReadMe,便将ReadMe添在文件名处,单击“打开”按钮,弹出图3.89所示的对话框。 对上述程序作以下说明。 1)通用文件对话框类CFileDialog的函数原型如下: CFileDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt=NULL, LPCTSTR lpszFileName=NULL, DWORD dwFlags=OFN_HIDEREADONLY| OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter=NULL,CWnd *pParentWnd=NULL); 图3.89 显示ReadMe文件的全路径名 参数: ① bOpenFileDialog:为真时,表示文件打开对话框,为FALSE时表示文件保存对话框。 ② lpszDefExt用于指定文件扩展名。若用户在文件名编辑框中没有输入扩展名,则系统在文件名后自动添加lpszDefExt指定的扩展名。 ③ lpszFileName用于在文件名编辑框中指定开始出现的文件名,若为NULL时,则不出现。 ④ dwFlags 用于指定对话框的界面标志,当为OFN_HIDEREADONLY时,表示隐藏对话框中的“只读”复选框,当为OFN_OVERWRITEPROMPT时,表示文件保存,若有指定的文件重名,则弹出提示对话框。 ⑤ pParentWnd用于指定对话框的父窗口指针。 ⑥ lpszFilter用于确定出现在文件列表框中的文件类型,它由一对或多对字符串组成,每对字符串中第1个字符串,表示过滤器名称,第2个字符串表示文件扩展名,若指定多个扩展名则有“:”分隔,字符串最后用两个“|”结尾。 注意:字符串应写在一行,若一行写不下则用“\”连接。 2)AfxMessageBox用于弹出一个消息对话框。 3)GetPathName是CFileDialog类成员函数,用于获取文件的全路径名。 4)CFileDialog中类似的成员函数还有很多,例如:CString GetFileName()const;该函数返回在对话框确定的文件名;如确定的文件是“C:\FILES\TEXT.DAT”则返回“TEXT.DAT”。CString GetFileExt()const;该函数返回在对话框确定的文件扩展名;如确定的文件是“DATA.TXT”,则返回“TXT”。 注意:只有当调用对话框类的成员函数DoModal并返回IDOK后,该对话框类的这些属性成员函数才会有效。 3.9.2 消息对话框 消息对话框是最简单的一类对话框,它只是用于显示信息的。使用时,直接在程序中调用它们即可,它们的函数原型如下: 第3章 对话框与控件 int AfxMessageBox(LPCTSTR lpszText,uint nType=MB_OK,UINT nIDHelp=0); int MessageBox(LPCTSTR lpszText,LPCTSTR lpszCaption=NULL,UINT nType=MB_OK); 这两个函数都是用于创建和显示消息对话框的,它们也有所不同。AfxMessageBox是全程函数,可以用在任何地方,MessagBox只能在控件、对话框、窗口等一些窗口类中使用。 参数: lpszText表示在消息对话框中显示的字符串文本; lpszCaption表示消息对话框的标题,为NULL时,使用默认标题; nIDHelp表示消息的上、下文帮助ID标识符; nType表示消息对话框的图标类型以及所包含的按钮类型,这些类型是用MFC预先定义的一些标识符来指定的,如表3.38和表3.39所示。 表3.38 消息对话框常用图标类型 图 标 类 型 含 义 MB_ICONHAND、MB_ICONSTOP、MB_ICONERROR 用来表示 MB_ICONQUESTION 用来表示 MB_ICONEXCLAMATION、MB_ICONWARNING 用来表示 MB_ICONASTERISK、MB_ICONINFORMATION 用来表示 表3.39 消息对话框常用按钮类型 按钮类型 含 义 MB_ABOUTRETRYIGNORE 表示含有“关于”、“重试”、“忽略”按钮 MB_OK 表示含有“确定”按钮 MB_OKCANCEL 表示含有“确定”、“取消”按钮 MB_RETRYCACEL 表示含有“重试”、“取消”按钮 MB_YESNO 表示含有“是”、“否”按钮 MB_YESNOCANCEL 表示含有“是”、“否”、“取消”按钮 在例3.31的OnFileOpen()函数中,再添加如下阴影部分的代码: void CMainFrame::OnFileOpen() { CString filter; filter="文本文件(*.txt)|*.txt|C++文件(*.h,*.cpp)|*.h;*.cpp||"; CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,filter); if(dlg.DoModal()==IDOK) { CString str=dlg.GetPathName(); AfxMessageBox(str); } int nChoice=MessageBox("学好Visual C++,做一名合格的程序员", "努力学习",MB_OKCANCEL|MB_ICONASTERISK); 第3章 对话框与控件 if(nChoice==IDOK) //if(nChoice==IDYES) { } } 编译运行,按上面的操作法,弹出“通用对话框”,接着单击“确定”按钮后,又弹出一对话框,对话框上有了“确定”和“取消”按钮,并且显示“ i ”图标,如图3.90所示。 图3.90 运行结果:! 确定 取消 图3.91 运行结果:? 是 否 取消 如果再将如下阴影部分代码取替上面的阴影部分代码,则会出现图3.91所示的情况。 void CMainFrame::OnFileOpen() { // TODO: Add your command handler code here CString filter; filter="文本文件(*.txt)|*.txt|C++文件(*.h,*.cpp)|*.h;*.cpp||"; CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,filter); if(dlg.DoModal()==IDOK) { CString str=dlg.GetPathName(); AfxMessageBox(str); } int nChoice=MessageBox("学好Visual C++,做一名合格的程序员", "努力学习",MB_YESNOCANCEL|MB_ICONQUESTION); if(nChoice==IDOK) //if(nChoice==IDYES) { } } 编译运行,按上面的方法操作后,便看到对话框中出现了“是”、“否”、“取消”3个按钮,图标又变成了“ ?”。 3.10 章后实训 实训1 计算器应用程序 1. 建一个基于对话框的应用程序 这个应用程序名为:计算器,将对话框上的OK和Cancel删除掉。 第3章 对话框与控件 2. 可视化设计 界面如图3.92所示。 对应的各控件及其属性如表3.40所示。 图3.92 计算器 表3.40 对话框所加控件及其属性 控件 ID属性 Caption标题 Edit Box IDC_DISPLAY Button IDC_BUTTON7 7 Button IDC_BUTTON8 8 Button IDC_BUTTON9 9 Button IDC_BUTTON_ADD + Button IDC_BUTTON_CLEAR C Button IDC_BUTTON4 4 Button IDC_BUTTON5 5 Button IDC_BUTTON6 6 Button IDC_BUTTON_MINUS - Button IDC_BUTTON_SQRT sqrt Button IDC_BUTTON1 1 Button IDC_BUTTON2 2 Button IDC_BUTTON3 3 Button IDC_BUTTON_MUTIPLY * Button IDC_BUTTON_RECIPROCAL 1/x Button IDC_BUTTON0 0 Button IDC_BUTTON_SIGN +/- Button IDC_BUTTON_POINT . Button IDC_BUTTON_DIV / Button IDC_BUTTON_EQUAL = 3. 在CMyDlg类为编辑框IDC_DISPLAY加变量m_display,类型为:String 为了能够在程序运行过程中,将输入的数据和计算的结果在编辑框上显示,必须为它引入一个变量,从而能够使编辑框以变量的形式出现在程序中,利用函数: 第3章 对话框与控件 UpdateData(true)或UpdateData(false)就可以达到目的。 4. 为CMyDlg类添加数据成员 (1)为了实现计算功能,需要向CMyDlg类(计算器Dlg.h)的public下添加变量: double m_first; //存储一次运算的第一个操作数以及一次运算的结果 double m_second; //存储一次运算的第二个操作数 double m_coff; //存储小数点的系数权值 CString m_operator; //存储运算操作数 注意:如上4个变量的添加方法,也可用右击CmyDlg类的方法添加。 (2)在构造函数中初始化成员变量: m_first=0.0; m_second=0.0; m_operator=_T("+"); m_coff=1.0; 5. 在CMyDlg类中,为Button按钮的BN_CLICKED添加响应函数,如表3.41所示 表3.41 Button按钮的BN_CLICKED响应函数 Object IDs Messages Member Functions IDC_BUTTON0 BN_CLICKED OnOnButton0() IDC_BUTTON1 BN_CLICKED OnOnButton1() IDC_BUTTON2 BN_CLICKED OnOnButton2() IDC_BUTTON3 BN_CLICKED OnOnButton3() IDC_BUTTON4 BN_CLICKED OnOnButton4() IDC_BUTTON5 BN_CLICKED OnOnButton5() IDC_BUTTON6 BN_CLICKED OnOnButton6() IDC_BUTTON7 BN_CLICKED OnOnButton7() IDC_BUTTON8 BN_CLICKED OnOnButton8() IDC_BUTTON9 BN_CLICKED OnOnButton9() IDC_BUTTON_ADD(+) BN_CLICKED OnButtonAdd() IDC_BUTTON_CLEAR(C) BN_CLICKED OnButtonClear() IDC_BUTTON_DIV(/) BN_CLICKED OnButtonDiv() IDC_BUTTON_EQUAL(=) BN_CLICKED OnButtonEqual() IDC_BUTTON_MINUS(-) BN_CLICKED OnButtonMinus() IDC_BUTTON_MUTIPLY(*) BN_CLICKED OnButtonMutiply() IDC_BUTTON_POINT(.) BN_CLICKED OnButtonPoint() IDC_BUTTON_RECIPROCAL(1/x) BN_CLICKED OnButtonReciprocal() IDC_BUTTON_SIGN(+/-) BN_CLICKED OnButtonSign() IDC_BUTTON_SQRT(sqrt) BN_CLICKED OnButtonSqrt() 第3章 对话框与控件 6. 编写程序代码 单击对话框中某个Button按钮(比如“8”)时,导致Windows产生BN_CLICKED消息。而要完成某种功能,就要由消息响应函数中的代码来实现。因此,要编写程序代码,具体步骤有以下几步。 (1)为数字“N”按钮的消息响应函数添加代码。 算法设计如下: 1)作为整数输入数字N时(数字N=0、1、2、…、9) m_second = m_second*10+N; 2)作为小数输入数字N时(N=0、1、2、…、9) m_second = m_second + N*m_coff; m_coff *=0.1; 所以,在数字“8”按钮的消息响应函数中添加如下代码: void CMyDlg::OnButton8() { if(m_coff==1.0) //作为整数输入 m_second=m_second*10+8; else //作为小数输入 { m_second = m_second+8*m_coff; m_coff *= 0.1; } UpdateDisplay(m_second); //更新编辑框的数据显示 } 其他数字(0~9)按钮的消息响应函数添加代码,与其完全类似,请逐个加入类似代码。只是将其中的语句改成:m_second*10+ 0---9和0---9*m_coff 即可,如数字“0”,则: if(m_coff==1.0) //作为整数输入 m_second=m_second*10+0; ….. m_second = m_second+0*m_coff; (2)为运算符按钮的消息响应函数添加代码。 当单击四则运算符(+、-、*、/)时,就是要将一次数据与当前数据进行运算,作为下次的第一操作数,并在编辑框中显示。由以下算法描述。 1)m_first与m_second做m_operator运算后→m_first。 2)0→m_second。 3)m_first→m_display(显示)。 4)用当前单击的运算符→m_operator。 5)将小数的权值变量m_coff置1.0复位。 单击“+”运算符,在消息响应函数中编写如下代码: void CMyDlg::OnButtonAdd() { Calculate(); m_operator="+"; 第3章 对话框与控件 } 其他,“ - ” ,“ * ” ,“ / ” 完全类似。 (3)为等号 “ = ”按钮消息响应函数添加代码。 当点击等号 “ = ” 按钮时,算法设计如下: 1)m_first与m_second作m_operator运算后→m_first。 2)m_first→m_display(显示)。 3)0→m_second。 4)0→first。 5)将小数的权值m_coff置1.0。 6)输入的运算符→m_operator。 void CMyDlg::OnButtonEqual() { Calculate(); m_first=0.0; m_operator="+"; } (4)在OnButtonSqrt()函数中,编写如下代码: void CMyDlg::OnButtonSqrt() { m_second=sqrt(m_second); UpdateDisplay(m_second); } (5)为“+/-”,“.”,“C”和“1/x”按钮的消息响应函数编写代码: // " C " void CMyDlg::OnButtonClear() { m_first=0.0; m_second=0.0; m_operator="+"; m_coff=1.0; UpdateDisplay(0.0); } // " 1/x " void CMyDlg::OnButtonReciprocal() { if(fabs(m_second)<=0.000001) { m_display="除数不能为零"; UpdateData(false); return; } m_second=1.0/m_second; UpdateDisplay(m_second); } 第3章 对话框与控件 // " . " void CMyDlg::OnButtonPoint() { m_coff=0.1; } // " +/- " void CMyDlg::OnButtonSign() { m_second=-m_second; UpdateDisplay(m_second); } (6)还要添加四则运算函数和显示函数。 在工作区窗口,打开ClassView选项卡,右击CMyDlg类,在弹出的快捷菜单中选择Add Member Function命令,在类型处写:void,函数声明处写:Calculate(void)。之后重复上面步骤再加入一个函数,类型处写:void,函数声明处写:UpdateDisplay(double lVal)。这两个函数所加的代码如下: void CMyDlg::Calculate() { switch(m_operator.GetAt(0)) { case '+': m_first +=m_second; break; case '-': m_first -=m_second; break; case '*': m_first *=m_second; break; case '/': if(fabs(m_second)<=0.000001) { m_display="除数不能为零"; UpdateData(false); return; } m_first /=m_second; break; } m_second=0.0; m_coff=1.0; UpdateDisplay(m_first);//更新编辑框的显示内容 } void CMyDlg::UpdateDisplay(double lVal) { m_display.Format(_T("%f"),lVal); int i=m_display.GetLength();//格式化输出 while(m_display.GetAt(i-1)=='0') //将输出结果后的零全部截去 { m_display.Delete(i-1,1); i--; } UpdateData(false); } (7)在CMyDlg.cpp文件的头部加:#include “math.h”。 第3章 对话框与控件 (8)编译运行正确后,便可随意进行四则运算等,如图3.92所示。 对上述程序作以下说明。 (1)关于UpdateData()函数 UpdateData()函数是CDialog的基类CWnd的成员函数,其函数原型如下: BOOL UpdateData(BOOL bSaveAndValidate = TRUE); 一般在对话框的派生类中,利用UpdateData()函数进行控件和相应变量之间的数据传递,形式如下: UpdateData(TRUE); //将控件中的数据传递给相应的变量 UpdateData(FALSE); //将变量中的数据传递给相应的控件 例如:在UpdateDisplay()函数中,首先将函数参数lVal通过转化和截去零操作,赋值给与编辑框控件相关联的变量m_display,最后调用UpdateData(false)使变量m_display的值显示在编辑框中。 (2)数据交换和校验 对话框数据交换(Dialog Data EXchange,DDX)可以方便地实现对话框中控件数值的初始化和获取用户的数据输入。对话框数据校验(Dialog Data Validation,DDV)可以对对话框中的控件的数据进行校验。具体实现时可以通过ClassWizard定义与控件关联的数据成员实现DDX,通过限定数据范围实现DDV,例如:在对话框“IDD_MY_DIALOG”中,通过ClassWizard对标识号。为“IDC_DISPLAY”的“Edit Box”控件创建了m_display变量,数据类型为CString 。ClassWizard自动地在文件MyDlg.cpp中创建了相应的对话框数据交换代码: void CMyDlg::DoDataExchange(CDataExchange* pDX)//自动生成的对话框数据交换代码 { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyDlg) DDX_Text(pDX, IDC_DISPLAY, m_display); //}}AFX_DATA_MAP } (3)字符串CString类 CString类的对象由一个长度可变的字符序列组成,包含很多成员函数用来操作字符串,可以很方便地实现对字符串的各种操作。CString类中的字符是TCHAR类型的。 1)构造函数 CString(); //产生一个空的CString对象 CString(const CString&stringSrc); //用另一个CString对象的值初始化对象 CString(TCHAR ch,int nRepeat=1); //用一个字符重复若干次初始化对象 CString(LPCTSTR lpch,int nLength); //用一个字符数组的指定长度初始化对象 CString(const unsigned char *psz); //从一个无符号字符指针初始化对象 CString(LPCWSTR lpsz); //从一个Unicode字符串初始化对象 CString(LPCSTR lpsz); //从一个ANSI字符串初始化对象 2)常用成员函数 CString类的常用成员函数: int GetLength()const //获取CString类对象包含的字符串的长度 第3章 对话框与控件 BOOL IsEmpty()const //测试CString类对象包含的字符串是否为空 void Empty() //使CString类对象包含的字符串为空字符串 TCHAR GetAt(int nIndex)cont //获取字符串指定位置处的字符 void SetAt(int nIndex,TCHAR ch) //设定字符串指定位置处的字符 TCHAR operator[](int Index)const //获取字符串指定位置处的字符 Operator LPCTSTR()CONST //返回指向存储在CString类对象内的字符串指针 int Compare(LPCTSTR lpsz)const //比较两个字符串,类似于C中strcmp()函数 int CompareNoCase(LPCTSTR lpsz)const //类似于C中Compare()函数,但忽略字符大小写 MakeUpper //将字符串中所有的字符全部转化成大写形式 MakeLower //将字符串中所有的字符全部转化成小写形式 例1:连接字符串 CString m_str1="下午"; CString m_str2="好!"; CString m_str3=m_str1+m_str2; 结果m_str3的值为"下午好!" 例2:比较字符串 CString m_str1="a"; CString m_str2="b"; int result=m_str1.Compare(m_str2); if(result==0) AfxMessageBox("两者相同"); else if(result>0) AfxMessageBox("m_str1大于m_str2"); else AfxMessageBox("m_str1小于m_str2"); 实训2 对话框与控件的综合运用 程序运行,首先弹出登录对话框,输入正确的用户名和密码后方可进入程序运行界面。为程序添加“控件操作”主菜单,并为其添加“按钮控件”、“列表框控件”两个子菜单项。编写两个子菜单项的消息处理函数,使“按钮控件”演示如何使用单选按钮控制图片变换,“列表框控件”演示如何使用列表框选择不同的控件选项,然后动态生成并显示。 (1)创建一个单文档应用程序,名为:对话框与控件。 (2)添加一个顶层菜单项,名为:控件操作(K)。再在其下面建立两个子菜单,一个名为:按钮操作(A),ID为ID_BUTTON_TEST,另一个名为:组合框操作(Z),ID为ID_COMBO_TEST。 (3)添加3个对话框资源。第1个对话框的ID为:IDD_IDENTITY,标题为:输入密码;将其OK和Cancel改为“确定”和“取消”。第2个对话框的ID为:IDD_BUTTONTEST,标题为:按钮操作,删除Cancel按钮,将OK改为“退出”。第3个对话框的ID为:IDD_COMBOTEST,标题为:组合框操作,删除Cancel按钮,将OK改为“退出”。 第3章 对话框与控件 (4)为添加的对话框建类:输入密码对话框(IDD_IDENTITY)的类名为:CDlgIdentity;按钮操作对话框(IDD_BUTTONTEST)的类名为:CDlgButton;组合框操作对话框(IDD_COMBOTEST)的类名为:CDlgCombo。 图3.93 登录对话框 (5)参照图3.93~图3.95的控件布局,分别为输入密码对话框(IDD_IDENTITY)、按钮操作对话框(IDD_BUTTONTEST)、组合框操作对话框(IDD_COMBOTEST),添加如表3.42所示的控件。其中组合框操作对话框的组合框控件的属性选Data,并在Enter listbox items处写:静态文本、编辑框、组框、单选按钮、复选框。注意要用Ctrl + Enter组合键回车,分项输入。再打开Styles(样式)选项卡,去掉Sort(分类)前面的“√”即取消“分类”。如图3.96和图3.97所示。 (6)在CDlgIdentity类(密码),为控件IDC_EDIT_USER和IDC_EDIT_PASSWORD分别添加类型为CString的成员变量m_strUser和m_strPassword。 图3.94 “按钮控件”对话框 图3.95 “组合框控件”对话框 表3.42 添加的控件及其属性 对话框 控件类型 控件ID 标题 属性 输入密码 静态文本 IDC_STATIC 用户名 静态文本 IDC_STATIC 密码 编辑框 IDC_EDIT_USER 选择Password 编辑框 IDC_EDIT_PASSWORD 选择Password 按钮操作 组框 IDC_STATIC 选择图片 单选按钮 IDC_RADIO_SPORT 体育 单选按钮 IDC_RADIO_MUSIC 音乐 图片控件 IDC_BITMAP Type: Bitmap 第3章 对话框与控件 组合框操作 静态文本 IDC_STATIC 请选择 组合框 IDC_COMBO_CONTROL 取消Sort选项 图3.96 组合框Data属性 图3.97 不选中Sort(分类)选项 (7)在应用程序类:“对话框与控件.cpp(CMyApp.cpp)”中找到InitInstance()函数,该函数负责应用程序的初始化工作,如初始化数据、创建文档模版(与文档、视图和框架窗口相关联)、显示应用程序主窗口等。为了使窗口出现前,先出现输入密码对话框,在InitInstance()函数中加入如下代码: BOOL CMyApp::InitInstance() { AfxEnableControlContainer(); ……………… int nCount=0; //此变量限制密码错误时尝试登录次数 while(nCount<3) { CDlgIdentity dlgIdentity; if(dlgIdentity.DoModal()==IDOK) if((strcmp(dlgIdentity.m_strUser,"nsxy")!=0)|| (strcmp(dlgIdentity.m_strPassword,"1949")!=0)) // 验证用户名和口令 { MessageBox(NULL,"用户名或口令错,请重新输入!", "错误信息",MB_OK|MB_ICONERROR); nCount++; //错误次数加 1 } else //口令正确 break; //就可进入处理程序 else //单击"取消"按钮,退出程序 { return FALSE; } if(nCount>=3) //错误次数大于3次,就退出程序 { MessageBox(NULL,"口令输入已经超过三次,请退出!","错误信息", MB_OK|MB_ICONERROR); return FALSE; //退出程序 } 第3章 对话框与控件 } return TRUE; } 图3.98 “登录”对话框 因为在应用程序(CMyApp.cpp)中要调用“登录”对话框(CDlgIdentity)类,所以在“CMyApp.cpp”中要包含其头文件:#include “DlgIdentity.h”。 编译运行,弹出“登录”对话框,如图3.98所示。 (8)为按钮操作菜单 ID_BUTTON_TEST和组合框操作菜单ID_COMBO_TEST分别添加消息处理程序。由于只完成弹出对话框的操作,因此把处理程序映射到框架类CMainFrame中,并在该类CMainFrame.cpp中添加对两个对话框头文件的引用。 #include "DlgCombo.h" #include "DlgButton.h" void CMainFrame::OnButtonTest() { CDlgButton dlgButton; dlgButton.DoModal(); } void CMainFrame::OnComboTest() { CDlgCombo dlgCombo; dlgCombo.DoModal(); } (9)向项目中导入(Import)两个.bmp位图资源,作为交互选择的图形,这里Import“体育”和“音乐”两个.bmp类型的位图图形。 通过ClassWizard为图片控件(IDC_BITMAP)在CDlgButton类(按钮操作对话框)添加一个成员变量m_cBmp,类型为CStatic(Add Member Variable对话框Category中选择Control,便在Variable type中出现CStatic类)。 在DlgButton.h(按钮操作对话框类头文件)中的public下声明变量:CBitmap cBmp; (10)在CDlgButton(按钮操作)对话框类,分别为两个单选按钮IDC_RADIO_SPORT和IDC_RADIO_MUSIC添加BN_CLICKED消息处理函数,并添加代码: void CDlgButton::OnRadioSport() // 体育位图图片 { cBmp.DeleteObject(); //删除底层对象 cBmp.LoadBitmap(IDB_BITMAP2); //装载位图 m_cBmp.SetBitmap(HBITMAP(cBmp)); //给静态控件设置HBITMAP类的位图 } void CDlgButton::OnRadioMusic() // 音乐位图图片 { cBmp.DeleteObject(); //删除底层对象 第3章 对话框与控件 cBmp.LoadBitmap(IDB_BITMAP1); //装载位图 m_cBmp.SetBitmap(HBITMAP(cBmp)); //给静态控件HBITMAP类型的位图 } (11)为CDlgButton(按钮操作)对话框类添加WM_INITDIALOG初始对话框函数,并添加代码: BOOL CDlgButton::OnInitDialog() { CDialog::OnInitDialog(); cBmp.LoadBitmap(IDB_BITMAP1); //初始装载位图图形为"音乐" m_cBmp.SetBitmap(HBITMAP(cBmp)); //给静态控件设置HBITMAP类型的位图 return TRUE; } 图3.99 “按钮控件”对话框 (12)编译运行,选中“体育”单选按钮,便在对话框右边的图片控件上显示出“体育”位图图形,若选中“音乐”单选按钮,便显示“音乐”位图图形,结果如图3.99所示。 (13)为组合框操作对话框(CDlgCombo)上的组合框控件IDC_COMBO_CONTROL 添加一个类型为CComboBx的成员变量m_ComboBox和一个类型为CString的成员变量m_strCtrlName。 在类CDlgCombo.h文件的private下,添加一个指针变量: CWnd *pWndCtrl; // 指向动态创建的控件对象 (14)利用ClassWizard类向导为组合框对话框(CDlgCombo)类添加WM_INITDIALG和WM_DESTROY的消息处理函数,当用户打开对话框或关闭对话框时,分别调用这两个成员函数。 BOOL CDlgCombo::OnInitDialog() { CDialog::OnInitDialog(); pWndCtrl = NULL; //初始化控件对象指针 return TRUE; } void CDlgCombo::OnDestroy() { CDialog::OnDestroy(); if(pWndCtrl != NULL) delete pWndCtrl; } (15)利用ClassWizard在组合框操作对话框类(CDlgCombo)中,为组合框控件IDC_COMBO_CONTROL添加CBN_SELCHANGE的消息处理函数,当组合框中的当前选项发生变化时调用该函数。为了在退出该函数后在对话框中保留所创建的控件,使用new运算符在内存中创建控件对象,然后调用成员函数C 第3章 对话框与控件 Reate()在对话框指定位置(通过CRect参数)创建并显示控件。注意:在每一次创建一个新的控件之前,必须使用delete运算符将上一次创建的控件删除。 void CDlgCombo::OnSelchangeComboControl() { if(pWndCtrl!=NULL) { delete pWndCtrl; //删除上一次生成的控件对象 pWndCtrl = NULL; } int nChoice = m_ComboBox.GetCurSel(); //获得当前选项的索引 switch(nChoice) //根据选项生成不同类型的控件对象 { case 0: pWndCtrl = new CStatic; ((CStatic *)pWndCtrl)->Create(_T("静态文本"),WS_VISIBLE, CRect(180,40,300,70),this,1); break; case 1: pWndCtrl = new CEdit; ((CEdit *)pWndCtrl)->Create(WS_VISIBLE|WS_BORDER, CRect(180,40,300,70),this,2); break; case 2: pWndCtrl = new CButton; ((CButton *)pWndCtrl)->Create(_T("组框"),WS_VISIBLE|BS_GROUPBOX, CRect(180,40,300,100),this,3); break; case 3: pWndCtrl = new CButton; ((CButton *)pWndCtrl)->Create(_T("单选按钮"),WS_VISIBLE|BS_RADIOBUTTON, CRect(180,40,300,70),this,4); break; case 4: pWndCtrl = new CButton; ((CButton *)pWndCtrl)->Create(_T("复选框"),WS_VISIBLE|BS_CHECKBOX, CRect(180,40,300,70),this,5); break; default: //没有选择列表项 pWndCtrl = new CStatic; ((CStatic *)pWndCtrl)->Create(_T("没有选择"),WS_VISIBLE, 图3.100 “组合框控件”对话框 CRect(180,40,300,70),this,1); break; } } (16)编译运行,结果如图3.100所示。在组合框里选任一个控件,都在组合框的右边显示出该控件。 第3章 对话框与控件 图3.101 第6步选CEditView 实训3 更改字体设置 (1)创建一个单文档应用程序,名为:更改字体设置。第6步基类选:CEditView,如图3.101所示。这样可以使程序运行后在客户区出现闪动的输入光标,允许输入字符。 (2)添加3个对话框模版,① 标题:字体选择,ID为:IDD_FONT_CHOOSE;② 标题:字体规格,ID为:IDD_FONT_EFFECT;③ 标题:字体大小,ID为:IDD_FONT_SIZE。 分别为这3个对话框建类,基类都为CPropertyPage,① IDD_FONT_CHOOSE(字体选择)类名为:CFontChoose;② IDD_FONT_ EFFECT(字体规格)类名为:CFontEffect;③ IDD_FONT_SIZE(字体大小)类名为:CFontSize; 按图3.102~3.104所示的控件布局为对话框添加表3.43所示的控件。 图3.102 “字体选择”对话框 图3.103 “字体规格”对话框 图3.104 “字体大小”对话框 表3.43 属性页对话框成员变量表 对话框 控件类别 控件ID 成员变量 变量类型 标题 字体选择 静态文本 IDC_STATIC 系统字体 列表框 IDC_FONT_LIST m_lstFont ClistBox 字体规格 组框 IDC_STATIC 字体规格 复选框 IDC_BOLD m_bBold BOOL 粗体 复选框 IDC_ITALIC m_bItalic BOOL 斜体 字体大小 静态文本 IDC_STATIC 高度 编辑框 IDC_FONT_HEIGHT m_nHeight UINT 第3章 对话框与控件 静态文本 IDC_STATIC 宽度 编辑框 IDC_FONT_WIDTH m_nVidth UINT (3)向项目中添加属性单类:选择View→ClassWizard命令,单击Add Class按钮,选择New菜单,在Base class(基类)处选择:CPropertySheet,在Name处写:CPropSheet。 说明:可以通过上面的方法添加新的类,也可以通过选择“插入”→“类”命令来添加新的类,两者虽然都可以添加新的类,但最主要的不同点在于以下两点。 1)后者建立的类可以以CObject为基类,而前者不可以。 2)如果编程时需要添加一个不以任何类为基础的新类时,使用后者实现非常方便。使用这两者均可以添加CPropertySheet类,可以选择期中的任意一种来完成此工作。 (4)为了能在属性单中显示已经建立好的3个属性页对话框,需要使用属性单的成员函数将属性页添加到属性单中,因此,在CPropSheet.h中包含3个属性页类的头文件: #include "FontChoose.h" #include "FontEffect.h" #include "FontSize.h" 并且在该类的public下加入3个成员变量: CFontChoose m_pageFont; CFontEffect m_pageEffect; CFontSize m_pageSize; 在属性单类的执行文件CPropSheet.cpp的两个构造函数中都添加下列代码: this->AddPage(&m_pageFont); this->AddPage(&m_pageEffect); this->AddPage(&m_pageSize); (5)为字体选择类CFontChoose添加初始化函数WM_INITDIALOG,并添加下列代码,为“字体选择”属性页中的列表添加备选的字体名称。 BOOL CFontChoose::OnInitDialog() { CPropertyPage::OnInitDialog(); //设置列表框显示内容时所用的字体为"楷体_GB2312" CFont font; font.CreatePointFont(120,"楷体_GB2312"); m_lstFont.SetFont(&font); //添加4种字体名称 m_lstFont.AddString("Courier"); m_lstFont.AddString("Time New Roman"); m_lstFont.AddString("Arial"); m_lstFont.AddString("楷体_GB2312"); //设置列表框初始选中的字体为第一种字体,即"Courier"字体 m_lstFont.SetCurSel(0); return TRUE; 第3章 对话框与控件 } (6)为了保存在属性页对话框中的选择的字体,在视图类头文件CMyView.h的private下添加成员变量:CFont *pNewFont;。 在该文件的上面添加对属性单的包含:#include “PropSheet.h”。 (7)添加主菜单项,名为:字体设置,ID为ID_FONT_SET,并将其消息映射到视图CMyView类中。选择View→ClassWizard命令,在弹出的对话框的Class name处选择CMyView,Object IDs处找到ID_FONT_SET,在Messages处选择COMMAND,单击Add Function按钮,就将菜单映射函数OnFontSet()加到了视图类中。在该函数中添加下列代码: void CMyView::OnFontSet() { //设置属性单显示时的标题栏 CPropSheet propSheet("字体设置",this,0); if(propSheet.DoModal()==IDOK) //模式属性单 { CString str; //用来获取列表框中的字体名称字符串 CEdit &MessageBody = GetEditCtrl(); //获取视图窗口包含的编辑控件 // LOGFONT结构用于说明字体的所有属性,该变量用来记录各属性页的输入值 LOGFONT logft; logft.lfHeight = propSheet.m_pageSize.m_nHeight; //获取字体高度 logft.lfWidth = propSheet.m_pageSize.m_nVidth; //获取字体宽度 logft.lfEscapement = 0; //文本行的倾斜度 //是否粗体 logft.lfWeight = propSheet.m_pageEffect.m_bBold?FW_BOLD:FW_NORMAL; logft.lfItalic = propSheet.m_pageEffect.m_bItalic; //是否斜体 logft.lfUnderline = 0; //不创建下画线 logft.lfStrikeOut = FALSE; //不创建删除线 // propSheet.m_pageFont.m_lstFont.GetText(propSheet.m_pageFont. // m_lstFont.GetCurSel(),str); //获取字体列表框中选中的字体名称 // strcpy(logft.lfFaceName,str); //将选中的字体名称放入LOGFONT结构体变量中 //创建新字体 pNewFont = new CFont; pNewFont->CreateFontIndirect(&logft); MessageBody.SetFont(pNewFont); //设置编辑控件字体,使其生效 } } (8)为了使字体大小属性页中字体的宽度和高度有初始值,可以在属性页对应的类FontSize.cpp中修改其构造函数。这里将初始值均设置为10(原来为0),代码如下: CFontSize::CFontSize() : CPropertyPage(CFontSize::IDD) { //{{AFX_DATA_INIT(CFontSize) m_nHeight = 10; m_nVidth = 10; 第3章 对话框与控件 //}}AFX_DATA_INIT } (9)因为在OnFontSet()函数中创建了新的字体,所以在程序结束时应清除该字体指针。清除工作可以在DestroyWindow()函数中完成。添加DestroyWindow()的方法是:在项目工作区打开ClassView选项卡,右击CMyView类,在弹出的快捷菜单中选择Add Virtual Function命令,弹出图3.105所示的对话框,在左边的窗口中双击DestroyWindow,然后单击Add and Edit按钮进行代码编辑,该函数中添加的代码如下: BOOL CMyView::DestroyWindow() { if(pNewFont) delete pNewFont; return CEditView::DestroyWindow(); } (10)编译运行,先在文档中写入一些字,再打开菜单,设置字体,结果如图3.106所示。这样退出后,可以保存,之后再打开保存的文件,还能显示出写的内容。 图3.105 添加DestroyWindow()函数 图3.106 属性页中设置字体示意图 图4-10“选择性粘贴”对话框 第2章 菜单、工具栏和状态栏的设计 在Windows应用程序中,菜单、工具栏、状态栏等内容都是不可缺少的界面元素。菜单是一系列可视的命令列表,用户能够选中其中的菜单项(命令)并执行相应任务。工具栏提供图形按钮,实现快捷操作,用户可以通过工具栏执行最常用的命令,增强方便程度。在状态栏中,可以显示动态的提示信息,便于用户的一些操作。 第3章 对话框与控件 2.1 设计菜单 菜单为用户控制程序提供了一套分级选项,无论是标准菜单及命令,还是热键或弹出式菜单,以及为菜单或其命令定义加速键和状态条提示,都可用菜单编辑器来完成;除此之外,菜单项作为一个普通的对象也可在编辑时进行移动、复制、删除等操作。图2.1是一个典型的菜单实例,图中包含了标准菜单命令、快捷键、加速键、子菜单、核对符等。这些都是编写程序时经常遇到的。下面我们就介绍如何创建和编辑菜单项。 图2.1 典型菜单实例 2.1.1 用编辑器设计菜单 当用户使用AppWizard创建SDI或MDI应用程序时,系统将为用户自动生成默认的菜单栏。用户需要做的工作仅仅是打开菜单编辑器,进行必要的修改,再编写菜单选项相应的消息处理函数即可。当然也可在菜单编辑器中创建新的菜单或创建新的菜单资源,如快捷菜单等。 无论是编辑已有菜单资源还是创建新的菜单资源,首先应当进入菜单资源编辑器。 例2.1 在菜单编辑器的”文件(F)”下拉的某个位置加一个菜单,其作用是单击它后,能在屏幕上显示一行字。编程步骤如下: (1)建一个SDI单文档应用程序名为:显示一行字 (2)用编辑器设计菜单: 项目工作区ResourceViewà单击打开Menu à双击IDR_MAINFRAMEà右面出现菜单编辑器,见图2.1.1à单击“文件(F)”出现下拉菜单,见图2.1.2所示à 第3章 对话框与控件 点中最后的空白菜单,双击它à出现“菜单项属性对话框”à在Caption处写菜单名:显示一行字(&C)à在ID处写ID_FILE_XSà在prompt(注释)栏中写:点击新建菜单项,在窗口显示一行字à关闭该对话框。 顶层菜单的空位置 子菜单的空位置 图2.1.1 菜单资源编辑器 窗口显示菜单 菜单标题 注释栏 菜单标识符 图2.1.2 菜单编辑器和修改菜单项属性的结果 (3)在文档的头文件CMyDoc.h的public下加: CString str; //定义字符串变量 在文档的实现文件CMyDoc.cpp的构造函数里加: str=” ”; //将字符串变量赋初值为”空” 第3章 对话框与控件 (4)将菜单的标识ID_FILE_XS映射到视图类CMyView中。 View->Classvizard->Message Maps->Classname置CMyView->Object IDs选中ID_ FILE _XS->选中COMMAND->单击Add Function->OK->Edit Code。在OnFileXS()映射函数中加代码: void CMyView::OnFileXS() { CMyDoc *pDoc=GetDocument(); //获得文档类指针 ASSERT_VALID(pDoc); //检查pDoc指针是否有效 pDoc->str=“你成功的在File菜单下,建立了一个显示一行字菜单”; Invalidate(); //去强制执行OnDraw()函数 } (5)再在视图实现文件CMyView.cpp里的OnDraw(CDC *Pdc)函数里加代码: CMyView::OnDraw(CDC *Pdc) { pDC->TextOut(100,100,pDoc->str); } //在窗口的x=100,y=100坐标处输出字符串 (6)编译运行 * 在出现的文档窗口上,单击菜单的“文件”->单击“显示一行字”菜单项,在屏幕上显示程序中写的一行字。 * 单击“文件(F)”,出现下拉采单,直接按C键,也出现这行字。 * 将鼠标放在“显示一行字”这个菜单上,下面状态栏会出现你在程序中写的注释:“点击新建菜单项,在窗口显示一行字”。 说 明: 1、TextOut(100,100,pDoc->str)是CDC类的输出函数,100,100是x,y坐标,pDoc->str是输出str内容。 2、改变菜单位置:鼠标左键选中该菜单不放,拖到你想要加的位置即可。 3、想在哪个位置加菜单:可选中后面的那个菜单,按Insert键即可。 4、“显示一行字(&C)”:其中“&”用于将其后面的字符作为该菜单项的助记符,也就是它后面的 字符成了快捷键字符。打开这个菜单,直接按这个助记符键,菜单命令就执行。 注 意:快捷键字符C不能与同一级快捷键字符重复,例如:若写“显示一行字 (&X)”则与下面的“退出(&X)”重复,系统无法判别是哪个X,则不能执行。 图2.1.2中的小对话框(Menu Item Properties)是双击“文件(F)”下的空白菜单出现的菜单属性对话框,我们用它建立了“显示一行字(&C)”菜单,菜单General属性的各项含义如表2.1所示。 第3章 对话框与控件 例2.2 在顶层菜单栏里建立一个菜单项,并在其下面建立带有子菜单的菜单项,使有的子菜单具有加速键、变灰和核对符,又使每个子菜单都能显示信息。步骤如下: (1)建一个单文档的应用程序(或用例2.1程序),名为:山东旅游 (2)建立菜单: 1)ResourceViewàMenuàIDR_MAINFRAMEà右面出现菜单编辑器à左键选中顶层最后的空白菜单不放,将其拖到“帮助”的前面,松开鼠标(或选中“帮助”菜单,按Insert键)。双击这个空白菜单,出现“菜单属性对话框”,在Caption处写:山东旅游(&S)à Pop_up处于选中状态(屏蔽ID)à退出。 2)双击下面出现的空白菜单,出现“菜单属性对话框”,选中Pop_up(屏蔽ID)àCapton处写:烟台(&Y) 3)右边出现空白子菜单,双击它,出现“菜单属性对话框”àID处写:ID_SD_YT_PLàCapton处写:蓬莱 Ctrl +F5 (注:Ctrl +F5是加速键标识)à注释栏prompt处写:蓬莱仙境 4)双击“蓬莱”下面的子菜单,双击它,出现“菜单属性对话框”àID处写:ID_SD_YT_NSàCapton处写:南山(&N)àprompt处写:南山大佛 5)双击“烟台”下面的空白菜单, 出现“菜单属性对话框”à选中Pop up(屏蔽ID)àCapton处写:青岛(&Q) 6)右边出现空白子菜单,双击它,出现“菜单属性对话框”à ID处写:ID_SD_QD_LSàCapton处写:崂山 Ctrl +F6àprompt处写:崂山道士 表2. 1 菜单General属性对话框的各项含义 项 目 含 义 ID 菜单的资源ID标识符 Caption(标题) 用于标识菜单项显示文本,助记符字母前面须有一个&符号,这个字 母与Alt构成组合键 Separator(分隔符) 选中时,菜单项是一个分隔符或一条水平线 Checked(选中的) 选中时,菜单项文本前显示一个选中标记 Pop_up(弹出) 选中时,菜单项含有一个弹出式子菜单 Grayed(变灰) 选中时,菜单项显示是灰色的,用户不能选用 第3章 对话框与控件 Inactive(非激活) 选中时,菜单项没有被激活,用户不能选用 Help(帮助) 选中时,菜单项在程序运行时被放在顶层菜单的最右端 Break(暂停) 当为Column时,对于顶层菜单项来说,被放置在另外一行上,而 对于弹出式子菜单的菜单项来说,则被放置在另外一列上,当为Bar 时,与Column相同,只不过对于弹出式子菜单来说,它还在新列 与原来的列之间增加一条竖直线,注意:这些效果只能在程序运行 后才能看到。 Prompt(提示) 用于指明光标移至该菜单项时,在状态栏上显示的提示信息 7)双击“青岛”下面的空白菜单, 出现“菜单属性对话框”à选中Pop up(屏蔽ID)àCapton处写:泰安(&T) 8)右边出现空白子菜单,双击它,出现“菜单属性对话框”à ID处写:ID_SD_TA_TSàCapton处写:泰山 Ctrl +F7àprompt处写:泰山日出 9)双击“泰安”下面的空白菜单,出现“菜单属性对话框”à选中Pop up(屏蔽ID)àCapton处写:济南(&J) 10)右边出现空白子菜单,双击它,出现“菜单属性对话框”à ID处写:ID_SD_JN_BTQ àCapton处写:趵突泉 Ctrl +F8àprompt处写:天下第一泉 图2.1.3 加速键资源列表 第3章 对话框与控件 (3)填加加速键表: 1)单击项目工作区的资源界面ResourceViewà打开Acceleratorà双击IDR_MAINFRAMEà出现加速键表,双击最下面的空白格,见图2.1.3所示à出现加速键属性对话框Accl Properties àID处下拉找到ID_SD_YT_PL置好àkey处下拉,找到VK_F5置好(或置好ID后,单击[Next Key Typed],出现一个小对话框后,再按下Ctrl+F5键也可)见图2.1.4所示,这样就为“蓬莱”菜单置好了加速键。“加速键General属性”对话框的各项含义如表2.2所示。 图2.1.4 “Accel Properties”(加速键属性)对话框 2)你再双击最下面的空白格,按上步的方法,分别将青岛崂山(ID_SD_QD_LS)、泰安泰山(ID_SD_TA_TS),济南趵突泉(ID_SD_JN_BTQ)菜单分别置好VK_F6 ,VK_F7,VK_F8的加速键。 注 意:图2.1.4中Modifiers处,选中Ctrl,说明是Ctrl键,选中Alt说明是Alt键,选中Shift说明是Shift键,小对话框“Press a key to be ysed as the accelerator”是单击“Next Key Typed”弹出来的。 表2. 2 “加速键General属性”对话框的各项含义 项目 含义 ID 指定资源ID号的列表项,为了能和菜单联用,通常选择某菜单项的ID号 Modifiers 用于确定Ctrl、Alt、Shift是否是构成加速键的组成部分 Type 用于确定该加速键的值是虚拟键(VirKey),还是ASCII Key 是指启动加速键的键盘按键 下一键(Next Key Type) 单击此按钮后,用户操作的任何按键将成为此加速键的键值。 (4)使菜单变灰(不被激活,不起作用) 第3章 对话框与控件 ViewàClassWizardàClassname选CMyViewà在Object IDS里找到你想要变灰的菜单,这里选中ID_SD_TA_TS(泰山)à右边Messages里选中UPDATE COMMAND UI(命令属性)àAddFoution àOKàEdit Codeà在该函数里写: void CMyView::OnSdTaTs() { pCmdUI->Enable(false); } 说 明: pCmdUI是CCmdUI类的指针对象。CCmdUI类只是被用在一个CCmdTarget派生类中的ON_UPDATE_COMMAND_UI处理程序中。当用户拉出一个菜单时,每个菜单项都需要知道它应该被显示为可用还是禁用。当菜单被拉出时,(框架)会寻找并调用各个ON_UPDATE_COMMAND_UI处理程序,每个处理程序都调用CCmdUI类中像Enable和Check这样的成员函数,然后框架将(按照其合适的方式)显示各个菜单项。 (5)核对菜单项,使这个菜单名字的前面加个“√”号 ViewàClassWizardàClassname选CMyViewà在Object IDS里找到你想要核对的菜单, 这里选中ID_SD_YD_NS(南山)à右边Messages里选中UPDATE COMMAND UIàAddFoutionàOKàEdit Codeà在该函数里写: void CMyView::OnUpdateSdYtNs(CCmdUI* pCmdUI) { pCmdUI->Enable(true);//这里如果是Enable(false)则该菜单变灰 pCmdUI->SetCheck(1);//设置核对符,如果括弧里写0是删除核对符 } (6)菜单命令响应 1)在View.h里的public:下定义变量:CString str; 在View.cpp的构造函数里将变量值赋空:str=“ ”; 2)将烟台的子菜单“蓬莱”的ID标识符ID_SD_YT_PL映射到视图类View里: ViewàClassWizardà在LassName置View(视图类)à在ObjectIDs里找到ID_SD_YT_PLà选中COMMANDàAdd FunctionàEdit Codeà加代码: void CMyView::OnSdYtPl() { str=“蓬莱仙境”; Invalidate(); } 3)将烟台的子菜单“南山”的ID_SD_YT_NS映射到视图类View里,并加代码: void CMyView::OnSdYtNs() 第3章 对话框与控件 { str=“南山大佛”; Invalidate(); } 4)分别将青岛的子菜单“崂山”ID_SD_QD_LS、泰安的子菜单“泰山”ID_SD_TA_TS、济南的子菜单“趵突泉”ID_SD_JN_BTQ映射到视图类中,并加代码: void CMyView::OnSdQdLs() //青岛崂山 { str=“崂山道士”; Invalidate(); } void CMyView::OnSdTaTs() //泰安泰山 { str=“泰山日出”; Invalidate(); }  void CMyView::OnSdJnBtq()//济南趵突泉 { str="天下第一泉"; Invalidate(); } 5)在视图类实现文件CMyView.cpp的OnDraw()函数里加代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDC->TextOut(50,50,str); } (7)编译运行,,结果见图2.1所示 1)打开山东旅游菜单à烟台,见菜单“南山”前面有“√”号 2)打开山东旅游菜单,分别点“蓬莱”、“南山”、“崂山”、“趵突泉”便出现各自的信息。 而“泰山”是灰色的,不能显示信息。 3)程序运行后,出项空白窗口,你按Ctrl+F5、Ctrl+F6、Ctrl+F8分别在窗口中显示 “蓬莱仙境”,“崂山道士”,“天下第一泉”。Ctrl+F7变灰不能显示信息。 4)打开山东旅游菜单,按相应的助机记符(烟台)Y、(青岛)Q、(泰山)T、(济南)J键,便出现各自的子菜单,将鼠标放在子菜单“南山”上,按助记符N,便在文档窗口上显示出:南山大佛。 第3章 对话框与控件 说 明: void Invalidate( BOOL bErase = TRUE ); 该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。 视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。 它和 UpdateWindow( )区别在于: UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。   2.1.2 菜单的编程控制 在交互式软件设计中,菜单有时会随着用户操作的改变而改变,这时的菜单就需要在程序中进行控制。MFC提供的菜单类CMenu可在程序运行时,处理有关菜单的操作,如:创建菜单、装入菜单、删除菜单、获取菜单或设置菜单的状态等。下面给出了CMenu类的常用成员函数。 1、 创建菜单: CMenu类的CreateMenu()和CreatePopupMenu()函数分别用于创建一个菜单或子菜单框架,它们的原型是: BOOL CreateMenu(); 产生一个空菜单 BOOL CreatePopupMenu();产生一个空的弹出式子菜单 2、 装入菜单 将菜单从资源装入应用程序中,需要调用Cmenu类成员函数LoadMenu或者用SetMenu对应用程序菜单进行重新设置。 BOOL LoadMenu(LPCTSTR lpszResourceName); BOOL LoadMenu(UINT nIDResource); 参 数: lpszResourceName:菜单资源名称 nIDResource: 菜单资源ID标识号 3、 添加菜单项 当菜单创建后,用户可以调用AppendMenu或InsertMenu函数来添加一些菜单项。 第3章 对话框与控件 但每次添加时,AppendMenu是将菜单项添加在菜单的末尾处,而InsertMenu在菜单的指定位置处插入菜单项,并将后面的菜单项依次下移。 BOOL AppendMenu(UINT nFlags,UINT nIDNewItem=0, LPCTSTR lpszNewItem=NULL); BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const CBitmap *pBmp); BOOL InSertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0, LPCTSTR lpszNewItem=NULL); BOOL InSertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem, const CBitmap *pBmp); 参 数: nIDNewItem 表示新菜单项的资源ID号 lpszNewItem表示新菜单项的内容 pBmp 用于菜单项的位图指针 nPosition 表示新菜单项要插入的菜单项位置 nFlags表示要增加的新菜单项的状态信息,其含义见表2.3所示。 表2.3 nFlags的值及其对其他参数的影响 nFlage值 含义 nPosition值 nIDNewItem值 lpszNewItem MF_BYCOMMAND 菜单项以ID标识符来标识 菜单项资源ID MF_BYPOSITION 菜单项以位置来标识 菜单项的位置 MF_POPUP 菜单项有弹出式子菜单 弹出式菜单句柄 MF_SEPARATOR 分割线 忽略 忽略 MF_OWNERDRAW 自画菜单项 自画所需的数据 MF_STRING 字符串标志 字符串指针 MF_CHECKED 设置菜单项的选中标记 MF_UNCHECKED 取消菜单项的选中标记 MF_DISABLED 禁用菜单项 第3章 对话框与控件 MF_ENABLED 允许使用菜单项 MF_GRAYED 菜单项灰显 注 意: (1)当nFlags为MF_BYPOSITION时,nPosition表示新菜单项要插入的具体位置,为0时表示第1个菜单项,为-1时,将菜单项添加至菜单的末尾处。 (2)在nFlags的标志中,可以用”|”(按位或)来组合,例如MF_CHECKED|MF_STRING等。但有些组合是不允许的,例如MF_DISABLED、MF_ENABLED和MF_GRAYED,MF_STRING、MF_OWNERDRAW、MF_SEPARATOR和位图,MF_CHECKED和MF_UNCHECKED都不能组合在一起。 (3)当菜单项增加、改变或删除后,不管菜单依附的窗口是否改变,都应调用CWnd::DrawMenuBar来更新菜单。 4、 删除菜单项 调用DeleteMenu函数可将指定的菜单项删除。函数DeleteMenu的原型如下: BOOL DeleteMenu(UINT nPosition,UINT nFlags); 参 数: nPosition表示要删除的菜单项位置,它由nFlags进行说明。 当:Flags为MF_BYCOMMAND时,nPosition表示菜单项的ID标识符。 当:Flags为MF_BYPOSITION时,nPosition表示菜单项的位置(第一个菜单项为0) 5、 获取菜单项 以下三个CMenu成员函数分别获得菜单的项数、菜单项的ID标识符以及弹出式子菜单的句柄。 UINT GetMenuItemCount()const;获得菜单项的项数,调用失败返回-1。 UINT GetMenuItemID(int nPos)const;获得由nPos指定菜单项位置(以0为基数)的菜单项的标识号,若nPos是SEPARATOR(分隔符)则返回-1。 CMenu *GetSubMenu(int nPos)const;获得指定菜单的弹出式菜单的菜单句柄,该弹出式菜单位置由参数nPos指定,开始位置为0,若选单不存在,则创建一个临时菜单指针。 例2.3 用编写程序的方法添加并处理一个新的菜单项 (1)创建一个单文档(SDI)应用程序(或用例2.2程序),名为:添加菜单项 (2)ViewàResourceSymbols弹出图2.1.5的对话框。 (3)单击“New”按钮,弹出“New Symbol”对话框àNewà在名字(Name)框中输入一个新的标识符 第3章 对话框与控件 ID_NEW_MENUITEM。在值(Value)框中,输入该ID的值, 如图2.1.6所示。系统要求用户定义的ID值应大于15(0X000F)而小于61440(0XF000)。这里保留默认的ID值101,单击“OK”按钮。 图2.1.5 “资源符号”对话框 图2.1.6“New Symbol”对话框 (4)关闭“资源符号”对话框,在CMainFrame::OnCreate函数中添加下列代码,该函 数在框架窗口创建时自动调用。 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { …… CMenu *pSysMenu=GetMenu();//获得主菜单句柄指针 CMenu *pSubMenu=pSysMenu->GetSubMenu(1);//获得第二个子菜单指针 CString StrMenuItem(“新的菜单项”);//字符串对象 pSubMenu->AppendMenu(MF_SEPARATOR);//增加一水平分割线(见表2.3) //在子菜单末尾增加一菜单项,允许使用ON_UPDATE_COMMAND_UI或ON_COMMAND的菜单//项下面MF_STRING是字符串标志,ID_NEW_MENUITEM是新的菜单标识符,StrMenuItem是新菜单//项内容 pSubMenu->AppendMenu(MF_STRING,ID_NEW_MENUITEM,StrMenuItem); m_bAutoMenuEnable=FALSE;//关闭系统自动更新菜单状态,见下面说明3 pSysMenu->EnableMenuItem(ID_NEW_MENUITEM,MF_BYCOMMAND| MF_ENABLED);//激活菜单项 DrawMenuBar();//更新菜单 第3章 对话框与控件 return 0; } (5)编译运行,程序添加的菜单如图2.1.7,但此时只是将菜单项加上了,命令却无反映。 程序添加的菜单项 图2.1.7程序添加的菜单项 图2.1.8 菜单命令执行结果 (6)使用ClassWizard在CMainFrame主框架类中添加OnCommand消息函数的重载,并检测用户菜单的nID参数: View->ClassWizard->在Class name处下拉置CMainFrame ->Messages处找到OnCommand消息,将其映射到CMainFrame里并添代码: BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam) { //参数wParam的低字节表示菜单、控件、加速键的命令ID if(LOWORD(wParam)==ID_NEW_MENUITEM) MessageBox(“你选中了新的选单项”); } (7)编译运行并测试。点击菜单项的编辑->新的选单项;弹出图2.1.8对话框,显示“你选中了新的选单项” 说 明: 1、WPARAM wParam,LPARAM lParam参数,见第1章例20的说明。 2、LOWORD是获取其参数中的整型值位数。 下面是用C++语言编写的程序,可以看到LOWORD是取wParam参数的右边的4位数,即菜单的ID值(是用资源符号建菜单的Value框中写的101)。 #include 第3章 对话框与控件 // #include #include int main() { int iInWord = 101; cout<m_pMainWnd; CMenu *pSysMenu=pFrame->GetMenu(); //获得程序窗口菜单指针 int nCount=pSysMenu->GetMenuItemCount();//获得顶层菜单个数 int nSubMenuPos=-1; //给特征变量送个标记,下面要用 for(int i=0;iGetMenuString(i,str,MF_BYPOSITION); 第3章 对话框与控件 if(str.Left(4)=="文件") { nSubMenuPos=i; //将菜单的条目号送给特征变量 break; } } if(nSubMenuPos<0) return; //没有找到返回 pSysMenu->GetSubMenu(nSubMenuPos) //获得“文件”下面的子菜单 ->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, //见表2.4 point.x,point.y,this);// 在屏幕的任意地方显示一个弹出式菜单。 } (3)在视图的执行文件“快捷菜单View.cpp”文件头部加:#include “MainFrm.h” (4)编译运行,在应用程序窗口的客户区中,用鼠标单击右键,会弹出快捷菜单(将原 File下拉的菜单弹出来),见图2.1.9所示。 说 明: 1、WM_CONTEXTMENU鼠标右键按下时发送的消息 2、GetMenuString(i,str,MF_BYPOSITION); 将指定菜单项的标签拷贝到指定的缓冲区。 如果其最后的参数为MF_BYCOMMAND,则 其他参数指定菜单项标识符。如果其最后的参 数为MF_BYPOSITION,则其他参数指定菜单 项位置。如果其最后的参数为: MF_BYPOSITION,这个参数就代表菜单条目 在菜单中的位置(第一个菜单条目的位置为零)。 图2.1.9 例2.4快捷菜单显示结果 3、AfxGetApp ()函数:是获取应用程序实例指针    GetMainWnd()函数:是获取主窗口对象指针。这两个函数可以合成一个:   afxgetmainwnd();  我们通常把一些重要的工程一开始就需要初始化的并且在其它地方类中都要用到的变量或函数定义 在C***App类中,然后通过此函数获得这些变量或函数。 由于菜单、工具栏、状态栏是由主框架类CMainFrame来控制的,虽在视图类可以添加快捷菜单消息映射,但若要在视图类中访问应用程序的主框架窗口的系统菜单,则必须通过AfxGetApp来获取主框架类对象指针后才能获取相应的菜单。AfxGetApp是CWinApp类的一个成员函数,该函数可在应用程序项目中的任何类中使用,用于获取应用程序中的CWinApp类对象指针。 第3章 对话框与控件 例2.5 使用快捷菜单(以快捷方式弹出自己设计的菜单项) (1)创建一个单文档应用程序,名为:建立快捷菜单。 (2)InsertàResourceà点中MenuàNewà便在Menu资源下出现一个新菜单资源(默 认的ID号为IDR_MENU1),将此ID号改为:IDR_MYFLOATMENU (3)双击右边出现的空白菜单项,出现 Menu Item Properties对话框,在对话框中选中Pop up,在Caption处写:弹出快捷菜单->关闭à点开下面的子菜单,依次添加如下表所示的子菜单项: 菜单 ID 标 题 属 性 ID_MENU_SCOREIN 成绩输入(&S) 默认 ID_MENU_SCOREPRINT 成绩打印(&P) 默认 ID_SEPARATRO 选中Separator ----- 其他(&Q) 选中Pop_up,其余默认 (4)将ID_MENU_SCOREIN的COMMAND消息映射到主框架类MainFrame中(如果要出现提问对话框,选择Select a new class,出现Select Class对话框,选择对话框上的CMainFrameàSelect,回到MFC ClassWizard,接着加如上ID的COMMAND消息)。 ViewàClassWizardàClassNameàCMainFrameà将上面的ID分别加COMMAND消息。这里我们仅加ID_MENU_SCOREIN的COMMAND消息,并加如下代码: void CMainFrame::OnMenuScorein() { AfxMessageBox(“现在就输入成绩吗?”); } (5)在CMainFrame类加入WM_CONTEXTMENU消息处理函数,添加代码: void CMainFrame::OnContextMenu(CWnd *pWnd,CPoint point) { CMenu menu; menu.LoadMenu(IDR_MYFLOATMENU);//刚才加上的菜单资源 menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN| TPM_RIGHTBUTTON,point.x,point.y,this); 第3章 对话框与控件 } (6)运行并测试,在出现的应用程序窗口中,单击鼠标右键,会出现你创建的快捷菜单,如图2.1.10所示。再单击“成绩输入”,会出现写有“现在就输入成绩吗?”的对话框。 2.2 工具栏 工具栏是一系列工具按钮的组合,借助它 们可以提高用户的工作效率。Visual C++6.0系 统保存了每个工具栏相应的位图,其中包括所有 按钮的图象,而所有的按钮图象具有相同的尺 寸(15像素高,16像素宽),它们在位图中的排 列次序与屏幕上的按钮在工具栏上的次序相同。 图2.1.10 例2.5快捷菜单显示结果 2.2.1使用工具栏编辑器 在项目工作区窗口中选择ResourceView页面,双击“Toolbar”项目中的IDR_MAINFRAME,则工具栏编辑器出现在主界面的右边,如图2.2.1所示。 工具栏空按钮 图形工具箱 颜色工具箱 图2.2.1 工具栏编辑器 例2.6 工具栏按钮和菜单联用 (1)创建一个SDI单文档应用程序,名为:工具栏 第3章 对话框与控件 (2)ResourceViewàToobaràIDR_MAINFRAME工具栏编辑器出现在主界面的右边 à单击最后一个空的工具栏按钮(建完该工具栏按钮后,其后面又会自动出现一个新的空工具栏按钮)à点颜色工具箱的红色,再点图形工具箱的画刷à之后将鼠标移到下面较大的方块图上,来回移动鼠标将这个按钮涂红色后,见图2.2.1所示à再双击上面这个按钮,出现属性对话框,见图2.2.2所示,ID处写:ID_TOOLBARà在prompt(注释)框内键入:建立新文档\n新建 (3)将ID_TOOLBAR映射到View视图类中,并加代码: void CMainFrame::OnToolbar() { MessageBox(“工具栏显示”); } (4)编译运行鼠标放在刚创建的工具 拦上出现“新建”框,而一会儿便消失。 在状态拦上出现“建立新文档”字样。再 单击这个按钮,在文档窗口出现小对话 框,上面写的是“工具栏显示”。 图2.2.2 “工具栏按钮属性”对话框 (5)建一个菜单项ResourceViewà Menuà双击IDR_MAINFRAMEà双击右边出现空白菜单à标题处写:和工具栏联用àID处写(和工具栏的ID一样):ID_TOOLBARà运行à点这个菜单和点刚才创建的工具栏的显示结果是一样的(这就是工具栏和菜单相结合)。鼠标点中这个菜单不放,状态栏出现“建立新文档”,放开鼠标,状态栏的“建立新文档”没了,但在文档窗口出现一个小对话框,和点刚才建的工具栏按钮一样,上面也写着“工具栏显示”。 说 明: 1、移动一个工具栏按钮:用鼠标左键单击这个按钮不放,拖动至相应位置即可。 2、删除一个工具栏按钮:用鼠标左键单击这个按钮不放,拖动它离开工具栏。 3、复制一个工具栏按钮:若在移动一个按钮的同时,按下Ctrl键,则在新位置复制一个按钮。 4、在工具栏中插入空格有以下几种情况: (1)如果工具栏按钮前没有空格,拖动该按钮向右移动并当覆盖相邻按钮的一半以上时,释放鼠标,则此按钮前出现空格。 (2)如果工具栏按钮前有空格而按钮后没空格:拖动该按钮向左移动并当按钮的左边界接触到前面按钮时,释放鼠标键,则此按钮后出现空格。 (3)如果工具栏按钮前后均有空格,拖动该按钮向右移动并当接触到相邻按钮时,则此按钮前的空格保留,按钮后的空格消失。相反,拖动该按钮向左移动并当接触到前一个相邻按钮时,则此按钮前面的空格消失,后面的空格保留。 第3章 对话框与控件 5、工具栏按钮属性的设置:双击某按钮弹出“工具栏属性”对话框,如图2.2.2所示。工具栏按钮属性对话框中的各项含义如表2.5所示。 2.2.2 多个工具栏的使用 实际应用中,常常需要多个工具栏,下面来讨论多个工具栏的创建、显示和隐藏,以及多个工具栏和菜单之间的联动操作等。 例2.7 使用多个工具栏 1、添加并更改应用程序菜单 (1)创建一个单文档应用程序,名为:多个工具栏 表2.5 工具栏按钮属性对话框中的各项含义 项 目 含 义 ID 工具栏按钮的标识符,可以输入自己的标识符名称,也可从ID的下 拉列表中选取标识符名称 Width(宽) 工具栏按钮的像素宽度 Height(高) 工具栏按钮的像素高度 Prompt(提示) 工具栏按钮提示文本:若为“建立新文档\n新建”,则表示将鼠标指 向该按钮时,在状态栏中显示“建立新文档”,而在弹出的提示信息 中出现“新建”字样。”\n”是它们的分割转义符。 (2)InsertàResourceà在资源类型中选中Menu(或按快捷键Ctrl+R)à单击“New”按钮,便在右边出现一个空菜单,系统给的默认ID为IDR_MENU1见图2.2.3所示。 (3)在Menu资源的ID_MENU1上单击鼠标右键,从弹出的快捷菜单中选择“Properties” 命令,出现如图2.2.4所示的“菜单属性对话框”,在这里可以重新指定菜单资源ID,设置菜单资源的语言和条件。这个条件用于决定菜单资源包含到哪个环境中,例如当指定条件为_DEBUG,则菜单资源只存于Debug编译环境中。 第3章 对话框与控件 空菜单 新菜单默认ID 图2.2.3 添加菜单资源后开发环境 图2.2.4 “菜单属性”对话框 (4)为菜单ID_MENU1添加一个顶层弹出菜单项:测试(&T),并在该菜单下添加一个子菜单:返回(&R),ID设为ID_TEST_RETURN,如图2.2.5所示。注意:“测试(&T)”中的符号 “&” 用于指定后面的字符(T)是一个助记符。 (5)打开此程序的菜单资源Menu,双击IDR_MAINFRAME,在“查看”菜单的最后添加一个子菜单项:显示测试菜单(&M),将ID设为:ID_VIEW_TEST,见图2.2.6所示。 (6)在CMainFrame类头文件MainFrm.h中的public下添加成员变量: CMenu m_NewMenu; (7)ViewàClassWizard(或按快捷键Ctrl+W)à切换到Message Maps页面,从“Classname”列表中选CMainFrame,分别为菜单项ID_VIEW_TEST和ID_TEST_RETURN添加COMMAND消息映射,使用默认的消息映射函数名,并加代码: void CMainFrame::OnViewTest() { m_NewMenu.Detach(); //使菜单对象和菜单句柄分离. m_NewMenu.LoadMenu(IDR_MENU1); //将菜单从资源装入应用程序中 SetMenu(NULL); //清除应用程序菜单 SetMenu(&m_NewMenu); //设置应用程序菜单 } void CMainFrame::OnTestReturn() { m_NewMenu.Detach(); //使菜单对象和菜单句柄分离 m_NewMenu.LoadMenu(IDR_MAINFRAME); SetMenu(NULL); SetMenu(&m_NewMenu); } 第3章 对话框与控件 图2.2.5 设计新的菜单资源 图2.2.6 在查看下面建一个菜单项 说 明: 代码中,LoadMenu和Detach都是CMenu类成员函数,LoadMenu用于装载菜单资源,而Detach是使菜单对象与菜单句柄分离。在调用LoadMenu后,菜单对象m_NewMenu就拥有一个菜单句炳,当再次调用LoadMenu时,由于菜单对象的句柄已经创建,因而会发生运行错误,但当菜单对象与菜单句柄分离后,就可以再次创建菜单了。SetMenu是CWnd类的一个成员函数,用于设置应用程序的菜单。 (8)编译运行:单击菜单查看à显示测试菜单à菜单上 面出现“测试”à测试à返回à又回到原菜单。 2、添加并设计工具栏按钮 (1)ResourceViewà打开resourcesà打开Toolbar à双击IDR_MAINFRAME,显示出工具栏编辑器, 设计的2个 工具栏按钮 利用工具栏编辑器设计两个工具栏按钮,设计结 果如图2.2.6所示。 (2)双击设计的第1个工具栏按钮,弹出该工 具栏按钮的属性对话框,将该工具栏按钮的ID 号设为:ID_VIEW_TEST,在提示框内输入: 图2.2.6 设计的2个工具栏按钮 显示测试菜单\n显示测试菜单。 (3)再双击设计的第2个工具按钮,弹出该工具栏按钮的属性对话框,将该工具栏按钮的ID号设为ID_TEST_RETURN,在提示框内输入:返回应用程序主菜单\n返回主菜单。 第3章 对话框与控件 (4)再编译运行后,将鼠标移至第1个工具栏按钮处,这时在状态栏上显示出“显示测试菜单”,稍等片刻后,还会出现一个小窗口,也显示“显示测试菜单”字样。再将鼠标移至第2个工具栏按钮处,在状态栏上显示“返回应用程序主菜单”,稍等片刻后,又会出现一个小窗口,显示“返回主菜单”,如图2.2.7所示。先单击第1个按钮,再单击第2个按钮,会各自执行和菜单一样的命令。 3、添加工具栏 (1)在项目工作区的ResourceView页面中,展开Toolbar(工具栏)资源,用鼠标单击 IDR_MAINFRAME不松开,然后按下Ctrl 键,移动鼠标将ID_MAINFRAME拖到 Toolbar资源名称上,松开鼠标和Ctrl键, 这样就复制了工具栏默认资源: ID_MAINFRAME,复制后的资源标识,系 统自动设为:IDR_MAINFRAME1。 (2)用鼠标右键单击工具栏资源 IDR_MAINFRAME1,从弹出的“工具栏属 性”对话框中选择Properties命令,将ID设 为IDR_TOOLBAR1,如图2.2.8所示。 图2.2.7显示测试菜单 图2.2.8 “工具栏属性”对话框 图2.2.9 删除不要的工具按钮 (3)双击IDR_TOOLBAR1,打开工具栏资源,删除(鼠标按住原有按钮不放,将其拖出工具栏即可)不要的工具栏按钮,如图2.2.9所示。 (4)在CMainFrame类头文件CMainFrm.h 的public下,添加成员变量:CToolBar m_wndTestBar;// CToolBar类封装了工具栏的操作 第3章 对话框与控件 (5)在CMainFrame::OnCreate函数中添加以下工具栏创建代码: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; int nRes = m_wndTestBar.CreateEx(this,TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS| CBRS_FLYBY|CBRS_SIZE_DYNAMIC,CRect(0,0,0,0), AFX_IDW_TOOLBAR+10); if(!nRes||!m_wndTestBar.LoadToolBar(IDR_TOOLBAR1)) { TRACE0("Failed to create toolbar\n"); return -1; } ……… m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); m_wndTestBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); DockControlBar(&m_wndTestBar); return 0; } (6)编译运行,结果见2.2.10所示,点击原 工具栏新加的2个按钮和复制的2个工具栏 按钮,都执行和菜单一样的命令。 图2.2.10 运行结果 说 明: (1)CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_GRIPPER| CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_SIZE_DYNAMIC,CRect(0,0,0,0), AFX_IDW_TOOLBAR+10);创建工具栏 第一个参数用于指定工具栏所在的父窗口指针(this表示当前的CMainFrame类窗口指针)。 第二个参数用于指定工具按钮的风格(TBSTYLE_FLAT表示工具按钮是“平面”的)。 第三个参数用于指针工具栏的风格,由于这里的工具栏是CMainFrame的子窗口,因此需要指定WS_CHILD|WS_VISIBLE(见第4章表4.1: 创建子窗口|窗口最初是可见的)。 第3章 对话框与控件 CBRS_TOP 将工具栏放在边框窗口的顶部 CBRS_BOTTOM 将工具栏放在边框窗口的底部 CBRS_GRIPPER 表示工具栏前面有一个“把手” CBRS_SIZE_DYNAMIC 表示工具栏在浮动时,其大小是可以动态改变的 CBRS_NOALIGN: 边框窗口改变大小时,工具栏不重定位 CBRS_TOOLTIPS: 使工具栏提示有效 CBRS_SIZE_FIXED: 工具栏尺寸固定 CBRS_FLOATIONG: 工具栏是浮动的 CBRS_FLYBY: 在状态栏中显示按钮的有关信息 CBRS_HIDE_INPLACE: 不显示工具栏 第四个参数用于指定工具栏四周的边框大小,一般都为0(如:CRect(0,0,0,0))。 最后一个参数用于指定工具栏这个子窗口的标识ID(与工具栏资源标识不同)。 (2)if语句中的LoadToolBar函数用于装载工具栏资源。若CreateEx或 LoadToolBar的返回值为0,既调用不成功,则显示诊断信息“Failed to create toolbar”。 TRACEO是一个用于程序调试的跟踪宏OnCreate函数返回-1时,主框架窗口被清除。 (3)应用程序中的工具栏一般具有停靠或浮动性: m_wndTestBar.EnableDocking使得m_wndTestBar对象可以停靠。 CBRS_ALIGN_ANY表示可以停靠在窗口的任一边。 EnableDocking(CBRS_ALIGN_ANY)调用的是CFrameWnd类的成员函数,用于让工具栏或其它控 制条在主框架窗口可以进行停靠操作。 DockControlBar也是CFrameWnd类的成员函数,用于将指定的工具栏或其他控制条进行停靠。 (4)代码中的AFX_IDW_TOOLBAR是系统内部的工具栏子窗口标识,并将: AFX_IDW_TOOLBAR+1的值表示默认的状态栏子窗口标识。如果在创建新的工具栏时没有指定相 应的子窗口标识,则会使用默认的AFX_IDM_TOOLBAR。这样,当打开“查看”菜单,单击“工具栏” 菜单时,显示或隐藏的工具栏不是原来的工具栏,而是新添加的工具栏。为此,需要重新指定工具 栏子窗口的标识,并使其值等于AFX_IDW_TOOLBAR+10。 4、完善程序代码 (1)调用CFrameWnd类的成员函数ShowControlBar,使程序一开始运行时,将工具栏IDR_TOOLBAR1隐藏起来。 第3章 对话框与控件 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... ... ShowControlBar(&m_wndTestBar,FALSE,FALSE);//关闭测试工具栏 return 0; } 说 明: ShowControlBar函数有3个参数,第1个参数用于指定要操作的工具栏或状态栏指针,第2个参数是一个布尔型,当为TRUE时表示显示,否则表示隐藏,第3个参数用于表示是否延迟显示或隐藏,当为FALSE时表示立即显示或隐藏。 (2)在CMainFrame::OnViewTest和CMainFrame::OnTestReturn函数中添加下列代码: void CMainFrame::OnViewTest() { ... ... ShowControlBar(&m_wndTestBar,TRUE,FALSE); //显示测试工具栏 ShowControlBar(&m_wndToolBar,FALSE,FALSE);//关闭主工具栏 } void CMainFrame::OnTestReturn() { ShowControlBar(&m_wndTestBar,FALSE,FALSE);//关闭测试工具栏 ShowControlBar(&m_wndToolBar,TRUE,FALSE); //显示主工具栏 } (3)编译运行:结果见图2.2.11和2.2.12所示。 图 2.2.11 程序运行的结果 图2.2.12 单击前面的工具按钮后结果 2.3 状态栏 状态栏是一条水平长条,位于应用程序的主窗口的底部,它可以分割成几个窗格, 第3章 对话框与控件 用来显示多组信息。应用程序往往需要把当前的状态信息或提示信息告诉用户,虽然其他窗口(如窗口的标题栏上、提示窗口等)也可显示文本,但它们的功能比较有限,而状态栏能很好地满足应用程序显示信息的需求。 2.3.1 状态栏的定义 在MFC AppWizard创建的SDI或MDI应用程序框架的MainFrm.cpp文件中有一个静态数组indicators数组,它被MFC用做状态栏的定义,见图2.3.1所示。 这个数组中的元素是一些标识常量或是字串资源的ID标识符。默认的indicators数组包含了4个元素,它们是: ID_SEPARATOR 是用于标识信息行窗格的,菜单项或工具栏按钮的许多信息都在这个 信息行窗格中显示。 ID_INDICATOR_CAPS 是用于标识指示器窗格显示出CapsLock键的状态(大写),CapsLock键就是在键盘左边的那个控制大小写转换的键。 ID_INDICATOR_NUM 是用于标识指示器窗格显示出NumLock键状态(数字)。 ID_INDICATOR_SCRL 是用于标识指示器窗格显示出ScrollLock键的状态(滚动)。 就绪 大写 数字 滚动 static UINT indicators[]= { ID_SEPARATOR, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, } 图2.3.1 indicators数组元素与标准状态栏窗口的关系 2.3.2 状态栏的常用操作 Visual C++6.0中可以方便地对状态栏进行操作,如增加窗格、减少窗格、在状态栏 第3章 对话框与控件 中显示文本、改变状态栏的风格大小等,并且MFC的CStatusBar类封装了状态栏的大部分操作。 1、增加和减少窗格 状态栏中的窗格可以分为信息行窗格和指示器窗格2类: (1)若在状态栏中增加一个信息行窗格,则只需要在indicators数组的适当位置增加一个ID_SEPARATOR标识即可。 (2)若在状态栏中增加一个用户指示器窗格,则在indicators数组中的适当位置增加一个在字符串表中定义过的资源ID,其字符串的长度表示用户指示器窗格的大小。 (3)若状态栏减少一个窗格,其操作与增加相类似,只需减少indicators数组元素即可。 2、在状态栏上显示文本 (1)调用CWnd::SetWindowText更新信息行窗格(或窗格0)中的文本。由于状态栏也 是一种窗口,故在使用时可直接调用。若状态栏变量为m_wndStatusBar显示为: m_wndStatusBar。SetWindowText(“消息”)语句将在信息行窗格(或窗格0)内显示“消息” 字样。 (2)手动处理状态栏的ON_UPDATE_COMMAND_UI更新消息,并在处理函数中调用: CCmdUI::SetText函数。 (3)调用CStatusBar::SetPaneText函数更新任何窗格(包括信息行窗格)中的文本,此函 数原型描述如下: BOOL SetPaneText(int nIndex,LPCTSTR lpszNewText,BOOL bUpdate=TRUE); 参 数: 1、nIndex是表示设置的窗格索引(第1个窗格的索引为0) 2、lpszNewText表示要显示的字符串,若bUpdate为TRUE,则系统自动更新显示的结果。 下面我们用两种方法在状态栏中显示鼠标在客户区的位置: 例 2.8 在状态栏的最右边两个窗格中显示出当前鼠标在窗口客户区的位置。 (1)创建一个单文档应用程序(或用上个“多个工具栏”程序),名为:状态栏 第3章 对话框与控件 (2)将项目工作区切换到ClassView页面,展开CMainFrame所有项,双击CMainFrame() 函数,在文档窗口中出现该函数的定义,在它的前面是状态栏数组的定义。 (3)将状态栏indicators数组的定义改为下列代码: static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_SEPARATOR, }; (4)将鼠标移动消息WM_MOUSEMOVE映射到视图类“状态栏View”中。 由于鼠标移动消息WM_MOUSEMOVE 在CMainFrame类映射后不起作用,因此只能映射到视图“状态栏View” 类中。但是,这样一来,就需要更多的代码,因为状态栏对象m_wndStatusBar是CMainFrame类定义的成员变量,因而需要在视图类“状态栏View”中添加访问CMainFrame类的代码。 void CNnView::OnMouseMove(UINT nFlags, CPoint point) { CString str; //获得主窗口指针 CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd; //获得主窗口中的状态栏指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; if(pStatus) { str.Format("X=%d,Y=%d",point.x,point.y);//格式化文本 //这里”1”是更新第2个窗格的文本,而”0”是第1窗格 pStatus->SetPaneText(1,str); } } (5)将MainFrm.h文件中的受保护变量m_wndStatusBar改为公共变量。 (6)在“状态栏View.cpp”视图类执行文件的头部写:#include “MainFrm.h”。 (7)编译运行,结果见图2.3.2所示。 3、改变状态栏的风格 在MFC的CStatusBar类中,有2个成员函数可以改变状态栏风格,他们是: void SetPaneInfo(int nIndex,UINT nID,UINT nStyle,int cxWidth); viod SetPaneStyle(int nIndex,UINT nStyle); 参 数: 第3章 对话框与控件 nIndex表示要设置的状态栏窗格的索引 nID用于为状态栏窗格指定新的ID cxWidth表示窗格的像素宽度 nStyle表示窗格的风格类型,用于指定窗格的外观,例如:SBPS_POPOUT表示窗格是凸起来的,状态栏窗格的风格类型如表2.6所示: (8)在上面的OnMouseMove(UINT nFlags, CPoint point)函数里加: void CNnView::OnMouseMove(UINT nFlags, CPoint point) { ... ... //下面函数第一个参数表示状态栏窗格索引,第二个参数见表2.6所示 pStatus->SetPaneStyle(1,SBPS_POPOUT); str.Format("X=%d,Y=%d",point.x,point.y); //格式化文本 pStatus->SetPaneText(1,str); //更新第2个窗格的文本 } 表2.6 状态栏窗格的风格类型 风格类型 含义 SBPS_NOBORDERS 窗口周围没有3D边框 SBPS_POPOUT 反显边界以使文字“凸出来” SBPS_DISABLED 禁用窗格,不显示文本 SBPS_STRETCH 拉伸窗格,并填充窗格不用的空白空间。但状态栏只能有 一个窗格具有这种风格 SBPS_NORMAL 普通风格,它没有”拉伸”,”3D边框”或”凸出来”等特性 编译运行,见状态栏的第2个窗格凸起来了,如图2.3.3所示 鼠标位置窗格凸起来了 鼠标位置窗格凹下去的 图2.3.2 鼠标的位置显示在状态栏上 图2.3.3 改变状态栏的风格 例 2.9 用与例2.8不同的方法,在状态栏的最右边两个窗格中显示出当前鼠标在窗口客户区的位置。 第3章 对话框与控件 (1)建一个单文档的应用程序,名为:状态栏风格 (2)将项目工作区窗口切换到ResourceView页面,双击”String Table”项的: “String Table”图标,则在主界面的右边出现字符串编辑器。在字符串列表的最后一行的 空项上双击鼠标左键,弹出一个对话框如图2.3.4所示: 图2.3.4 字符串属性对话框 (3)在该对话框中,用户可以指定相应的ID和字符串值,这里加入2个ID和其字符 串资源,即:ID_LEFT(在Caption处写:X=999)和ID_RIGHT(在Caption处写:Y=999),其字 符的多少决定窗格的大小,其结果如图2.3.5所示。 (4)打开MainFrm.cpp文件,将原先的indicators数组修改如下: static UINT indicators[] = { ID_SEPARATOR, // 第一个信息行窗格 ID_SEPARATOR, // 第二个信息行窗格 ID_LEFT, // 第三个窗格 ID_RIGHT, // 第四个窗格 }; 第3章 对话框与控件 图2.3.5 添加的字符串资源 (5)由于ClassWizard不能组织相应的命令更新消息的映射,用户必须手工添加消息处 理函数原型。打开CMyView.h文件,在AFX_MSG内增加消息处理语句,ClassWizard 以后允许用户访问和编辑该代码。 protected: //{{AFX_MSG(CQqView) // NOTE - the ClassWizard will add and remove member functions here. afx_msg void OnUpdateXY(CCmdUI *pCmdUI); // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG 说 明: CCmdUI没有基类,和CCmdTarget派生类的ON_UPDATE_COMMAND_UI句柄一起使用。当用户按下一个菜单,每个菜单项需要知道自己是显示成可以使用的还是不可以使用的,菜单项通过执行ON_UPDATE_COMMAND_UI局柄来实现它。 当菜单被按下,框架寻找并且唤醒ON_UPDATE_COMMAND_UI句柄,每一个句柄调用CCmdUI的一个功能(如Enable and Check),然后框架就相应的现实每一个菜单项。一个菜单项不用改变实现ON_UPDATE_COMMAND_UI的代码就能够用按钮或块捷键代替 (6)打开View.cpp文件,在其消息映射入口处添加消息映射宏函数 BEGIN_MESSAGE_MAP(CQqView, CView) //{{AFX_MSG_MAP(CQqView) // NOTE - the ClassWizard will add and remove mapping macros here. ON_UPDATE_COMMAND_UI(ID_LEFT,OnUpdateXY) ON_UPDATE_COMMAND_UI(ID_RIGHT,OnUpdateXY) // DO NOT EDIT what you see in these blocks of generated code! 第3章 对话框与控件 //}}AFX_MSG_MAP (7)在视图类CMyView.cpp文件的末尾,增加修改状态栏指示器的消息映射函数代码, 当状态栏的窗格需要更新时,应用程序框架自动调用此函数(全用手写)。 void CMyView::OnUpdateXY(CCmdUI *pCmdUI) { pCmdUI->Enable(TRUE); } //使窗格文本能被更新 说 明: CCmdUI类对象方法(成员函数也叫对象方法)如下: 对象方法 作 用 ContinueRouting() 告诉命令发送机构沿着handlers键继续发送当前的消息 Enable() 为该命令激活或关闭用户界面项 SetCheck() 为该命令设置用户界面项的核对状态 SetRadio() 类似SetCheck成员函数,但通过单选组操作 SetText() 为这个命令设置用于用户界面的文本 (8) 用ClassWizard在视图类CMyView中加入WM_MOUSEMOVE(鼠标移动)消息处理函数,并添加下列代码。该函数先获得状态栏对象的指针,然后调用SetPaneText函数更新第三和第四窗格中的文本。 void CMyView::OnMouseMove(UINT nFlags,CPoint point) { CString str; CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd; //获得主窗口指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; //获得主窗口中的状态栏指针 if(pStatus) { str.Format(“X=%d”,point.x);//格式化文本 pStatus->SetPaneText(2,str);//更新第三个窗格的文本 str.Format(“Y=%d”,point.y); pStatus->SetPaneText(3,str);//更新第四个窗格的文本 } } (9)将MainFrm.h文件中的受保护变量m_wndStatusBar变成公共变量。 (10)在视图View.cpp文件的开始处增加语句:#include “MainFrm.h” (11) 编译运行,见图2.3.6,鼠标位置就在状态栏里显示出来了。 第3章 对话框与控件 (12)改变状态栏的风格:将上例OnMouseMove(UINT nFlags, CPoint point)代码修改为: void CQqView::OnMouseMove(UINT nFlags, CPoint point) { CString str; CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;//获得主窗口指针 CStatusBar *pStatus=&pFrame->m_wndStatusBar; //获得主窗口中的状态栏指针 if(pStatus) { pStatus->SetPaneStyle(2,SBPS_POPOUT);//第2个栏 pStatus->SetPaneStyle(3,SBPS_POPOUT);//第3个栏 str.Format("X=%d",point.x); //格式化文本 …………………………………. } CView::OnMouseMove(nFlags, point); } (13)编译运行,见图2.3.7所示,第2第3窗格凸起来了。 鼠标位置窗格凹下去的 鼠标位置窗格凸起来了 图2.3.6 鼠标的位置显示在状态栏上 图2.3.7 设置状态栏的风格 2.4 交互对象的动态更新 用户交互对象是指可由用户操作而产生命令消息的对象,如:菜单项、工具条中的按钮和加速键,每个用户交互对象都有一个唯一的ID号,在发送消息时,该ID号被包含在WM_COMMAND消息中。特别是:菜单项可以有灰色显示,选中和未选中三种状态,而工具栏按钮则可以有禁止和选中状态等。 为了能使用户交互对象动态更新,MFC是通过ClassWizard直接映射交互对象的更新命令消息来实现的。它自动将用户交互对象的ID标识符与ON_UPDATE_COMMAND 第3章 对话框与控件 _UI宏相连接并产生处理更新消息的相应函数。为说明交互对象的动态更新,举例如下: 例2 .10 交互对象的动态更新 (1)创建一个单文档的应用程序,名为:动态更新 (2)àResourceViewàToolbarà选中IDR_MAINFRAME按下Ctrl键不放,移动鼠标将IDR_MAINFRAME拖到Toolbar资源名上,这样就复制了工具栏IDR_MAINFRAME,复制后的资源标识,系统自动设为IDR_MAINFRAME1。 (3)用鼠标右键单击IDR_MAINFRAME1,从弹出的快捷菜单中选择Properties命令, 在弹出的属性对话框中将ID改为IDR_NEWBAR。 (4)删除几个IDR_NEWBAR上的工具按钮,以与IDR_MAINFRAME有区别。 (5)在主框架CMainFrame类的头文件MainFrm.h的protected下,声明变量: CToolBar m_wndNewBar; BOOL m_bNewBar; (6)在CMainFrame::OnCreate中添加下列代码: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if(!m_wndNewBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD|WS_VISIBLE| CBRS_TOP|CBRS_GRIPPER|CBRS_TOOLTIPS|CBRS_FLYBY| CBRS_SIZE_DYNAMIC,CRect(0,0,0,0),AFX_IDW_TOOLBAR+10)|| !m_wndNewBar.LoadToolBar(IDR_NEWBAR)) { TRACE0(“Failed to ctreate newbar\n”); return -1; //fail to create } ... ... m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); m_wndNewBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); 第3章 对话框与控件 DockControlBar(&m_wndNewBar); return 0; } (7)打开菜单资源Menu,双击IDR_MAINFRAME,在“查看”菜单下添加一个菜单项名字写:新工具栏(&N),ID标识符设定为ID_VIEW_NEWBAR。 (8)用MFC ClassWizard在CMainFrame类中添加菜单ID_VIEW_NEWBAR的COMMAND和UPDATE_COMMAND_UI两个消息映射,并在映射函数中添加下列代码: void CMainFrame::OnViewNewbar() { m_bNewBar=!m_bNewBar; ShowControlBar(&m_wndNewBar,m_bNewBar,FALSE);//显示或隐藏工具栏 } void CMainFrame::OnUpdateViewNewbar(CCmdUI* pCmdUI) { m_bNewBar=m_wndNewBar.IsWindowVisible(); //pCmdUI->SetRadio(m_bNewBar); //选中用(•) pCmdUI->SetCheck(m_bNewBar); //选中(√ ) } 说 明: 1、代码中,OnUpdateViewNewbar是ID_VIEW_NEWBAR的更新命令消息的消息映射函数。该函数只有1个参数,它是指向CCmdUI对象的指针。CCmdUI类仅用于ON_UPDATE_COMMAND_UI消息映射函数,它的成员函数将对菜单项、工具栏按钮等用户交互对象起作用,如表2.7说明: 2、调用Create时,还可以指定工具栏的风格,默认风格是: WS_CHILD|WS_VISIBLE|CBRS_TOP创建子窗口|窗口最初是可见的(见第4章表4.1) 3、以WS_为开头表示窗口风格,以SW_开头表示窗口状态的改变(见第4章表4.3) (9)编译运行 1)打开“查看”菜单,可以看到“新工具栏”菜单前面有一个“√”,见图2.4.1所示。单击“新工具栏”菜单,则新创建的工具栏不见了,而“新工具栏”菜单前面的标记√没有了。 2)若将代码中SetCheck改为SetRadio,则“√”变成了“• ”,这就是交互对象的更新效果。 第3章 对话框与控件 单击后√消失,新工具栏不见了 新加菜单前带√ 新加工具栏 图2.4.1 查看下拉菜单 图2.4.2 单击“新工具栏”菜单 表2.7 CComdUI类的成员函数对用户交互对象的作用 用户交互对象 Enable SetCheck SetRadio SetText 菜单项 允许或禁用 选中(√ )或未选中 选定用(•) 设置菜单文本 工具栏按钮 允许或禁用 选定、未选定或不确定 同SetCheck 无效 状态栏窗格(PANE) 使文本可见或不可见 边框外凸或正常 同SetCheck 设置窗格文本 CDialogBar中的按钮 允许或禁用 选中或未选中 同SetCheck 设置按钮文本 CDialogBar中的控件 允许或禁用 无效 无效 设置窗口文本 章 后 实 训 实训1 通用菜单 (1)建一个单文档应用程序,名为:通用菜单 (2)建立菜单:在顶层菜单“帮助”的后面连续建立名为:图形颜色(&C),图形边宽(&W),可选图形(&G),属性选为:√Pop Up的三个菜单,如图1所示。 建立的三个菜单 第3章 对话框与控件 图1 建立的三个顶层菜单 图2 “图形颜色”下建的三个子菜单 (3)按下表在“图形颜色”菜单下面连续建立三个子菜单,如图2所示。 ID 标题(Caption) 提示(Prompt) ID_COLOR_RED 红色(&R) 你选择了红色 ID_COLOR_GREEN 绿色(&G) 你选择了绿色 ID_COLOR_BLUE 蓝色(&B) 你选择了蓝色 按下表在“图形边宽”菜单下面连续建立三个子菜单。 ID 标题(Caption) 提示(Prompt) ID_LINE_SINGE 单线宽(&S) 你选择了单线宽 ID_LINE_THREE 三线宽(&T) 你选择了三线宽 ID_LINE_FIVE 五线宽(&F) 你选择了五线宽 按下表在“可选图形”菜单下面连续建立三个子菜单。 ID 标题(Caption) 提示(Prompt) ID_GRAPH_LINE 直线(&L) 你选择了直线 ID_GRAPH_CIRCLE 椭圆(&C) 你选择了椭圆 ID_GRAPH_RECTANGLE 矩形(&R) 你选择了矩形 (4)在主框架CmainFrame.cpp加入各菜单COMMAND(命令消息)和 UPDATE_COMMAND_UI(更新消息)处理函数: 1)加入“图形颜色”菜单下面三个子菜单: ID_COLOR_RED,ID_COLOR_GREEN,ID_COLOR_BLUE的COMMAND命令消息 和UPDATE_COMMAND_UI更新命令消息。 第3章 对话框与控件 2)加入“图形边宽”菜单下面三个子菜单: ID_LINE_SINGLE,ID_LINE_THREE,ID_LINE_FIVE的COMMAND命令消息和 UPDATE_COMMAND_UI更新命令消息。 3)加入“可选图形”菜单下面三个子菜单: ID_GRAPH_LINE,ID_GRAPH_CIRCLE, ID_GRAPH_RECTANGLE的COMMAND命令消息和UPDATE_COMMAND_UI更新命令消息。 (5)在CmainFrame类添加成员变量: 在项目工作区的“ClassView”选项卡中右键单击“CmainFram”,选择“Add Member Variable”命令,出现“Add Member Variable”对话框,在Variable Type处写:COLORREF ,在Variable Name处写:m_Color 。接之按此方法添加: int m_Thickness 、UINT m_Graph 、UINT m_TagColor三个变量。 (6)在CmainFrame.cpp的构造函数中,为以上添加的4个成员变量赋初值: CMainFrame::CMainFrame() { m_Color=RGB(255,0,0);//初始颜色为红 m_TagColor=ID_COLOR_RED;//初始颜色ID标识符为红 m_Graph=ID_GRAPH_LINE;//初始图形的ID标识符为直线 int m_Thickness=1; //画图形时线的宽度 } (7)创建浮动菜单:Insert ->Resource->出现Insert Resource对话框,选中“Menu”->单击New按钮,见图3所示->双击右边的空白菜单,出现Menu Item Properties对话框,在“General”选项卡中选中“√Pop_Up”,“Caption”(标题)处写:浮动菜单 (8)将该菜单项的标识符“ID_MENU1”改为:IDR_PopUpMenu (9)双击“Menu”下的标识符:IDR_MAINFRAME,鼠标右键单击“图形颜色”菜单,选择“Cope”命令。再双击“Menu”下的IDR_PopUpMenu菜单,右边出现“浮动菜单”,在“浮动菜单”下面空白子菜单上单击右键,选择“Paste”命令。用相同的方法将“图形边宽”和“可选图形”复制到“浮动菜单”的下一个子菜单下,如图4所示。 插入的菜单项 第3章 对话框与控件 图3 插入的菜单项 图4 将加入的菜单拷贝到浮动菜单下 (10)为第(4)步在CmainFrame类添加的各个菜单命令COMMAND消息和UPDATE_COMMAND_UI更新命令消息处理函数加代码: //单击菜单“图形颜色”->“红色”命令后的消息处理函数 void CMainFrame::OnColorRed() { m_Color=RGB(255,0,0);//表示选择了红色 m_TagColor=ID_COLOR_RED;//设置颜色标志为ID_COLOR_RED } //单击菜单“图形颜色”->“红色”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorRed(CCmdUI* pCmdUI) { //判颜色标识是否等于该命令对应的标识符ID_COLOR_RED,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_TagColor==ID_COLOR_RED?1:0); } //单击菜单“图形颜色”->“绿色”命令后的消息处理函数 void CMainFrame::OnColorGreen() 第3章 对话框与控件 { m_Color=RGB(0,255,0); //表示选择了绿色 m_TagColor=ID_COLOR_GREEN; //设置颜色标志为ID_COLOR_GREEN } //单击菜单“图形颜色”->“绿色”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorGreen(CCmdUI* pCmdUI) {//判颜色标识是否等于该命令对应标识符ID_COLOR_GREEN,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_TagColor==ID_COLOR_GREEN?1:0); } //单击菜单“图形颜色”->“蓝色”命令后的消息处理函数 void CMainFrame::OnColorBlue() { m_Color=RGB(0,0,255); //表示选择了蓝色 m_TagColor=ID_COLOR_BLUE; //设置颜色标志为ID_COLOR_BLUE } //单击菜单“图形颜色”->“蓝色”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateColorBlue(CCmdUI* pCmdUI) { //判颜色标识是否等于该命令对应标识符ID_COLOR_BLUE,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_TagColor==ID_COLOR_BLUE?1:0); } //单击菜单“图形边宽”->“单线宽”命令后的消息处理函数 void CMainFrame::OnLineSingle() { m_Thickness=1;//设置为单线宽 } //单击菜单“图形边宽”->“单线宽”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateLineSingle(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于1,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Thickness==1?1:0); } //单击菜单“图形边宽”->“三线宽”命令后的消息处理函数 void CMainFrame::OnLineThree() { m_Thickness=3; //设置为三线宽 } //单击菜单“图形边宽”->“三线宽”命令后,可引发该菜单命令的更新消息处理函数 第3章 对话框与控件 void CMainFrame::OnUpdateLineThree(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于3,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Thickness==3?1:0); } //单击菜单“图形边宽”->“五线宽”命令后的消息处理函数 void CMainFrame::OnLineFive() { m_Thickness=5; //设置为五线宽 } //单击菜单“图形边宽”->“五线宽”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateLineFive(CCmdUI* pCmdUI) { //判断图形边宽变量的值是否等于5,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Thickness==5?1:0); } //单击菜单“可选图形”->“直线”命令后的消息处理函数 void CMainFrame::OnGraphLine() { m_Graph=ID_GRAPH_LINE;//设置图形标识为ID_GRAPH_LINE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.MoveTo(100,310); cdc.LineTo(500,310); cdc.SelectObject(oPen); } //单击菜单“可选图形”->“直线”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphLine(CCmdUI* pCmdUI) { //判图形标识是否等于该命令对应标识符ID_GRAPH_LINE,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Graph==ID_GRAPH_LINE?1:0); 第3章 对话框与控件 } //单击菜单“可选图形”->“椭圆”命令后的消息处理函数 void CMainFrame::OnGraphCircle() { m_Graph=ID_GRAPH_CIRCLE;//设置图形标识为ID_GRAPH_CIRCLE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Ellipse(100,100,200,200); cdc.SelectObject(oPen); } //单击菜单“图形颜色”->“椭圆”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphCircle(CCmdUI* pCmdUI) {//判图形标识是否等于该命令对应标识符ID_GRAPH_CIRCLE,若相等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Graph==ID_GRAPH_CIRCLE?1:0); } //单击菜单“可选图形”->“矩形”命令后的消息处理函数 void CMainFrame::OnGraphRectangle() { m_Graph=ID_GRAPH_RECTANGLE; //设置图形标识为ID_GRAPH_RECTANGLE CPen * oPen, nPen; //创建画笔,颜色由变量m_Color决定,宽度由变量m_Thickness决定 nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); 第3章 对话框与控件 oPen=cdc.SelectObject(&nPen); cdc.Rectangle(350,100,450,200); cdc.SelectObject(oPen); } //单击菜单“可选图形”->“矩形”命令后,可引发该菜单命令的更新消息处理函数 void CMainFrame::OnUpdateGraphRectangle(CCmdUI* pCmdUI) {//判图形标识是否等该命令对应标识符ID_GRAPH_RECTANGLE,若等则在该菜单命令左边显示“Ö” pCmdUI->SetCheck(m_Graph==ID_GRAPH_RECTANGLE?1:0); } (11)在视图类(CMyView)中加入WM_RBUTTONDOWN(鼠标右键按下)的消息处理函数,并加代码: //在视图窗口中鼠标右键单击时的消息处理函数的定义 void CMyView::OnRButtonDown(UINT nFlags, CPoint point) { CMenu popMenu; //声明菜单类对象 //LoadMenu成员函数从一个执行文件中获取一个菜单资源,并把它分配给CMenu对象,该操作若成 //功则继续,否则抛出异常 if(!popMenu.LoadMenu(IDR_PopUpMenu)) ::AfxThrowResourceException(); //GetSubMenu成员函数返回一个指向CMenu对象的指针,若给定位置不存在浮动菜单,返回NULL。 CMenu * pPopUpMenu=popMenu.GetSubMenu(0); //若不存在浮动菜单,将抛出异常 if(pPopUpMenu==NULL) ::AfxThrowResourceException(); this->ClientToScreen(&point); //TrackPopupMenu成员函数在确定位置显示悬浮式弹出菜单,并跟踪此菜单的项目选择过程 pPopUpMenu->TrackPopupMenu(TPM_CENTERALIGN|TPM_RIGHTBUTTON, point.x,point.y,::AfxGetMainWnd()); CView::OnRButtonDown(nFlags, point); } 第3章 对话框与控件 (12)编译运行,见图5和图6所示。在显示图形时,视图窗口背景色被设置为灰色。在视图窗口中,鼠标右键单击,则出现浮动菜单,其实现的结果与顶层菜单是一样的。 图5 在窗口中右键单击鼠标出现浮动菜单 图6 点击菜单“可选图形”->“椭圆” 实训2 多信息状态栏 (1)打开上面的“练习题1—通用菜单”程序,在该程序基础上继续做。 (2)View->Resource Symbols(资源符号)->出现Resource Symbols对话框->单击New->出现New Symbols对话框,在Name文本框中写:ID_STATUSPART1->系统自动在Value(值)文本框中加入一个值,如图7所示。按同样方法,依次加入资源符号名为:ID_STATUSPART2、ID_STATUSPART3和IDS_STATUS_TIMER。 图7 添加资源符号 图8 添加串属性表 (3)在项目工作区的“Resource View”选项卡中展开“String Table”,双击标识符“String Table”,打开字符串编辑窗口,双击最后一个空白行,出现“String Properties”对话框,在“ID”文本框中输入“ID_STATUSPART1”在“标题”文本框中输入“ztguang”,如图8所示。用同样的办法,依次加表1所列出的串属性。 第3章 对话框与控件 表1 串属性表 Control IDs 标 题 ID_STATUSPART2 ztguang ID_STATUSPART3 ztguang IDS_STATUS_TIMER 00:00 (4)在主框架头文件CMainFrm.h的public下定义变量和声明函数。 //定义存放信息的变量 public: CString m_StatusPart1;//存放颜色红、绿、蓝 CString info1;//在状态栏显示红、绿、蓝 CString m_StatusPart2;//存放单线宽、三线宽、五线宽 CString info2;//在状态栏显示单线宽、三线宽、五线宽 CString m_StatusPart3;//存放图形直线、椭圆、矩形 CString info3;//在状态栏显示直线、椭圆、矩形 UINT m_nIDTimer; //每秒钟发送一个消息到应用程序的消息队列,该变量存放时间消息队列 CStatusBar m_StatusBar;//声明类CStatusBar对象 //添加TimerProc()函数的声明 static void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT uIDEvent,DWORD dwTime); //添加ShowStatusBarInfo()函数的声明 void ShowStatusBarInfo();//显示状态条信息 //在消息映射处加入新的消息映射函数声明 protected: //{{AFX_MSG(CMainFrame) afx_msg void OnUpdateTime(CCmdUI *pCmdUI);//声明状态条时间显示函数 //}}AFX_MSG (5)在主框架执行文件CMainFrm.cpp中加代码: 1)消息映射开始处添加: BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) 第3章 对话框与控件 ON_UPDATE_COMMAND_UI(IDS_STATUS_TIMER,OnUpdateTime)//状态条时间显示 //}}AFX_MSG_MAP END_MESSAGE_MAP() 2)修改状态栏内容 static UINT indicators[] = { ID_SEPARATOR,//在状态条左边生成空的状态栏目,用于菜单命令和工具条命令提示信息的显示 ID_STATUSPART1,//显示选择的图形颜色 ID_STATUSPART2,//显示选择的图形边宽 ID_STATUSPART3,//显示选择的图形 ID_INDICATOR_CAPS,//显示键盘键Caps Lock键的状态 ID_INDICATOR_NUM,//显示键盘键NumLock键的状态 ID_INDICATOR_SCRL,//显示键盘键ScrollLock键的状态 IDS_STATUS_TIMER,//显示系统时间 }; 3)在构造函数中为变量赋初值: CMainFrame::CMainFrame() { m_Color=RGB(0,0,0);//m_Color=RGB(255,0,0);//颜色 m_Thickness=0;//m_Thickness=1;//线宽 m_Graph=0;//m_Graph=ID_GRAPH_LINE;//图形ID m_TagColor=0;//m_TagColor=ID_COLOR_RED;//颜色ID m_StatusPart1="ztguang";//颜色栏初值 m_StatusPart2="ztguang";//线宽栏初值 m_StatusPart3="ztguang";//图形栏初值 } 4)在析构函数中加清除(应用程序消息队列)函数 CMainFrame::~CMainFrame() { ::KillTimer(NULL,m_nIDTimer);// 清空时间消息队列 } (6)修改主框架类(CMainFrame)OnCreate(LPCREATESTRUCT lpCreateStruct)消息处理函数: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) 第3章 对话框与控件 return -1; //Create()建立一状态条子窗口,并将它和CstatausBar对象联系在一起,同时按默认 //值设定状态栏的字模和高度 if (!m_StatusBar.Create(this) || //Setlndicators()按照数组indicators中的对应元素的值设定标识符ID值,加栽由每 //个ID所指定的字符串资源,并把字符串设置为标识符的文字 !m_StatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } //发送消息,以便更新状态条时间,该语句每秒钟发送一个消息到应用程序的消息队列,当应用程序 //清空其消息队列时,时间显示将被更新。 m_nIDTimer = ::SetTimer(NULL, 0, 1000, TimerProc); //可将原代码全注释掉 return(); } (7)为主框架执行文件CMainFrm.cpp中的各个菜单命令函数加代码: void CMainFrame::OnColorRed() {//当单击菜单“图形颜色”->“红色”命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart1="红色"; ShowStatusBarInfo(); m_Color=RGB(255,0,0);//颜色 m_TagColor=ID_COLOR_RED;//颜色的ID } void CMainFrame::OnColorGreen() { //当单击菜单“图形颜色”->“绿色”命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart1="绿色"; ShowStatusBarInfo(); m_Color=RGB(0,255,0); 第3章 对话框与控件 m_TagColor=ID_COLOR_GREEN; } void CMainFrame::OnColorBlue() { //当单击菜单“图形颜色”->“蓝色”命令后,就执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart1="蓝色"; ShowStatusBarInfo(); m_Color=RGB(0,0,255); m_TagColor=ID_COLOR_BLUE; } void CMainFrame::OnGraphLine() { //当单击菜单“可选图形”->“直线”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart3="直线"; ShowStatusBarInfo(); m_Graph=ID_GRAPH_LINE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.MoveTo(100,310); cdc.LineTo(500,310); cdc.SelectObject(oPen); } void CMainFrame::OnGraphCircle() { //当单击菜单“可选图形”->“椭圆”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart3="椭圆"; ShowStatusBarInfo(); 第3章 对话框与控件 m_Graph=ID_GRAPH_CIRCLE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Ellipse(100,100,200,200); cdc.SelectObject(oPen); } void CMainFrame::OnGraphRectangle() { //当单击菜单“可选图形”->“矩形”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart3="矩形"; ShowStatusBarInfo(); m_Graph=ID_GRAPH_RECTANGLE; CPen * oPen, nPen; nPen.CreatePen(PS_SOLID,m_Thickness,m_Color); CClientDC cdc(this); CRect rect; GetClientRect(&rect); cdc.FillRect(&rect,CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); oPen=cdc.SelectObject(&nPen); cdc.Rectangle(350,100,450,200); cdc.SelectObject(oPen); } void CMainFrame::OnLineSingle() {//当单击菜单“可选图形”->“单线宽”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart2="单线宽"; 第3章 对话框与控件 ShowStatusBarInfo(); m_Thickness=1; } void CMainFrame::OnLineThree() { //当单击菜单“可选图形”->“三线宽”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart2="三线宽"; ShowStatusBarInfo(); m_Thickness=3; } void CMainFrame::OnLineFive() { //当单击菜单“可选图形”->“五线宽”命令后,就会执行该消息执行函数,首先设置变量的值, //然后调用ShowStatusBarInfo()函数,在状态栏相应的位置显示刚设置的值。 m_StatusPart2="五线宽"; ShowStatusBarInfo(); m_Thickness=5; } (8)在主框架执行文件CMainFrame.cpp中,加入自定义函数及代码:(全用手写) // ShowStatusBarInfo()显示状态栏内容函数:将CString类对象m_StatusPart信息格式化后放入//CString对象info中,调用SetPaneText()函数,用info的内容设置状态条中由第一个参数指定的位置 void CMainFrame::ShowStatusBarInfo() { info1.Format("%s",m_StatusPart1);// m_StatusPart1颜色 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART1),info1); info2.Format("%s",m_StatusPart2);// m_StatusPart2线宽 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART2),info2); info3.Format("%s",m_StatusPart3);// m_StatusPart3图形 m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex(ID_STATUSPART3),info3); } void CMainFrame::OnUpdateTime(CCmdUI *pCmdUI) //状态条时间显示函数 第3章 对话框与控件 { CTime timer = CTime::GetCurrentTime(); char szTimer[6]; int mHour=timer.GetHour(); int mMinute=timer.GetMinute(); //如要按12小时制显示,请去掉下面2条语句的注释 //if (nHour > 12) //nHour = nHour - 12; wsprintf(szTimer, "%i:%02i", mHour, mMinute); //把时间写到Pane m_StatusBar.SetPaneText(m_StatusBar.CommandToIndex (IDS_STATUS_TIMER), LPCSTR(szTimer)); pCmdUI->Enable(); } void CALLBACK CMainFrame::TimerProc(HWND hwnd, UINT uMsg, UINT uIDEvent,DWORD dwTime) { CMainFrame *pMainWnd = (CMainFrame *)AfxGetApp()->m_pMainWnd; ASSERT(uIDEvent == pMainWnd->m_nIDTimer); CCmdUI cui; cui.m_nID = IDS_STATUS_TIMER; cui.m_nIndex = 4; cui.m_pMenu = NULL; cui.m_pOther = &pMainWnd->m_StatusBar; pMainWnd->OnUpdateTime(&cui);//调用OnUpdateTime()函数,更新时间显示 } (9)编译运行,见图9、图10、图11所示。 第3章 对话框与控件 图9 状态栏的初始状态 图10 选择菜单菜单后的状态栏 实训3 自定义工具条,工具条由三种 颜色按钮组成,还能使其放在窗口中的任 何位置,并能随时关闭。 (1)创建一个单文档的应用程序,名为: 浮动工具条 (2)添加工具条资源: Insert->Resource ->选中Toolbae->New,将新工具条ID 改为:IDR_COLORTOOLBAR。 图11 选择“可选图形”的“矩形” (3)单击出现的第1个工具条按钮,用画 刷将其涂成红色,接之将出现的第2个按钮涂成绿色,第三个按钮涂成蓝色。 (4)将三个工具条按钮的ID分别改为: ID_BUTTONRED,ID_BUTTONGREEN,ID_BUTTONBLUE 并将它们的COMMAND消息分别映射到主框架类CMainFrame中。 (5)创建显示/隐藏工具条、显示/隐藏状态栏和显示新建工具条的菜单: Menu->IDR_MAINFRAME->在“帮助”菜单的前面建一个菜单:观察(&V)->选中Pop up->双击下面的子菜单,ID处写:ID_VIEW_TOOLBAR,Caption处写:工具条(&T),prompt处写:显示或隐藏工具栏\n显隐工具栏。 下一个子菜单,ID处写:ID_VIEW_STATUS_BAR,Caption处写:状态栏(&S), prompt处写:显示或隐藏状态栏\n显隐状态栏 再下一个子菜单,ID处写:ID_VIEWCOLORTOOLBAR,Caption处写:新建颜色工具条(&N)。 (6)在主框架类CMainFrame.h头文件的public下添加成员变量: CToolBar *m_pColorToolbar; (7)在主框架类CMainFrame.cpp文件的构造函数中置初值: CMainFrame::CMainFrame() :m_pColorToolbar(0) { } (8)用ClassWizard在主框架类中创建自定义菜单命令ID_VIEWCOLORTOOLBAR的消息映射,并加下列代码: void CMainFrame::OnViewcolortoolbar() 第3章 对话框与控件 { if(0==m_pColorToolbar) { m_pColorToolbar=new CToolBar; if(0==m_pColorToolbar->Create(this)) { MessageBox("创建失败"); return ; } m_pColorToolbar->LoadToolBar(IDR_COLORTOOLBAR);//获得工具条资源 //设置工具条出现时的位置,这里是可将工具条放在任一侧 m_pColorToolbar->EnableDocking(CBRS_ALIGN_ANY); DockControlBar(m_pColorToolbar); } else if(m_pColorToolbar->IsWindowVisible()==TRUE)//该函数是获得给定窗口的可视状态。 ShowControlBar(m_pColorToolbar,FALSE,FALSE); else ShowControlBar(m_pColorToolbar,TRUE,TRUE); } 9、编译运行,单击“观察”菜单见图12所示,单击“新建颜色工具条”菜单,出现图13所示, 见“新建颜色工具条”菜单前面“√”不见了(见图12)。将工具条拖到窗口中见图14所示,单击“工具栏”菜单,“工具栏”前“√”不见了,工具条也不见了,见图15所示,单击“状态栏”菜单和单击拖到窗口的工具条右上角上的“ⅹ”情况如何,读者可试试看。 注 意:新建的工具条在左上角,鼠标要放在工具条按钮的边缘上,按下左键才能拖动,否则若将鼠标放在工具条按钮上是拖不动的。 第3章 对话框与控件 图12 单击“观察”菜单 图13 单击“新建颜色工具条”菜单 此处的原工具条消失了 图14 将工具条拖到窗口中 图15 单击“工具栏”菜单,工具条不见了 说 明: 如果在一个框架窗口中存在多个标准或浮动工具条时,需要利用函数: void DockControlBar(CControlBar *pBar,UINT nDockBarID=0,LPCRECT lpRect= NULL)来确定要控制停靠位置的工具条,它也是CFrameWnd类的成员函数。 参 数: pBar用来指向被控制停靠位置的工具条对象指针。 nDockBarID用来确定工具条停靠在框架窗口的哪条边上,其控制风格的具体取值为: AFX_IDW_DOCKBAR_TOP 停靠在框架窗口的顶部。 AFX_IDW_DOCKBAR_BOTTOM 停靠在框架窗口的底部。 AFX_IDW_DOCKBAR_LEFT 停靠在框架窗口的左边。 AFX_IDW_DOCKBAR_RIGHT 停靠在框架窗口的右边。   当参数nDockBarID的取值为0时,则工具条可以停靠在框架窗口中的任何一个可停靠的边上,其默认的初始位置为窗口顶部。 应用程序中的工具栏的停靠或浮动性有: CBRS_ALIGN_TOP 允许停靠客户区顶部 CBRS_ALIGN_BOTTON 允许停靠客户区底部 CBRS_ALIGN_LEFT 允许停靠客户区左侧 CBRS_ALIGN_RIGHT 允许停靠客户区右侧 CBRS_ALIGN_ANY 允许停靠客户区任意一侧 第3章 对话框与控件 CBRS_FLOAT_MULTI 允许多个控制条在一个单一的小框架窗口中浮动 第3章 对话框与控件 本章我们将了解如何应用Visual C++6.0创建并使用对话框,如何在对话框中添加控件,并将控件连接到代码中;以及如何查找、设置对话框和控件的各种属性等。 对话框是一种弹出式的特殊窗口,几乎所有的Windows应用程序都要借助于对话框来和用户打交道;它主要用来实现应用程序和用户之间的信息交互。对话框上必须有相应的控件,通过控件,对话框可以收集用户的信息或向用户提供信息显示。对话框是一种非常有用的资源,它的主要功能有两个,一是发送消息,二是接收输入;对话框分为两类,一是模式对话框,二是非模式对话框;另外还可以直接建立基于对话框的应用程序。 3.1 对话框的使用 Visual C++6.0提供的对话框编辑器能“可视”地进行设计、编辑对话框,并可用ClassWizard为对话框从CDialog基类中派生一个类;MFC的CDialog类封装了用于对话框的显示、关闭等操作的许多功能函数,例如:DoModal函数用来显示模式对话框并返回用户操作的结果。在Visual C++6.0应用程序中,使用一个对话框的一般过程是: (1)添加对话框资源; (2)设置对话框的属性; (3)添加和布局控件; (4)创建对话框类; (5)添加对话框代码; (6)在程序中调用对话框。 例3.1 模式对话框 所谓模式对话框是指当对话被弹出时,用户必须在对话框中进行相应的操作,在退出对话框之前,对话框所在的应用程序不能继续执行。平常我们所见到的对话框大多是模式对话框。建立模式对话框的步骤如下: (1)建一个SDI(单文档)应用程序,名为:模式对话框 (2)创建对话框模板的过程是:鼠标放到顶层菜单,点击 InsertàResourceà选中Dialogà单击New;在页面右边就显示出了对话框模板。再将鼠标放到控件工具栏上,左键单击静态文本 第3章 对话框与控件 (Aa)控件不放,移动到对话框上的合适位置,放开左键,静态文本(Aa)控件就添加到对话框上了。鼠标右键对准这个静态文本控件单击,便出现快捷菜单,选择属性(properties)单击à出现属性对话框à在标题(Caption)框中写:“新建模式对话框”,关闭属性对话框,“新建模式对话框”字样就显示在这个静态文本控件上。 (3)修改对话框ID:鼠标右键对准对话框的任何位置单击,出现快捷菜单,选择属性(properties)选项,出现属性(Dialog properties)对话框,将对话框的ID设置为IDD_MYDIALOG,标题(Caption)写:模式对话框 (4)给对话框创建类: 双击对话框的任何位置à出现Adding a Class对话框à选Create a new class,单击OKà出现New Class对话框,在Name框中写类名为:CMyDlgà单击OK,这样就建成了对话框类:CMyDlg。 (5)建一个顶层菜单,ID为ID_PUPDIALOG,标题为:模式对话框(&A) 的菜单命令。 (6)将菜单(ID_PUPDIALOG)命令映射到主框架类CMainFrame中并加代码: void CMainFrame::OnPupdialog() { CMyDlg MyDlg; // CMyDlg是CDialog的派生类,这里是定义对话框类对象 MyDlg.DoModal(); //DoModal()是CDialog类成员函数,调用该函数来显示对话框。 } (7)在CMainFrame.cpp文件的前面加文件包含: #include “MyDlg.h” (8)编译运行,见图3.1.1所。只能在对话框中进行相应的操作,其它不能操作。 图3.1.1 模式对话框 图3.1.2 无模式对话框 例3.2 无模式对话框 第3章 对话框与控件 无模式对话框,弹出后,可一直保留在屏幕上,用户可继续在应用中进行其它操作或启动其它应用程序,当需要使用对话框时,只需象激活一般窗口一样激活对话框即可。 (1)建一个SDI(单文档)应用程序,名为:无模式对话框 (2)按上例步骤创建对话框模板,拖过一个静态文本,其属性对话框上的ID不动,在 标题框写:新建无模式对话框 (3)为对话框创建类,类名为:CDlg (4)创建菜单,菜单名字为:无模式对话框(&W),ID为ID_DLG (5)将菜单消息(ID_DLG)映射到视图类(CMyView)中。 (6)在视图类头文件CMyView.h的前面加文件包含:#include “Dlg.h” 在public:下加:CDlg *dlg; //定义对话框类指针对象 在视图类执行文件CMyView.cpp的菜单命令函数中加下列代码: CMyView::OnDlg() { dlg=new CDlg(this); //对话框类的首地址 dlg->Create(IDD_DIALOG1); //从资源中创建一个非模式对话框,Create是基类Cdialog成员函数 //下面参数SW_RESTORE 是用原来的大小和位置显示一个窗口,同时令其进入活动状态  dlg->ShowWindow(SW_RESTORE); //显示窗口 } (7)编译运行,结果见图3.1.2所示。对话框弹出后,一直保留在屏幕上,用户可继续在应用程序中进行其它操作。在应用程序菜单上多次点击“无模式对话框”菜单项,将会在文档窗口出现多个对话框;还可以拖动这些对话框。 说 明: 程序中ShowWindow(SW_RESTORE))是显示窗口函数,其参数宏和其它参数宏的意义如下: SW_RESTORE 用原来的大小和位置显示一个窗口,同时令其进入活动状态  SW_HIDE 隐藏窗口,活动状态给另一个窗口,( 激活另外一个窗口,当前窗口就跑到那个窗口后面了) SW_MINIMIZE 最小化窗口,活动状态给令一个窗口  SW_SHOW 用当前的大小和位置显示一个窗口,同时令其进入活动状态  SW_SHOWMAXIMIZED 最大化窗口,并将其激活  SW_SHOWMINIMIZED 最小化窗口,并将其激活  SW_SHOWMINNOACTIVE 最小化一个窗口,同时不改变活动窗口  SW_SHOWNA 用当前的大小和位置显示一个窗口,不改变活动窗口  SW_SHOWNOACTIVATE 用最近的大小和位置显示一个窗口,同时不改变活动窗口  SW_SHOWNORMAL 与SW_RESTORE相同   第3章 对话框与控件 模式对话框和无模式对话框在用资源编辑器设计和使用MFC ClassWizard创建用户对话框类的方法是一致的,但在对话框的创建和退出的方式是不同的。 (1)在创建时,模式对话框由系统自动分配内存空间,因此,在退出对话框时,对话框对象会自动删除;而无模式对话框则需要用户来指定内存,退出时还需用户自己来删除对话框对象。 (2)在退出时,两种对话框所使用的终止函数不一样。模式对话框通过调用CDialog::EndDialog来终止;而无模式对话框通过调用CWnd::DestroyWindow来终止。 3.2 资源与资源标识 资源是Windows编程中不可缺少的重要组成部分。Visual C++6.0将Windows应用程序中经常用到的菜单、加速键、工具栏、对话框、图标、光标、字符串等都视为“资源”,并将其单独存放在一个资源文件中。每个资源都由相应的标识符来区分,并且可以象变量一样进行赋值。 1、资源的分类 建一个单文档应用程序名为:对话框,然后将项目工作区切换到“ResourceView”页面,展开所有的节点,如图3.2.1所示。可以看出,一个单文档应用程序所使用的资源可分为以下7类: (1)快捷键列表(Accelerator):快捷键列表为一系列组合键的集合,被应用程序用于引发一个动作。该列表一般与菜单命令相关联,用于代替鼠标操作。 (2)对话框(Dialog):为含有按钮、列表框、编辑框等各种控件的窗口。 (3)图标(Icon):图标是代表应用程序显示在Windows桌面上的位图,它同时有32*32像素和16*16像素2种规格。 (4)菜单(Menu):用户通过菜单可以完成应用程序的大部分操作。 (5)字串表(String Table):是应用程序使用的全局字符串或其它标识符。 (6)工具栏按钮(Toolbar):是由一系列具有相同尺寸的位图组成的,它通常与一些菜单命令相对应,用于提高用户的工作效率。 (7)版本信息(Version):版本信息包含应用程序的版本、用户注册码等相关信息。 第3章 对话框与控件 除了上述常用资源类别外,Visual C++6.0应用程序中还可有鼠标、HTML等,也可以自己添加新的资源类别。 2. ID资源标识符 从图3.2.1可以看到每一个资源类别都有 资源类别 一个或多个相关资源,每一个资源均是由标 识符来定义的。当添加或创建一个新的资源 资源标识符 或资源对象时,系统会为其提供默认的名称 并赋值,如:IDR_MAINFRAME等,该定义 保存在Resource.h文件中;当然,用户也可 重新为标识符命名。在Visual C++6.0中,源 程序引用资源和其他用户定义的对象是通过 标识符来进行的。资源标识符的名称规则和其 他标识符相同,但要注意,资源标识符不区分 大小写字母,字符个数不得超过247个。一般 情况下,不同的资源使用不同的资源标识符 前缀。表3.1列出了资源标识符定义的常用前缀。 图3.2.1 单文档程序资源视图 表3. 1 资源标识符定义的常见前缀 标识符前缀 含 义 IDR_ 快捷键或菜单及相关资源 IDD_ 对话框资源 IDC_ 光标资源或控件 IDI_ 图标资源 IDB_ 位图资源 IDM_ 菜单项 ID_ 命令项 IDS_ 字符表中的字符串 IDP_ 消息框中使用的字符串 在Visuan C++中,资源标识符都有一个整数与之对应,取值范围为0~65535。系统为创建的标识符自动赋值,用户也可以修改这些值。在同一个程序项目中,资源标识符名称不能一样,不同的标识符的值也不能一样。如下所示的是:“模式对话框 第3章 对话框与控件 ”程序的Resource.h文件中定义的6个资源标识符,其中标识符IDR_MAINFRAME的值是128。 //Resource.h // Used by 模式对话框.rc #define IDD_ABOUTBOX 100 #define IDR_MAINFRAME 128 #define IDR_MYTYPE 129 #define IDD_MYDIALOG 130 //是新设置的模式对话框的ID值 #define ID_MENUITEM32771 32771 #define ID_PUPDIALOG 32773 选中顶层菜单的ViewàResource Symbols可以打开“Resource Symbols”对话框,如第2章图2.1.5所示。其中列出了已被使用和未被使用的标识符名称及其对应的值,从中可以新建一个标识符:在资源标识符浏览器中选择“New”命令,屏幕出现“New Symbol”对话框,输入标识符名称和数值即可,这样我们就可以在程序中处理这个标识符,例如:第2章例2.3,就是这个问题的实例。 3.3 创建对话框及添加控件 我们在模式对话框和非模式对话框的两节中,已经知道了创建对话框和添加控件的的方法,这里我们再做进一步的介绍。 1、创建对话框 建一个单文档应用程序,名为:创建对话框。 插入一个对话框,其步骤是:InsertàResourceà选中Dialogànew,便在此程序中创建了一个对话框(当然重复此步骤,可以创建多个对话框)。从中可以看出: (1)系统为这个对话框自动赋于一个默认的标识符,名为:IDD_DIALOG1,对话框的默认标题为Dialog,有“OK”和“Cancel”两个按钮,这两个按钮的ID标识符分别为IDOK和IDCANCEL (2)对话框模板资源所在的窗口称为“对话框资源编辑器”,在这里可以通过“控件 工具栏”和“布局工具栏”向对话框添加和布局控件,并可设置对话框的属性。 2、添加和布局控件 将控件工具栏(图3.3.1)上的某几个控件拖到对话框上,这里拖3个静态文本控件和一个按钮控件,如图3.3.3所示。用控件布局工具栏(图3.3.2所示)可以对这些控件进行布局、排序、大小调整、上下、左右对齐、测试等。 第3章 对话框与控件 控件的选择 静态图片 静态文本 编辑框 组框 按钮 复选框 单选按钮 组合框 列表框 水平滚动条 垂直滚动条 旋转按钮 进展条 滑动条 热键 列表视图 树形视图 标签 动画 复合编辑 日期选择 月历 IP地址 用户定制工具 扩展组合框 图3.3.1各个按钮所对应的控件类型 靠齐右边 左右居中 高度相同 测试对话框 靠齐下边 上下间隔相等 显示网格 靠齐左边 上下居中 宽度相同 显示标尺 靠齐上边 左右间隔相同 大小相同 图 3.3.2 控件布局工具栏 默认标识符 第3章 对话框与控件 添加的控件 控件工具栏 布局工具栏 对话框模板 图3.3.3 添加对话框资源后的开发环境界面 说 明: (1)添加控件的方法: 1)左键单击控件栏中某控件不放,移动到对话框的某位置,放开鼠标,控件就出现在对话框中。 2)左键单击控件栏中某控件之后松开,移动鼠标到对话框上某位置(鼠标箭头变成“+”字形状),单击左键,控件就出现在这个位置上。 (2)控件的选取:控件的删除、复制和布局操作一般都要先选取控件。 1)单击某个控件,则某个控件被选取。 2)先在对话框内按住鼠标左键不放,移动鼠标,拖出一个大的方虚框,然后释放鼠标,则被该虚框 所包围的控件都将被选取。 3)先按住Shift键不放,然后用鼠标选取控件,直到所需要的多个控件被选取之后,再放开Shift键,则这些控件被选取。 4)控件被选取时,鼠标点击对话框,则取消控件的选取。若多个控件被选取,再选取其中一个,则其中一个保留选取,其他控件被取消选取。 5)一旦单个控件被选取后,其四周由选择框包围着,选择框上还有几个(一般是8个)蓝色实心小方块,拖动它可改变控件的大小;多个控件被选取后,其中只有一个控件的选择框有几个蓝色实心小方块,这个控件称为主要控件,如图3.3.3的Button1(按钮控件)。而其他控件选择框的小方块是空心的,如图3.3.3的Static(3个静态控件)。多个控件的上下、左右对齐、间隔相等及大小一致等,都以主控件为准。 (3)控件的移动、复制和删除 1)当单个控件或多个控件被选取后,按方向键或用鼠标拖动控件的选择框可移动控件。 2)若在鼠标拖动过程中还按住Ctrl键,则复制控件。 第3章 对话框与控件 3)若按Del键可将选取的控件删除。 (4)测试对话框 “编排”菜单下的Test 命令或布局工具栏上的测试按钮,是用于模拟所编辑的对话框的运行情况,帮助用户检验对话框是否符合用户的设计要求以及空间功能是否有效等。 与布局工具栏相对应的菜单命令在Layout(编排)菜单下,而且大部分命令均有相应的快捷键,如图3.3.4所示。表3.2还列出了“编排”菜单命令的快捷键与功能描述。“编排”菜单不是在Visual C++6.0开发环境一开始就出现的,而是随着对话框编辑器的打开而显示的。 表3.2 “编排”菜单命令的快捷键及功能描述 菜单命令 快捷键 功能描述 Align --- 对全控件 Space Evenly --- 空间分布 Make Same Size --- 使多个控件具有相同尺寸 Arrange Button --- 按钮布置 Center in Dialog --- 在对话框内居中 Size to Content Shift+F7 按内容定义尺寸 Auto Size --- 自动大小 Flip --- 翻转 Tab Order Ctrl+D 设置Tab键次序 图3.3.4 编排菜单命令项 Guide Setings --- 网格、标尺等辅助工具的设置 Test Ctrl +T 测试对话框性能 3、设置对话框的属性 打开前面创建的“创建对话框”应用程序: (1)点击顶层菜单的ViewàProperties(2)按Alt+Enter(3)用鼠标右键单击对话框模板àProperties,这3种方式都能弹出对话框的属性框,见图3.3.5所示。 第3章 对话框与控件 图3.3.5 对话框属性界面 属性框中有:General(一般属性)、Styles((风格)、More Styles(更多风格)、Extended Styles(扩展风格)、More Extended Styles(更多扩展风格)等部分。下面介绍常用的General属性,如表3.3所示。将新加的对话框的属性(General)进行如下修改: (1)改对话框ID标识符为:IDD_MYDIALOG (2)改对话框标题(Caption)为:我的对话框 (3)单击Font…(字体)按钮,将字体改为“宋体,9”,以使自己的对话框和Windows中的对话框保持外观上的一致。 表3.3 对话框的General属性 项目 说明 ID框 修改或选择对话框的标识符名称(如改成:IDD_MYDIALOG) Caption(标题栏) 输入对话框的标题名称,中英文均可(如:我的对话框) Font…(字体按钮) 单击此按钮可选择字体的种类(如宋体)以及字体的大小(如9号) Xpos/Ypos 对话框左上角在父窗口中的X、Y坐标,都为0时表示居中 菜单框(Menu) 默认值为无,当对话框需要菜单时,输入或选择指定的菜单资源 Class name 默认值为无,它提供C/C++语言编程时所需要的对话框类名,对 MFC类库的资源文件来说,该项不被激活。 4、为对话框添加类 对准对话框的任意非控件位置双击 鼠标,将弹出图3.3.6所示的对话框,询 问是否为对话框资源创建一个新类,选 中Crealte a new class à单击OKà弹出图 3.3.7所示的对话框à写入一个新类的名 字如:CMyDlg(注意:类名应以大写的 “C”开头),下面的基类Base class和ID 标识符内容一般不改。 图 3.3.6 “Adding a Class”对话框图 第3章 对话框与控件 5、添加映射消息 ViewàClassWizardà出现 “MFC ClassWizard”对话框à在 Class name处选择CMyDlg à在 Object IDs里选定IDC_BUTTON1à 选中BN_CLICKED(单击该按钮)消 息à Add Functionà OKàEdit Code 6、添加代码 在MyDlg.cpp文件的OnButton1() (按钮消息映射)函数中写: void CMyDlg::OnButton1() 图3.3.7 “New Class”对话框 { MessageBox(“欢迎进入对话框的设计!”); } 这时运行还不见对话框被弹出:还需进行下一步。 7、在程序中调用对话框 由于对话框的代码是以类为模块来设计的,使用时需要在程序中加入该类的头文件,并定义一个类对象,然后就可以使用该类的相关成员。 打开项目工作区àFileViewà打开应用程序的执行文件“创建对话框.cpp”,在其前面加:#include “MyDlg.h” 在InitInstance函数体中的return TRUE语句之前添加下列代码: BOOL CMyApp::InitInstance() { ….. …… CMyDlg dlg; // 定义对话框类对象 // DoModal()函数是负责对话框的显示和终止 dlg.DoModal(); return } 8、编译运行,对话框被弹出,单击按 钮控件“Button1”则出现小对话框, 上面有“欢迎进入对话框的设计”的 图3.3.8 弹出对话框并单击按钮 字样,如图3.3.8所示。 注意:本例建的CMyDlg类及以后在各个项目中建立的类,在文档、视图、主框架类中都可同样使用。 第3章 对话框与控件 9、在按钮控件上直接显示信息 (注意:将上面添加的按钮(Button1)控件拉得长一些,以能显示出写入的全部信息)用ClassWizard在CMyDlg类中,加WM_INITDIALOG映射消息,步骤是: ViewàClassWizardà在Class name框中置CMyDlgà在Messages框中找到WM_INITDIALOG选中à 单击AddFunctionàEdit Code,便在CMyDlg.cpp文件中建立了BOOL CMyDlg::OnInitDialog()函数,在此函数中加代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); CStatic *pWnd=(CStatic*)GetDlgItem(IDC_BUTTON1);//得到IDC_BUTTON1控件句柄 pWnd->SetWindowText("直接在按钮上显示文字! ");//将这些字符显示在Button1控件上 } 10、编译运行!如图3.3.9所示,在按钮控件(Button1)上看到“直接在按钮上显示文字”字样,而且再单击按钮控件,又显示出一个小对话框,上面写着“欢迎进入对话框的设计!”。 如果我们象“模式对话框”和“非模式对话框”那样,用编辑器直接在控件的属性标题处写:“直接在按钮上显示文字”,就简单多了。而像我们这种用程序语句将文字写 控件上,比上面做法要复杂得多,因此一般不用这种方法。 说 明: 1、CStatic:是MFC的静态文本控件类。 2、GetDlgItem:是CWnd的成员函数,通过该函数可以得到对话框中某ID号对应的控件的句柄。 参数是控件的ID标识符,这里的ID标识符是 BUTTON1(该对话框上的按钮标识符)。 3、SetWindowText:是CWnd的一个成员函数, 用于设置窗口(控件)的文本内容。由于控件类 是CWnd的子类(派生类),因此可以使用基类的 SetWindowText来改变按钮控件的显示内容 11、对话框资源类型 打开前面的“创建对话框”应用程 图3.3.9 在按钮上直接显示信息 序,选择顶层菜单的Insert(插入)à 第3章 对话框与控件 Resource或按Ctrl+R,出现“Insert Resource”对话框à选中Dialog项à单 击其左边的“+”号,展开对话框资源 的不同类型,如图3.3.10所示,表3.4 列出了各种类型对话框资源的不同用途。 其中,“New(新建)”按钮用于创 建一个由“资源类型”列表中指定类型 的新资源,“Import(导入)”按钮是用于 图 3.3.10 对话框资源类型 将外部已有的位图、图标、光标或其他 定制的资源添加到当前应用程序中,“Custom(定制)”按钮用于创建“资源类型”列表中没有的新的类型资源。 表3. 4 对话框资源类型 类型 说明 IDD_DIALOGBAR 对话条,往往和工具条放在一起 IDD_FORMVIEW 一个表状风格的对话框,用于无模式对话框或视图类 IDD_OLE_PROPPAGE_LARGE 一个大的OLE属性页 IDD_OLE_PROPPAGE_SMALL 一个小的OLE属性页 IDD_PROPPAGE_LARGE 一个大属性页,用于属性对话框 IDD_PROPPAGE_MEDIUM 一个中等大小的属性页,用于属性对话框 IDD_PROPPAGE_SMALL 一个小的属性页,用于属性对话框 3.4 控件的创建和使用方法 控件是Windows图形用户界面的主要组成部分之一,用户通过操作控件对象完成与应用程序之间的交互。控件的使用集中体现了Windows系统面向对象的特点。在应用程序中调用控件不仅简化了编程,还能完成常见的各种功能。为了更好地发挥控件的作用,用户还必须理解和掌握控件的属性、消息以及创建和使用方法。控件的外观和功能各不相同,但它们都是以子窗口的形式存在的,在控件创建、消息处理和控制方面有许多相似之处。 3、4、1 控件的创建方法 控件的创建方法有2种: (1)在对话框摸板中用编辑器指定控件,也就是说,将控件的父窗口指定为对话框,如前面“创建对话框”应用程序的BUTTON1按钮,是直接用鼠标拖过来的办法添加的。 第3章 对话框与控件 (2)将控件看做任意一窗口的子窗口,并通过调用相应的Create函数来创建。 该方法是用程序语句来创建一个控件,比较复杂,且不能发挥对话框编辑器的可视化的优点,因此,一般不用。但为了了解该创建方法,我们举一例如下: 例3.3 通过调用Create函数来创建一个控件 (1)建一个单文档的应用程序,名为:创建控件 (2)插入(Insert Resource)一个对话框,并为这个对话框建类,名为:CMyDlg (3)在应用程序的执行文件(创建控件.cpp)头部加: #include “MyDlg.h” 并在该文件InitInstance()函数的return TRUE;语句前加下列代码: BOOL CMyApp::InitInstance() { … … CMyDlg dlg; dlg.DoModal(); //DoModal()函数是负责对话框的显示和终止。 return TRUE; } (4)在对话框CMyDlg类的头文件MyDlg.h的 public下添加一个按钮类的指针变量: CButton *m_btnWnd; (5)为CMyDlg类加WM_INITDIALOG映射消息,并添加代码: BOOL CMyDlg::OnInitDialog() { ----- //控件是作为对话框的一个子窗口来创建的 m_btnWnd=new CButton(); //构造按钮控件的指针,CButton首地址送给指针变量。 m_btnWnd->Create(“你好”,WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, CRect(20,20,120,60),this,201); //创建按钮 CFont *font=this->GetFont(); //获取对话框的字体 m_btnWnd->SetFont(font); //设置控件上的字体 return TRUE; } (7)编译并运行,结果见图3.4.1所示,对话 框上出现一个按钮控件,上面写着“你好”,它 就是用程序语句在对话框上加的按钮控件。 说 明: 第3章 对话框与控件 1、由于OnInitDialog函数在对话框初始化时被调用, 图 3.4.1 用编程方法创建的控件 因此将对话框中的一些初始化代码都添加在此函数中。 2、代码中Create函数用于创建一个控件,该函数的:第1个参数用于指定该控件的标题,这里是“你好”。第2个参数用于指定该控件的风格。第3个参数用于指定该控件在父窗口的位置和大小。第4个参数用于指定父窗口的指针,这里是“this”。第5个参数是指定该控件的标识值,这里是“201”。 3、控件的风格: WS_CHILD、WS_VISIBLE和BS_PUSHBUTTON都是Visual C++6.0系统内部定义的标识符,可以直接引用。由于按钮是作为对话框的一个子窗口来创建的,因此其风格必须有(见第四章表4.1):(1)WS_CHILD创建子窗口(2)WS_VISIBLE使控件可见(3)BS_PUSHBUTTON表示创建的是按键按钮。 3.4.2 基于对话框的应用程序 可以直接利用MFC AppWizard应用程序向导,创建一个基于对话框的应用程序,这种程序运行后首先出现一个对话框。 (1)创建一个基于对话框的应用程序,名为:控件的创建,过程如下: 打开VC系统àFileàNewàMFC AppWizard(exe)àLocation处写路径名àProject name处写文件名:控件的创建àOKà选中Dialog based,见图3.4.2所示àFinishàOK这就建立了一个基于对话框的程序。 (2)将项目工作区切换到ClassView页 面,并展开CMyDlg类à右键对准 CMyDlg单击à便弹出如图3.4.3所示的 快捷菜单à从快捷菜单中选择Add Member Variable(添加成员变量)à出现 Member variable name对话框,在Variable Type处写:CButton 在Variable Name处 写:m_btnWndàOK。通常以”m_”作为 变量的开头,表示”成员” 的意思,如图 3.4.4所示。 图3.4.2 创建一个基于对话框应用程序 第3章 对话框与控件 加成员变量的快捷菜单 图3.4.3 弹出加成员变量(函数)的快捷菜单 (3)还是在ClassView页面,展开CMyDlg类,找到OnInitDialog函数双击它,在右侧便出现这个函数,在它的return TRUE语句前面加如下代码: BOOL CMyDlg::OnInitDialog() { … … … m_btnWnd.Create("你好", WS_CHILD|WS_VISIBLE| BS_PUSHBUTTON, CRect(20,20,120,60),this,201); //创建控件 //获取对话框的字体 CFont *font=this->GetFont(); m_btnWnd.SetFont(font); //设置控件字体 return TRUE; 图 3.4.4 添加成员变量 } (4)编译运行,结果如图3.4.1所示一样。 3、4、3 控件的消息及消息映射 应用程序可以向控件发送消息,控件也可以向应用程序发送消息,这样的消息被称为通知消息。当控件的状态发生改变(例如用户利用控件进行输入)时,子窗口控件通过向其父窗口(应用程序窗口)发送WM_COMMAND消息进行通信。消息WM_COMMAND的lParam参数中包含了控件句柄,wParam参数的低字节中含有控件标识符,wParam参数的高字节则为通知代码。各控件通知代码不一样,例如,按钮被单击产生的通知代码是BN_CLICKED,用户在编辑框中更改了输入数据会产生通知代码EN_CHANGE等。 第3章 对话框与控件 我们在3.3节的第5步,已为BUTTON1按钮添加了映射控件消息,当我们单击BUTTON1按钮时,系统会弹出一个小对话框。而在第9步则实现了直接将信息发送到BUTTON1控件上。为了进一步掌握控件的消息及消息映射,我们再作如下讨论。 例3.4 映射控件消息 (1)建一个基于对话框的应用程序,名为:映射控件消息 (2)将项目工作区窗口切换到ResourseView页面,打开Dialog,双击IDD_MY_DIALOG (3)删除对话框上面的一个原有控件“TODO……”,添加一个按钮控件Button1,保留其默认属性。 (4)ViewàClassWizardàClass name处置CMyDlgà在IDs列表中选择IDC_BUTTON1(这是添加按钮后,系统自动为这个按钮设置的默认标识符)à在Messages框中选择BN_CLICKED消息->单击”Add Function”按钮或双击BN_CLICKED消息,出现”Add Member Function”对话框,在这里可以输入成员函数的名称,我们取系统默认的函数名为:OnButton1,如图3.4.5所示。 说 明: 1、不同资源对象(控件、菜单等命令)所产生的消息是不相同的。例如按钮控件IDC_BUTTON1的消息有2个:BN_CLICKED和BN_DOUBLECLICKED,分别表示当用户单击或双击该按钮时产生的消息。 2、一般不需要对对话框中的“OK”(确定)和“Cancel”(取消)进行消息映射,系统已自动设置了这2个按钮的动作,当用户单击这2个按钮都将自动关闭对话框,而“OK”按钮动作还使得对话框数据有效。 第3章 对话框与控件 图3.4.5 添加按钮消息映射函数 (5)单击“OK”按钮,在MFC ClassWizard的”Member functions”列表中将列出新增加的成员函数,选择此函数,单击“Edit Code” 按钮(或直接在函数名上双击鼠标),开发环境 的文档窗口中将自动打开该函数所在的源代 码文件,并定位到该函数的实现代码处。在 此成员函数中添加下列代码: void CMyDlg::OnButton1() { MessageBox("你按下了\"Button1\"按钮!"); 图 3.4.6 映射控件对话框 } (6)编译运行,单击按钮控件,便出现一个小对话框,上面写“你按下了“Button1”按钮。如图3.4.6所示 例3.5 映射控件通用消息 上述过程是映射一个控件的某一 个消息,事实上也可以通过 WM_COMMAND消息映射来处理 一个或多个控件的通用消息,步骤 如下: (1)打开前面“映射控件消息”程序 (2)Viewà在“Class name”列表中 选择CMyDlgà在IDs列表中也选择 图3.4.7 添加OnCommand函数重载 CMyDlgà在Messages框中找到 OnCommand选中àAdd FunctionàEdit Code,这样在CMyDlg类中就建好了OnCommand消息函数,如图3.4.7所示。由于OnCommand函数是一个用于处理WM_COMMAND消息的虚函数,因此这里添加的OnCommand函数事实上是一个在类中实际调用的函数,可称为 第3章 对话框与控件 “实例函数”。这样的映射操作,可以称之为“对虚函数OnCommand的重载”。 (4)在OnCommand函数中添加下列代码: BOOL CMyDlg::OnCommand(WPARAM wParam, LPARAM lParam) { WORD nCode=HIWORD(wParam);//用于获取32位数值中的高位,通知代码 WORD nID=LOWORD(wParam);//用于获取32位数值中的低位,控件的ID标识值 if((nID==201)&&(nCode==BN_CLICKED))//判控件的ID标识符值与通知代码 MessageBox("你按下了\"你好\"按钮!"); if((nID==IDC_BUTTON1)&&(nCode==BN_CLICKED)) MessageBox("这是在OnCommand处理的结果!"); return CDialog::OnCommand(wParam, lParam); } (5)编译并运行,结果见图3.4.8和图3.4.9所示。 图3.4.8 第1次点击BUTTON1结果 图3.4.9 再按小对话框上的”确定”的结果 注 意:代码中if语句中的201是前面用Create创建按钮时指定的标识值。 说 明: 1、WORD是16位无符号整数。 BN_CLICKED是当用户单击按钮产生的消息。 EN_CHANGE是编辑框中的文本被改变时发出的消息。 WPARAM wParam此参数所代表的是虚拟键码,此码可用于检测究竟是哪一个键被按下。 LPARAM lParam是由32位组合而成,除部分保留未使用外,可以将其他部分分成6个域予以说明 第3章 对话框与控件 31 30 29 28 27 26 25 24 23 … 16 15 … 0 内容码 OEM扫描码 重复次数 原来的按键状态 扩展键标志 转换状态 (1)重复次数:是指键盘被按下或是放开按键的次数。 (2)OEM扫描码:是由键盘的硬件产生。 (3)扩展键标志:如果按的是增强键,如Alt、Ctrl等标志值是1,一般Windows应用程序很少用此域。 (4)内容码:主要用于检测是否按下Alt键。 (5)转换状态:是指如果有按键(Down)发生,此域为0;如果是放开(Up)发生,则此域为1。 还有:由于该项目中“Button1”按钮的BN_CLICKED消息处理同时存在2种函数,即OnButton1和OnCommand,你单击Button1按钮,系统先执行OnCommand函数代码,然后执行OnButton1函数代码。因OnCommand函数的最后一句代码有“return CDialog::OnCommand(wParam,lParam);”,它的作用是将控件的消息交由对话框其它函数处理。所以该例中,你点2次Button1按钮,就出现你设置的BUTTON1的不同的2个信息。由于用Create创建的控件无法用MFC ClassWizard直接映射其消息,因此上述方法弥补了ClassWizard的不足,使用时要特别注意。 2、除一些常用的数据类型外,Windows还提供一些宏来表示一些基本数据类型,如: LOBYTE、HIBYTE分别用于获取16位数值中的低位和高位字节。 LOWORD和HIWORD分别用于获取32位数值中的低位和高位字。 MAKEWORD是将2个16位无符号值结合成一个32位无符号值。 下面通知消息是所有Windows控件所共有的: NM_CLICK 在控件中单击鼠标左键按钮 NM_DBLCLK 在控件中双击鼠标左键按钮 NM_RDBLCLK 在控件中双击鼠标右键按钮 NM_RETURN 当控件具有输入焦点时按下ENTER键 NM_SETFOCUS 控件得到输入焦点 NM_KILLFOCUS 控件失去输入焦点 NM_OUTOFMEMORY 没有足够的内存使控件 第3章 对话框与控件 3.4.4 控件的数据交换(DDX)和数据效验(DDV) 为了能让用户直接方便地操作一个控件,MFC采用了独特的DDX和DDV技术,DDX将数据成员变量与对话框模板内控件相连接,这样使得数据在控件之间很容易传输。DDV用于数据的校验,例如它能自动校验数据成员变量数值的范围,并发出警告。使用MFC ClassWizard可以很容易地定义一个控件的成员变量及其数据范围。 例3.6 控件的数据传输 (1)打开上例“映射控件消息”程序: àViewàClassWizardàMember Variables à在Class name框中选定CMyDlg,然后 在Control IDs列表中à 选中 IDC_BUTTON1à单击AddVariable (或双击鼠标左键)à弹出 Add MemberVariable对话框à在 Member variable name处写好成员变量 名:m_MyBtn,Variable Type(变量类型) 图3.4.10 添加成员变量界面 为:CButton,在 Category(种类)为: Control见图3.4.10所示àOKà见图3.4.11就建好了成员变量。 (2)再向对话框加一个编辑控件,ID默认,加成员变量为:m_strEdit,类型为CString , 在Maximum characters(最大字符串个数)框写:20,目的是在按钮BUTTON1和编辑EDIT1 控件之间进行数据交换,交换的最大字符串个数是20。 (3)将CMyDlg::OnButton1()修改成: void CMyDlg::OnButton1() { UpdateData();//数据从控件向相关联的成员变量复制 m_MyBtn.SetWindowText(m_strEdit); //编辑框的内容在按钮上显示 //用下面语句代替上面语句也行,单击Button1按钮,此按钮上直接显示的就是“欢迎”。 //GetDlgItem(IDC_BUTTON1)->SetWindowText(“欢迎”); } (4)编译运行,当在编辑框输入 “你好”后,单击Button1按钮,则 第3章 对话框与控件 该按钮的名称就变成了编辑框中的 内容“你好”了(可将该程序中的 OnCommand函数内容注释掉,不然得 再单击一下确定),见图3.4.12所示。 说 明: 1、UpdateData(); 默认参数值是TRUE (真),数据从控件向相关联的成员变量 复制,若UpdateData(FALSE),数据由 图3.4.11 添加数据成员后的界面 控件相关联的成员变量向控件传输。 2、Category框内可选择Value或Control两种类型。Control所对应的变量类型就是MFC为该控件封装的控件类。Value所对应的是数值类型。不同控件所提供的关联的数值类型各不同,例如:对于编辑框来说,Variables type中的数值类型有:Cstring(字符串),int,UINT(32位无符号整数),long(32位带符号整数), DWORD (32位无符号整数,段地址和相关的偏移) ,float,double,BYTE(8位无符号整数),short,BOOL。 3、在DDV/DDX技术中,允许用户为同一个控件 关联多个变量,但必须保证这些变量名是互不相同 的,且这些变量在同一个类型中不能有多个变量,即 在Value和Control类型中各自只能有一个关联变量。 4、如果添加的关联变量是一个数值类型,则 “MFC ClassWizard”对话框的“Member Variables” 页面下方还要求用户输入变量的范围,这就是控件的 数据校验设置。例如本例的(2)添加编辑控件的 图3.4.12 控件的数据交换结果 成员变量之后,还在Maximum characters 框写入:20 5、添加BUTTON1和EDIT1控件的成员变量后,打开“映射控件消息Dlg”类源文件,可以发现MFC ClassWizard对上述操作进行了以下3个方面的工作(带阴影部分): (1)在“映射控件消息Dlg.h”文件中,系统已经添加了控件关联变量的声明即: class CMyDlg : public CDialog { … … //{{AFX_DATA(CMyDlg) enum { IDD = IDD_MY_DIALOG }; CButton m_MyBtn; CString m_strEdit; 第3章 对话框与控件 //}}AFX_DATA } (2)在“映射控件消息Dlg.cpp”文件的构造函数里见到系统已经添加好的: CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/) : CDialog(CMyDlg::IDD, pParent) { //{{AFX_DATA_INIT(CMyDlg) m_strEdit = _T(""); //}}AFX_DATA_INIT …. } (3)在“映射控件消息Dlg.cpp”文件的DoDataExchange函数体内,系统已经添加了控件的DDX/DDV代码,它们都是一些以DDV_或DDX_开头的函数调用。 void CMyDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyDlg) // IDC_BUTTON1和其成员变量m_MyBtn进行数据交换。 DDX_Control(pDX, IDC_BUTTON1, m_MyBtn); // IDC_EDIT1和其成员变量m_strEdit进行数据交换。 DDX_Text(pDX, IDC_EDIT1, m_strEdit); DDV_MaxChars(pDX, m_strEdit, 20); //校验m_strEdit的最大字符个数不超过20 //}}AFX_DATA_MAP } 说 明: 1、GetDlgItem()是CWnd的成员函数,通过该函数可以得到对话框中某ID号对应的控件的句柄。参数是控件的ID标识符,这里是IDC_BUTTON1(该按钮的ID标识符)。 2、SetWindowText是CWnd的一个成员函数,用于设置窗口(控件)的文本内容。 3.4.5 控件的通用属性 在控件的属性对话框中含有许多属 性:General(一般属性)、Styles(控件的 风格)、Extended Styles(控件的扩展风格)。 Styles和Extended Styles是用来设定控 件的外观的、辅助功能的。不同控件具 第3章 对话框与控件 有不同的风格和扩展风格,但控件的一 般属性基本相同,它通常有标识符、标 图3.4.13 按钮控件的属性对话框 题框等内容,表3.5列出了控件的通用 General属性,图3.4.13是按钮控件(BUTTON)的属性对话框。 表3.5 控件的General通用属性 项 目 说明 ID: 控件的标识符,每种控件都有默认的ID,例如按钮控件为IDC_BUTTON1 Caption: 控件的标题,大多数控件都有默认的标题,例如按钮控件为Button1 Visible: 指定控件初始化时是否可见 Group: 指定控件组中的第一个控件,如果该项被选中,则此控件后的所有控件均被 看成一组,成组的目的是可以让用户键盘方向键在同一组控件中进行切换 Help ID: 若该项被选中,则为该控件建立一个上下文相关的帮助标识符 Disabled: 指定控件初始化时是否禁用 TabStop: 若该项被选中,则用户可以使用Tab键来选择控件 3.5 常用控件 根据控件的使用情况将控件分为两种类型:常用控件和高级控件。常用控件包括:静态控件、按钮、滚动条、编辑框、列表框和组合框等六种控件,高级控件包括:上下控件、滑标控件、工具栏、状态栏、工具提示控件以及多文本编辑框等,这里我们重点介绍常用控件,见表3.6列出了我们经常用到的控件类。图3.5.1用到的控件有静态图片控件、按钮、静态控件、编辑框、组合框、滚动条等。 静态图片控件 按钮 静态控件 第3章 对话框与控件 编辑框 组合框 滚动条 编辑框 图3.5.1 控件示例 3.5.1 静态控件 静态控件是用于显示一个字符、框、矩形、图标、位图或增强的图元文件,它可以用做标签、框或用于分隔其它的控件。一个静态控件一般不接受用户输入,也不产生通知消息。 在对话框编辑器的控件工具栏中,属于静态控件的有:静态文本(Static Text)、组框(Group Box)、图片控件(Picture)三种。静态图片控件的General和Style属性对话框如图3.5.2和3.5.3所示。表3.7列出了其一般属性和风格的各个项的意义。我们可以选择Type(图片类型)、Image(图象资源)两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控件中。另外,用户还可设置其风格来改变控件的外观以及图像在控件的位置等,见表3.8列出的“Styles”风格。 表3. 6 常用控件类 控件名称 MFC类 功能描述 静态控件 CStatic 用于向用户显示一些几乎固定不变的文字或图形描述 按 钮 CButton 用于产生某些命令或改变某些选项设置 编辑框 CEdit 可完成文字的输入、输出双向操作,使用户能查看并编辑文字 列表框 CListBox 显示一个列表,让用户从中选取一个或多个项 组合框 CComboBox 它把列表框和编辑框有机地组合在一起,用户不仅能选择列表 中已有的项,还能编辑出新的项 滚动条 CScrollBar 通过滚动块在滚动条上的移动来改变某些数值 进展条 CProgressCtrl 用于指示一个操作的进度 旋转按钮 CSpinButtonCtrl 又称“上下控制”,是一对箭头按钮,用户单击它们可以增加 或减小某个值 滑动条 CSliderCtrl 是一个包含一个滑动块和可选的刻度线,用户可以用鼠标或方 第3章 对话框与控件 向键沿某个方向移动滑动块 旋转按钮 CSpinButtonCtrl 带有一对反向箭头的按钮,单击这对按钮可增大或减小某个值 日期时间 CDateTimeCtrl 用于选择指定的日期和时间 图象列表 CImageList 是一系列相同大小的图象的集合 标签控件 CTabCtrl 类似于一个笔记本的分割器或一个文件柜上的标签,使用它可 以将一个窗口或对话框的相同区域定义为多个页面 列表控件 ClistCtrl 可以用“大图标”、“小图标”、“列表”或“报表”等4种不同 的方式来显示一组信息 树控件 CTreeCtr 用树结构的形式显示一组信息,并能反映这些信息的层次关系 图3.5.2 静态图片控件的“General”属性 图3.5.3静态图片控件的“Style”风格 表3.7静态图片控件的General属性 项 目 说明 Visible 指定控件初始化时是否可见 Disabled 指定控件初始化时是否禁用 Help ID 若该项被选中,则为该控件建立一个上下文相关的帮助标识符 Group 看成同一组,成组的目的是可以让用户键盘方向在同一组控件中进行切换 TabStop 若该项被选中,则用户可以使用Tab键来选择控件 Type 图片类型,用户可以从中选择Frame(框)、Rectangle(矩形区域)、Icon(图标)、 Bitmap(位图)、Enhanced Metafile(增强图元文件,它是各种绘图命令的集合) 第3章 对话框与控件 Image 当图片类型为Icon或Bitmap时,通过此框可选择指定的资源ID号设置Frame 和Rectangle的颜色,它可以是black(黑色)、white(白色)、gray(灰色)、或者是 具有3D外观的etched(腐蚀色) Color 设置Frame和Rectangle的颜色,它可以是black(黑色)、white(白色)、gray (灰色)或者是具有3D外观的etched(腐蚀色) 表3.8静态图片控件的Styles风格 项 目 说 明 Sunken 选中时,在控件的周围有下沉的边框 Notify 选中时,当用户单击或双击图片时会向其父窗口发出通知消息 Right justify 选中时,用户重置图片大小,图片右下角是固定不变的 Border 选中时,图片周围有边框 Center image 选中时,图片显示在控件中央,其余区域由图片左上角的象素颜色来填充 Real size image 选中时,按图片的原始大小来显示,超过控件区域的部分被裁剪 例3.7 将一个.bmp图形显示在静态图片控件上 (1)建一个单文档(SDI)应用程序,名为:图片控件 (2)创建对话框模板: InsertàResourceà选中Dialogànew (3)将图片控件Picture拖到对话框上,并拉大些,将OK和CANCEL拖到下面。 (4)向项目中插入一个 .bmp图片: InsertàResourceà选中BitmapàImportà在出现的Import Resource对话框中下拉出:“所有文件(*.*)”,用“搜索”找到一个.bmp图形àImport导入到此项目中。 (5)将这个图片放到图片控件上: 在对话框上选中这个图片控件,右键对准图片控件单击à出现图片控件属性框对话框(Picture Propertiec)àType处下拉置Bitmapà在image处下拉置IDB_BITMAP1à关闭属性对话框à就把图片加到了图片控件上。 (6)为对话框添加类:双击新建对话框的任何位置àOKà类名写:CMyDlg (7)创建一个菜单项,用来显示对话框: ResourceViewàMenuàIDR_MAINFRAMEà双击空白菜单à点Pop-up(将其√去 第3章 对话框与控件 掉,不选中)àID处写:ID_DLG à菜单名写:显示图片对话框(&S)à关闭。 (8)在视图类的头文件CMyView.h的前面加:#include “MyDlg.h” 。 在该头文件的public:下加:CMyDlg *dlg; (9)将菜单命令(ID_DLG)映射到视图类CMyView中去,在该消息映射函数中加代码: CMyView::OnDlg() { dlg=new CMyDlg(this); dlg->Create(IDD_DIALOG1); dlg->ShowWindow(SW_RESTORE); } 说 明: SW_RESTORE:是 用原来的大小 和位置显示一个窗口,同时令其进入活动状态 。 (10)编译运行,见图3.5.4所示。 如果我们直接建立一个对话框应 用程序,在其上加一个图片控件,再 图3.5.4 显示图片控件 通过InsertàResourceàBitmapàImport 插进一个位图(.bmp)图形,通过图片控件的属性将这个图形加在这个图片控件上,运行程序,在对话框上就能直接显示了。 3.5.2 按钮控件 按钮控件是我们在进行可视化程序设计中经常用到的,它是实现一种开与关的输入。 1、常见的按钮控件有三种类型: (1)按键按钮:按键按钮通常可以立即产生某个动作,执行某个命令,因此也常被称为命令按钮。如果将其属性设定为Default button,这时它就有个黑边框,随之称为默认按钮,见图3.5.5上的“默认按钮”,表示其已接受到键盘的输入焦点,只要按回车键就能按下这个按钮。一般只把最常用的按键设定为默认按键按钮。 (2)单选按钮:其外形为按钮文本和其左侧一个小圆圈,当它被选中时,就标上一个黑点,它可分为一般和自动两种类型。 若是自动类型,选中同组按钮的某个单 选按钮,则其余的单选按钮的选中状态 就会清除,保证多个选项始终只有一个 被选中,如图3.5.5 上的Radio1所示。 第3章 对话框与控件 (3)复选框:其外形是在文本前有一个 空心方框,当它被选中时,就加上一个 “√”标记,通常复选框只有选中和未 选中两种状态,若其中有一个灰色“√”, 图3.5.5 三种不同类型的按钮 则这样的复选框是三态复选框,如图3.5.5 所示的check3,它表示复选框的选择状态是“不确定”。设定三态复选框的方法是在其属性的Style页面选中“Tri-state(状态)”项。 2、按钮的消息 常见的按钮映射消息有两个: (1)BN_CLICKED(单击按钮) (2)BN_DOUBLECLICKED(双击按钮) 3、按钮选中操作 最常用的按钮操作是设置或获取一个按钮或多个按钮的选中状态。CButton类的以下2个成员函数原型如下: void SetCheck(int nCheck); //设置指定按钮的选中状态 int GetCheck()const; //获取指定按钮的选中状态 其中,参数nCheck和函数GetCheck返回的值可以是:0表示不选中,1表示选中,2表示不确定(仅用于三态按钮) 而对于同组多个单选按钮的选中状态的设置或获取,需要使用CWnd类的成员函数: CheckRadioButton和GetCheckedRadioButton,它们的原型如下: void CheckRadioButton(int nIDFirstButton,int nIDLastButton,int nIDCheckButton); int GetCheckedRadioButton(int nIDFirstButton,int nIDLastButton); 参 数: nIDFirstButton和nIDLastButton分别指定这组单选按钮的第一个和最后一个按钮ID值。 nIDCheckButton用于指定要设置选中状态的按钮ID值。 4、按钮控件的应用举例 例3.8 使用复选框 (1)建一个基于对话框的应用程序,名为:复选框。 (2)将3个复选框和一个编辑框拖到对话框上,将编辑框拉长一些,并摆放整齐。对话框编辑器会将复选框的题注默认为Check1、Check2、Check3,见图3.5.6所示。 (3)用ClassWizard在CMyDlg类为编辑框IDC_EDIT1加String类型的成员变量:m_text 第3章 对话框与控件 (4)将这三个复选框的映射消息加到类CMyDlg,并加代码: void CMyDlg::OnCheck1() { m_text = "选中复选框 1"; UpdateData(false); } void CMyDlg::OnCheck2() { m_text = "选中复选框 2"; UpdateData(false); } void CMyDlg::OnCheck3() { m_text = "选中复选框 3"; UpdateData(false); 图3.5.6 控件对齐 } (6)编译运行,如图3.5.7所示。见复选框被单击之后,自动地在被选中和不选中之间切换(在编辑框显示程序中写入变量m_text中的信息),能够在任何时候决定复选框的状态,方法是将一个成员变量连接到复选框并使用GetCheck对象方法 例3.9 使用单选按钮 (1)建一个基于对话框的应用程序,名 为:单选按钮。 (2)拖过3个单选按钮和一个编辑框,把 编辑框拉长一些,并摆放整齐。对话框编辑 器也将给定单选按钮题注Radio1、Radio2、 Radio3等,当然我们可以更改它们。 (3)用ClassWizard为编辑框IDC_EDIT1 加一个成员变量:m_text1,并将Radio1、 Radio2、Radio3映射到CMyDlg中,并加代码: 图3.5.7 运行结果 void CMyDlg::OnRadio1() { m_text1 = "你选择了第一个单选按钮"; UpdateData(false); } void CMyDlg::OnRadio2() 第3章 对话框与控件 { m_text1 = "你选择了第二个单选按钮"; UpdateData(false); } void CMyDlg::OnRadio3() { m_text1 = "你选择了第三个单选按钮"; 图3.5.8 选中一个单选按钮 UpdateData(false); } (4)编译运行,见图3.5.8所示。 注 意:当我们单击其中一个单选按钮时,没有办法再选中其它单选按钮,这是因为这三个单选按钮已经被当作了一个整体,它们都位于同一个窗口里,你单击了一个单选按钮之后,程序将自动将其它的单选按钮改为不选。 如果你想在同一窗口的多个单选按钮中,选中一个后还可以选中其它,那你就得将多个单选按钮分成组,每一组的单选按钮用组框围起来,然后将每组的第一个单选按钮的属性的Group选中,置成“√”,这样你就可以在同一窗口的不同单选按钮中,同时选中多个。步骤再从(5)往下做: (5)再为对话框添加3个单选按钮(Radio4、Radio5、Radio6 )和一个编辑框(EDIT2),然后拖过一个组框(Group Box),将这三个单选按钮和编辑框用组框围起来。打开组框的属性对话框,将标题写成“第二组单选按钮”。再拖过一个组框(Group Box),将原来的3个单选按钮(Radio1、Radio2、Radio3)和一个编辑框(EDIT1)用组框围起来。打开组框的属性对话框,将标题写成“第一组单选按钮”。 (6)打开第一组第一个单选按钮Radio1的General属性,单击Group将其置成“√”。打开第二组第一个单选按钮Radio4的General属性,单击Group将其置成“√”。 (7)为编辑框EDIT2用ClassWizard添加一个成员变量:m_text2 (8)为三个单选按钮IDC_RADIO4、IDC_RADIO5、IDC_RADIO6添加映射消息并加下列代码: void CMyDlg::OnRadio4() { m_text2 = "你选中了单选按钮4"; UpdateData(false); } void CMyDlg::OnRadio5() { m_text2 = "你选中了单选按钮5"; UpdateData(false); } 第3章 对话框与控件 void CMyDlg::OnRadio6() { m_text2 = "你选中了单选按钮6"; UpdateData(false); 图3.5.9 同一窗口分组选中单选按钮 } (9)编译运行!分别选中一组和二组中的单选按钮出现图3.5.9所示结果。 例3.10 复选框和单选按钮混用 为花店设计一个小程序,允许顾客选择某一行花或者该行花中的许多支花。当用户单击一个相应于特殊排列的单选按钮时,程序将显示该排列里面各种各样的花(复选框)来供顾客继续选择,并且将在一个文本框里显示花的价格;当顾客单击另一单选按钮,程序应显示新一排列类型中的花,并给出该排列花的价格。 (1)建一个基于对话框的应用程序,名为:鲜花选择 (2)向对话框添加2个组合框,见图3.5.10所示。组合框能可视并有效地安排控件,特别是同一组框中的单选按钮具有一致的功能,因此能在程序中安排许多相互独立的单选按钮组。 图3.5.10 在对话框中添加2个组框 (3)右键单击组框,在弹出的快捷菜单中选择Properties(属性),然后在Caption框中 键入新的题注;这里将左边的组框设为:花的排列;右边的组设为:花的名字。 第3章 对话框与控件 (4)向“花的排列”组中添加4个单选按钮,分别将题注改为:一排、二排、三排、 四排;向“花的名字”组中添加4个复选框,分别将题注改为:山茶花、玫瑰花、百合 花、腊梅花。见图3.5.11所示。能让顾客使用单选按钮选取一个花排列,并使用复选框 指出哪些花位于哪个排列中。 图3.5.11 向组框中添加单选按钮及复选框 (5)用ClassWizard在CMyDlg类中为4个复选框添加BOOL类型的成员变量见图3.5.12 所示:IDC_CHECK1:m_check1,CHECK2:m_check2,IDC_CHECK3:m_check3, IDC_CHECK4为m_check4。见图3.5.13所示。 (6)向对话框中添加一个编辑框EDIT1,并用ClssWizard为其添加一个String类型的 成员变量m_text。 (7)用ClassWizard将映射消息IDC_RADIO1、IDC_RADIO2、IDC_RADIO3、IDC_RADIO4 连接到CMyDlg类的单选按钮控件。并在void CMyDlg::OnRadio1()前面定义整型变量n 第3章 对话框与控件 再为各单选按钮消息加代码: int n=0; // n用于确定是第几排的 void CMyDlg::OnRadio1() { n=1; m_check1 = true; m_check2 = true; m_check3 = true; m_check4 = true; m_text = " 有4 种花出售 "; UpdateData(false); void CMyDlg::OnRadio2() 图3.5.12 为复选框添加BOOL类型的成员变量 { n=2; m_check1 = true; m_check2 = false; m_check3 = true; m_check4 =false; m_text = " 有2种花出售 "; UpdateData(false); } void CMyDlg::OnRadio3() { n=3; m_check1 = false; m_check2 = true; m_check3 = false; m_check4 = true; m_text = " 有2种花出售"; UpdateData(false); } 图3.5.13 复选框的成员变量 void CMyDlg::OnRadio4() { n=4; m_check1 = true; 第3章 对话框与控件 m_check2 = true; m_check3 = true; m_check4 = false; m_text =" 有3种花出售 "; UpdateData(false); } (8)编译运行,单击一排,有4 种花出售,编辑框写:有4种花, 见图3.5.14。单击二排,见有2种 花出售,见图3.5.15所示。还有 图3.5.14 选一排花,有4种花出售 三排、四排可试试看。 (9)再用ClassWizard将映射消息 IDC_CHECK1、IDC_CHECK2、 IDC_CHECK3、IDC_CHECK4连接到 CMyDlg类的复选框控件,并加代码: void CMyDlg::OnCheck1() { if(n==3||n==0) { m_text =" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; 图3.5.15 选二排花,有2种花出售 UpdateData(false); return; } m_text =" (价格) Price: $ 100.00 "; m_check1 = true; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck2() { if(n==2||n==0) 第3章 对话框与控件 { m_text =" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 110.00 "; m_check1 = false; m_check2 = true; m_check3 = false; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck3() { if(n==3||n==0) { m_text =" "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 130.00 "; m_check1 = false; m_check2 = false; m_check3 = true; m_check4 = false; UpdateData(false); } void CMyDlg::OnCheck4() { if(n==4||n==2||n==0) { m_text=" "; 第3章 对话框与控件 m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = false; UpdateData(false); return; } m_text =" (价格) Price: $ 150.00 "; m_check1 = false; m_check2 = false; m_check3 = false; m_check4 = true; UpdateData(false); } (10)编译运行,单击某一排后再单击每一种花,每一种花的价钱都显示在编辑框里;单击二排只有“山茶”、“百合”,分别单击它们,出现各自的价钱;而单击“玫瑰”、“腊梅”却不出现它们的价钱,因为在这一排没有这两种花,这就是程序中变量n所起的作用。例如我们在:void CMyDlg::OnRadio3()函数中定义n=3,那么当点击三排(Radio3)时,“山茶”和“百合”就不被选中,也就是没有这两种花,而单击“山茶”(OnCheck1),就到消息函数:CMyDlg::OnCheck1()中,其中第一条语句就判别n是否等于3,如果等于3 ,将m_text变量置空返回,不执行下面语句,也就是不显示价钱;在CMyDlg::OnCheck3()中也是这样判别的,因为这两种花在三排里也没有,所以都不显示价钱。CMyDlg::OnCheck2()、CMyDlg::OnCheck4()函数中的if判别都是这个道理。 3.5.3 编辑框控件 编辑控件(Edit Box)的主要作用在于接收用户键盘输入,通过它可以很方便地输入各种文本,数字或者口令,也可以用它编辑和修改文本的内容。编辑框里的文本可以是单行,也可以是多行,由其属性Multiline决定。多行编辑具有简单文本编辑器的常用功能,例如它可以有滚动条,换行用 Ctrl+Enter键,还可以进行复制、粘贴等 操作。单行编辑仅用于单行文本的显示和 操作。当编辑框控件被激活并具有输入焦 点时,会出现一个闪动的插入符,表明当 第3章 对话框与控件 前插入点的位置。 1、编辑框的属性 编辑框的属性Style(风格)对话框 图3.5.16 编辑框的属性Style对话框 见图3.5.16所示,表3.9列出了Style的各项含义。 表3. 9 编辑框的Style属性 项目 说明 Align text 各行文本对齐方式:Left、Center、Right,默认时为Left Multiline 选中时为多行编辑框,否则为单行编辑框 Number 选中时控件只能输入数字 Horizontal scroll 水平滚动,仅对多行编辑框有效 Auto HScroll 当用户在行尾输入一个字符时,文本自动向右滚动 Vertical scroll 垂直滚动,仅对多行编辑器有效 Auto VScroll 当用户在最后一行按ENTER键时,文本自动向上滚动一页,仅对多 行编辑框有效 Password 选中时,输入编辑框的字符都将显示为“*”,仅对单行编辑框有效 No hide selection 通常情况下,当编辑框失去键盘焦点时,被选择的文本仍然反色显示。 选中时,则不具备此功能。 OEM convert 选中时,实现对特定字符集的字符转换 Want return 选中时,用户按下ENTER键,编辑框中就会插入一个回车符 Border 选中时,在控件的周围存在边框 Uppercase 选中时,输入在编辑框的字符全部转换成大写形式 Lowercase 选中时,输入在编辑框的字符全部转换成大写形式 Read_Only 选中时,防止用户输入或编辑文本 2、编辑框的通知消息 当编辑框的文本修改或者被滚动时,会向其父窗口发送一些通知消息,如表3.10所示。 第3章 对话框与控件 表3.10 编辑框的通知消息 通知消息 说明 EN_CHANGE 当编辑框中的文本已被修改,在新的文本显示之后发送此消息 EN_HSCROLL 当编辑框的水平滚动条被使用,在更新显示之前发送此消息 EN_KILLFOCUS 编辑框失去键盘输入焦点时发送此消息 EN_MAXTEXT 文本数目到达了限定值时发送此消息 EN_SETFOCUS 编辑框得到键盘输入焦点时,发送此消息 EN_UPDATE 编辑框中的文本已被修改,新的文本显示之前发送此消息 EN_VSCROLL 当编辑框的垂直滚动条被使用,在更新显示之前发送此消息 3、编辑框的基本操作 为了能让编辑框控件允许不同类型数据的输入和读取,用户需要使用DDV和DDX技术。DDX将控件的成员变量同对话框类控件相连接,这样使得数据在控件之间很容易传输;DDV用于数据的校验。 (1)密码设置 密码设置就是把编辑框设置成一个可输入密码的输入框。这时,用户输入的每个字符都被一个特殊的字符代替显示,这个特殊的字符称为密码字符。选择编辑框的属性对话框的“Styles”选项卡,选中“Password”就将编辑框设置成一个可输入密码的输入框。默认的密码字符是“*”,应用程序可以使用成员函数CEdit::SetPassWordChar来定义自己的密码字符,该函数原型如: void SetPasswordChar(TCHAR ch); 参 数:ch表示设定的密码字符。当ch=0时,编辑框控件内将显示实际字符。 例3.11 设置密码 (1)建一个多文档(或单文档)应用程序,名为:设置密码。 (2)插入一个对话框,将ID改为:IDD_PASSWORD_DIALOG,标题(Caption)写:设置密码,将OK和CANCEL改为确定和取消。 (3)按图3. 5.17的布局布置控件。 1)拖一个静态文本(Static Text),标题写:请 输入用户名,并拖一个编辑控件,将其ID改 为:IDC_PASSWORD_EDIT1,在Style中选 PassWord属性。 第3章 对话框与控件 2)再拖一个静态文本,标题写:请输入密码, 拖一个编辑控件,将其ID改为: 图3.5.17 密码设置界面 IDC_PASSWORD_EDIT2,在Style中选PassWord属性。 (4)创建对话框类,名为:CPassWordDialog (5)加成员变量:为对话框类(Class name处为CPassWordDialog)的编辑框控件IDC_PASSWORD_EDIT1和IDC_PASSWORD_EDIT2加String类型的成员变量:m_password1和m_password2,在 Maximum Characters处写:20(字符串个数最多为20) (6)在应用程序”设置密码.cpp”的InitInstance()函数中加代码: Bool CMyApp::InitInstance() { ----- m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); CPassWordDialog CDlg; if(CDlg.DoModal()==IDOK) //用户按了确定按钮 { if(CDlg.m_password1!="nanshan") //用户名 { AfxMessageBox("用户名错误,确定后将退出程序"); return FALSE; } if(CDlg.m_password2!="1949") //密码 { AfxMessageBox("密码错误,确定后将退出程序"); return FALSE; } } else //如果按下取消按钮程序也结束 return FALSE; return TRUE; } (7)在应用程序“设置密码.cpp“头部加:#include “PassWordDialog.h” (8)编译运行,结果见3.5.17所示。 (2)选择文本 用户可以在程序运行过程中选择编辑框中的文本,以便对选择的文本进行处理。这时用户可以用鼠标或键盘来选择文本。用鼠标选择文本的方法是:在要选择的文本的一端按下鼠标左键并拖动鼠标,到另一端释放鼠标键;用键盘选择文本的方法是:在按光标方向移动键的同时按住【Shift】键。在应用程序中也可以通过编程选择文本,这时需要通过调用成员函数CEdit::SetSel来实现。这个函数确定了编辑框内文本的选择范围,与该函数相应的还有CEdit::GetSel和CEdit::ReplaceSel,它们分别用来获取编辑框控件中前一选择的开始和结束的位置以及替代被选择的文本。 第3章 对话框与控件 (3)只读设置 只读设置就是把编辑框设置成一个只读的编辑框,这时该编辑框只能读不能输入和修改。设置的方法是选择编辑框的属性对话框的“Styles”选项卡,选中“Read-only”就将编辑框设置成一个只读的编辑框。也可以通过编程来实现编辑框的只读设置,需要调用成员函数CEdit::SetReadOnly来实现,该函数的原型:BOOL SetReadOnly( BOOL bReadOnly = TRUE ); 参 数: bReadOnly为TRUE,就将编辑框设置为只 读;否则该编辑框可读可写。 例3.12 绘制一个矩形,该矩形的颜色是由 用户的选择来确定的。 (1)创建一个基于对话框的应用程序,名为: 颜色矩形。 图3.5.18 选择颜色后的结果 (2)删除对话框上的“确定”和“删除”按 钮,参照图3.5.18向对话框添加控件,按表3.11设置控件属性和设置关联变量。 表3.11 添加的控件和关联的成员变量 控件的名称 ID值 Caption 成员变量 静态文本 IDC_STATIC 红色 静态文本 IDC_STATIC 绿色 静态文本 IDC_STATIC 蓝色 编辑框 ID_RED int m_red 编辑框 ID_GREEN int m_green 编辑框 ID_BLUE int m_blue 按钮 IDC_APPLY 应用 按钮 EXITBUTTON 退出 第3章 对话框与控件 (3)在“颜色矩形Dlg.h”文件的public下,添加公有成员变量: int m_change; //修改标志 在“颜色矩形Dlg.cpp”文件的构造函数中设置初值: m_change = 1; //表示根据选择的颜色画矩形 (4)添加消息映射函数及其代码: 将鼠标放在对话框的“应用”按钮上,单击右键,在弹出的快捷菜单上选择“ClaasWizard”->弹出如图3.5.19所示的对话框。将IDC_APPLY消息映射到CMyDlg类的“应用”控件。并加如下代码: void CMyDlg::OnApply() { m_change = 1; //使用户视图区无效,以清除上 //一次画的图 Invalidate(); } 说 明:m_change = 1 说明用户点 击了【应用】按钮,函数Invalidate 发出WM_PAINT消息,从而触发视 图类的OnPaint函数,进而重绘要更 新的区域。 图3.5.19 加映射消息对话框 用同样方法,为IDC_EXITBUTTON 添加BN_CLICKED消息映射函数OnExibutton,并加如下代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } (5)在颜色矩形Dlg.cpp文件的OnPaint 函数中添加画矩形的代码: void CMyDlg::OnPaint() { … … else { if(m_change==1) {//用屏幕上的当前值更新控制变量 图3.5.20 静态文本和编辑框的风格 UpdateData(); 第3章 对话框与控件 CPaintDC dc(this); //定义一个画刷类对象 CBrush mybrush; mybrush.CreateSolidBrush(RGB(m_red,m_green,m_blue));//创建画刷 CBrush *pOldbrush = (CBrush*)dc.SelectObject(&mybrush); dc.Rectangle(180,20,280,120); //画一个矩形,这里如果矩形重叠在控件上, // 可将x的2个坐标数写大些,如(220,20,320,20)等。 dc.SelectObject(pOldbrush);//恢复原来的画笔 } CDialog::OnPaint(); } } (6)编译运行! 在红色编辑框里写“255”,再按“应用”按钮,则旁边的矩形就呈现红色,若再在绿色编辑框里写“255”,再按“应用”按钮,则旁边的矩形就呈现黄色。还可进行多个混合选择等等,都试试看。结果见图3.5.18所示。关于绘图的知识,在后边的章节中介绍。 (7)设置静态文本和编辑框的风格: 将“红色”静态文本属性对话框的“Style”选项卡页面设置“Center Verticall”属性,并在“Align text”下拉列表框(Left、Center、Right)选择“Center”属性,在“Expand Style”页面设置“Cliente dge(深凹下框)”。同样也为“绿色”设置“Center Verticall”,“Center”,而在“Expand Style”页面设置“Static edge(浅凹下框)”。为蓝色设置“Center Verticall”,“Center”,而在“Expand Style”页面设置“Modal Frame(凸起框)”属性。而三个编辑框的“Expand Style”页面风格都设置“Client edge”、“Static edge”和“Modal Frame”属性,如图3.5.20所示。 注 意:控件的风格要根据外观的需要来选择,而有些则要实现一定的功能,如“设置密码”程序中,在“Style”选项卡页面设置“PassWord”属性。 例 3.13 如图3.5.21所示,当我们在数学、英语、计算机编辑框中输入学生成绩后,单击[计算平均分]按钮,将在静态文本控件上显示出这三个成绩的平均分。 (1)建一个基于对话框的应用程序,名为:计算平均成绩。 (2)删除对话框上的Cancel按钮,参看图3.5.21的控件布局,用编辑器为对话框添加如表3.12所示的控件。 第3章 对话框与控件 (3)打开ClassWizard的Member Variables标签,在Class name中选择CMyDlog,选中所需的控件ID号,双击鼠标或单击Add Variables按钮。依次为表3.13所列控件增加成员变量。 (4)在CMyDlog.cpp的OnInitDialog()函数中加 下列代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … … m_strAve="0.00"; //将成员变量数据传给控件,并在控件中显示。 UpdateData(FALSE); return TRUE; } 图3.5.21 运行结果 说 明:WM_INITDIALOG消息是当对话框显示前发送的,用户在此消息的映射函数中添加一些设置控件或其它的初始化代码,以便对话框创建时自动调用。 表3.12 向对话框上添加的控件 控件 ID号 标题 属性 静态文本 IDC_STATIC 显示平均分 默认 静态文本 IDC_AVERAGE 默认 Center,Center vertically,Static edge(浅凹) 组框 默认 成绩计算 默认 静态文本 默认 数学 默认 编辑框 IDC_EDIT1 默认 静态文本 默认 英语 默认 编辑框 IDC_EDIT2 默认 静态文本 默认 计算机 默认 编辑框 IDC_EDIT3 默认 按钮 IDC_BUTTON1 计算平均分 默认 按钮 IDOK 退出 Default button,其余默认 表3.13 为相关控件添加成员变量 控件ID号 变量类型 变量名 范围和大小 第3章 对话框与控件 IDC_AVERAGE CString m_strAve 20 IDC_EDIT1 float m_nScore1 0.0-100.0 IDC_EDIT2 float m_nScore2 0.0-100.0 IDC_EDIT3 float m_nScore3 0.0-100.0 (5)用ClassWziard为按钮IDC_BUTTON1在CMyDlg类中添加BN_CLICKED的消息映射,并加代码: void CMyDlg::OnButton1() { UpdateData(); //将控件显示的数据传给成员变量 double ave=(double)(m_nScore1+m_nScore2+m_nScore3)/3.0; m_strAve.Format("%6.2f",ave); UpdateData(FALSE); //将成员变量数据传给控件,并在控件中显示 } (6)编译运行,结果见图3.5.21所示 说 明:代码中,Format是CString类的一个经常使用的成员函数,它通过格式操作使任意类型的 数据转换成一个字符串,其输出形式相当于C语言的printf()函数。 3.5.4 列表框 列表框(List Box)是一个允许用户从已有的项目中进行选择的控件。列表框中的项目数是灵活变化的,除了可以在资源编辑器中通过设置列表框的属性来增减列表框外,还可以在程序运行时向列表框中添加或删除某些项,MFC的CListBox类封装了列表框控件的各种操作。下面我们先举例看一看列表框的应用情况。 例3.14 如图3.5.22所示,在编辑框中输入一个 学生的名字,单击【添加】按钮,可将这个学生的 名字加入到左边的列表框中;左边的列表框中选择 某一个人的名字,单击【删除】按钮,可将这个选 定的名字删除。 (1)利用MFC AppWizard[exe]向导,创建一个基 于对话框的应用程序,名为:列表框 (2)添加控件和关联的成员变量如表3.14所示, 第3章 对话框与控件 控件的布局如图3.5.22所示。 图3.5.22 列表框1运行结果 表3.14 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 列表框 IDC_NAMELLIST ClistBox m_NameList 静态文本 IDC_STATIC 请输入姓名 编辑框 IDC_NAME CString m_strName 按钮 IDC_ADD 增加 按钮 IDC_DELETE 删除 按钮 IDC_EXITBUTTON 退出 (3)添加映射消息函数 1)在CMyDlg类为按钮IDC_ADD添加BN_CLICKED映射消息,并加下列代码: void CMyDlg::OnAdd() { UpdateData();//将控件显示的数据传给成员变量 if(m_strName.IsEmpty()) //判断字符串是否为空 { AfxMessageBox("请输入姓名! "); return; } m_strName.TrimLeft(); //去掉字符串左边的换行符号、空格和控制字符 m_strName.TrimRight(); //去掉字符串右边的换行符号、空格和控制字符 //下面语句是判断输入的字符串是否已在列表框中,即是否重复 if(m_NameList.FindString(-1,m_strName)!=LB_ERR) { AfxMessageBox("该姓名已存在,请重新输入! "); return; } int index; //将输入的字符串加到列表框中,并将索引号保存在index中 index = m_NameList.AddString(m_strName); //下面语句是:将字符串与新增的列表项关联起来 m_NameList.SetItemDataPtr(index,new CString(m_strName)); } 第3章 对话框与控件 2)在CMyDlg类,为按钮IDC_DELETE添加BN_CLICKED消息并加代码: void CMyDlg::OnDelete() { int index; index = m_NameList.GetCurSel();//获得当前列表项的索引 if(index !=LB_ERR) { //释放关联数据所占的内存空间 delete (CString *)m_NameList.GetItemDataPtr(index); m_NameList.DeleteString(index); //删除列表框当前选项 UpdateData(false); //在列表框中显示数据 } else AfxMessageBox("没有选择列表项或选择失败! "); } 3)在CMyDlg类,为按钮IDC_ EXITBUTTON添加BN_CLICKED映射消息并加代码: void CMyDlg::OnExitbutton() { OnOK(); // 退出 } (4)编译运行,结果如图3.5.22所示。 1、列表框的基本操作 列表框中的列表项可以用每一项各自的文本字符串来标识,还可以通过每一列表项的索引来确定。索引表明项目在列表中的排列位置,它从0开始计算,即列表中第一项的索引是0,第二项的索引是1,以此类推。 (1)添加列表项 列表框创建时是一个空的列表,需要用户添加或插入一些列表项。CListBox类的 成员函数AddString和InsertString分别用于添加列表项,其函数原型为: int AddString(LPCTSTR lpszItem); int InsertString(int nIndex,LPCTSTR lpszItem); 其中:列表项的字符串文本由参数lpszItem来指定,成功返回列表在列表框的索引,错误返回LB_ERR,空间不够返回LB_ERRSPACE。但InsertString函数不会将列表项进行排序,不管列表框控件是否具有sort(按字母顺序排列)属性,只是将列表项插在指定索引的列表项之前,若nIndex等于-1,则列表项添加在列表框末尾。而AddString函数在当列表框控件具有sort属性时会自动将添加的列表项进行排序。函数原型中,LPCTSTR类型用于表示一个常值字符指针,这里可以将其理解成是一个常值字符串类型。 第3章 对话框与控件 以上2个函数只能将字符串增加到列表框中,但有时用户还会需要根据列表项,使用其它数据。这时,ListBox的SetItemData和SetItemDataPtr能有效解决这个问题,它们能使用户数据和某个列表项关联起来: int SetItemData(int nIndex,DWORD dwItemData); int SetItemDataPtr(int nIndex,void *pData); 其中,函数SetItemData是将一个32位数与某列表项(由nIndex指定)关联起来,而 SetItemDataPtr函数可以将用户的数组、结构体等大量的数据与列表项相关联。若产生错误,它们都返回LB_ERR。 与上述两个函数相对应的两个函数是GetItemData和GetItemDataPar,它们分别用来获取相关联的用户数据。原型如下: DWORD GetItemData(int nIndex) const; void *GetItemDataPtr(int nIndex) const; (2)列表框的单项选择 当选中列表框中某个列表项时,用户可以使用CListBox::GetCurSel函数来获取当前选项中的列表项的索引值。与该函数相对应的CListBox::SetCurSel函数用来设定某个列表项呈选中状态(高亮显示)。 int GetCurSel() const; //返回当前选择项的索引 int SetCurSel(int nSelect); 参 数:nSelect用来指定要设置的列表项的索引,错误时这两个函数都将返回LB_ERR。 若要获取某个列表项的字符串,可以用下列函数: int GetText(int nIndex,LPTSTR lpszBuffer) const; void GetText(int nIndex,CString &rString) const; 参 数:nIndex用来指定列表项的索引,lpszBuffer和rString用来存放列表项的文本。 (3)查找列表框 为了保证列表项不会重复地添加到列表框中,需要对列表框进行查找,可用CListBox类的成员函数FindString和FindStringExact在列表框中查找与其所匹配的列表项,其中,FindStringExact函数的查找精度高。这两个函数的原型如下: int FindString(int nStartAfter,LPCTSTR lpszItem) const; int FindStringExact(int nIndexStart,LPCTSTR lpszFind) const; 参 数:lpszItem和lpszFind指定要查找的列表项的文本值,nStartAfter和nIndexStart指定查找的开始位置,若为-1,则从头至尾查找。找到后,这两个函数都返回所匹配的列表项的索引,否则返回LB_ERR。 第3章 对话框与控件 (4)删除列表项 利用CListBox类的成员函数DeleteString和ResetContent可以删除指定的列表项或列表框的所有表目,其函数原型如下: int DeleteString(UINT nIndex); void ResetContent(); 参 数:nIndex指定要删除的列表项的索引。 注 意:若在添加列表项时使用SetItemDataPtr函数,必须在进行删除操作时及时将关联数据所占的内存空间释放出来。 2、列表框的风格 按性质来分,列表框有单选、多选、扩展多选以及非选四种类型,默认风格下的单选列表框让用户一次只能选择一个项,多选列表框可让用户一次选择几个项,而扩展多 选列表框允许用户用鼠标拖动或其他特殊组合键进行选择,非选择列表框不提供选择功能。列表框还有一系列其他风格,用于定义列表框的外观及操作方式,这些风格可在图3.5.23所示的列表框的“Styles”对话框中设置。表3.15列出了Styles各项的含义。 图3.5.23 列表框的“Styles”对话框 表3.15 列表框的Styles属性 项目 说明 Selection(选择) 指定列表框的类型:单选(Single)、多选(Multiple)、扩展 多选(Extended)、不选(None) Owner draw(所有者) 自画列表框,默认为No Has strings 选中时,在自画列表框的项目中含有字符串文本 第3章 对话框与控件 Border 选中时,使列表框含有边框 Sort 选中时,列表框的项目按字母顺序排列 Notify 选中时,当用户对列表框操作就会向父窗口发送通知消息 Multi-column 选中时,指定一个具有水平滚动的多列表框 Horizontal scroll 选中时,在列表框中创建一个水平滚动条 Vertical scroll 选中时,在列表框中创建一个垂直滚动条 No redraw 选中时,列表框发生变化后不会自动重画 Use tabstops 选中时,允许使用停止位来调整列表项的水平位置 Want key input 选中此项,当用户按键且列表框有输入焦点时,就会向列 表框的父窗口发送相应消息 Disable no scroll 选中时,即使列表框的列表项能全部显示,垂直滚动条也 会显示,但此时是禁用的(灰显) No integral height 选中时,在创建列表框的过程中,系统会把用户指定的尺 寸完全作为列表框的尺寸,而不论是否有项目在列表框,也 不能完全显示出来 3、列表框的通知消息 当列表框中发生了某个动作,如用户双击选择了列表框中某一项时,列表框就会向 其父窗口发送一条通知消息。常用的通知消息如表3.16所示。 表3.16 列表框的通知消息 通知消息 说明 LBN_DBLCLK 用户双击列表框的某项字符串时发送此消息 LBN_KILLFOCUS 列表框失去键盘输入焦点时发送此消息 LBN_SELCANCEL 当前选择项被取消时发送词消息 LBN_SELCHANGE 列表框中的当前选项将要改变时发送此消息 LBN_SETFOCUS 列表框获得键盘输入焦点时发送此消息 第3章 对话框与控件 例3.15 编一个学生成绩登记程序,如图3.5.24所示。在编辑框中敲入学生姓名和三 门课程的成绩;姓名被添加在列表框中,成绩与该列表项关联。任何时候选中列表中某 个学生,相应的记录数据都被显示出来。 图3.5.24 学生成绩登记 (1)创建一个基于对话框的应用程序,名为:成绩登记 (2)删除原来的Cancel按钮,参看图3.5.24的控件布局,用编辑器为对话框添加如 表3.17所示的控件。 (3)打开成绩登记Dlg.h头文件,在public:下添加数组: struct SCORE { int scoreE; //英语成绩 int scoreM; //数学成绩 int scoreC; //计算机成绩 }; (4)打开ClassWizard的Member Variables页面,在Class name中选择CMyDlg,选中所需的控件ID号,双击鼠标或单击Add Variables按钮,依次为表3.18所示控件增加成员变量。 第3章 对话框与控件 表3. 17 向对话框添加的控件 控 件 ID 号 标 题 属 性 组框 默认 学生成绩登记 默认 静态文本 默认 姓名 默认 编辑框 IDC_NAME 默认 静态文本 默认 英语 默认 编辑框 IDC_ENG 默认 静态文本 默认 数学 默认 编辑框 IDC_MAT 默认 静态文本 默认 计算机 默认 编辑框 IDC_COM 默认 按钮 IDC_ADD 添加记录 默认 按钮 IDC_DEL 删除记录 默认 按钮 IDOK 退出 Default button,其余默认 列表框 IDC_LIST 默认 表3.18 为控件加成员变量 控件ID号 变量类型 变量名 范围和大小 IDC_LIST CListBox m_List ------ IDC_NAME CString m_strName 20 IDC_ENG float m_nEng 0.0-100.0 IDC_MAT float m_nMat 0.0-100.0 IDC_COM float m_nCom 0.0-100.0 (5)切换到ClassWizard的Message Maps标签页,在CMyDlg类为按钮IDC_ADD(添加记录)添加BN_CLICKED的消息映射,并增加下列代码: void CMyDlg::OnAdd() { UpdateData(TRUE);//将控件值赋值给成员变量,UpdateData(FALSE)将成员变量值赋值给控件 //判断m_strName是否为空 if(m_strName.IsEmpty()) 第3章 对话框与控件 { MessageBox("姓名不能为空!"); return; } m_strName.TrimLeft();//删除字符串m_strName左边的空格及控制字符 m_strName.TrimRight();//删除字符串m_strName右边的空格及控制字符 if((m_List.FindString(-1,m_strName))!=LB_ERR) // FindString()在 ListBox 的项中搜索所有搜索文本的实例,即查找有无重复项 { MessageBox("列表框中已有相同姓名,不能添加!"); return; } //向列表框添加学生姓名,并将该学生姓名在列表框中的位置即索引号赋给nIndex,nIndex是索引号。 int nIndex=m_List.AddString(m_strName); SCORE data; //定义3门成绩的结构体变量 data.scoreE=m_nEng;// 英语成绩赋予结构体中的变量scoreY data.scoreM=m_nMat;// 数学成绩赋予结构体中的变量scoreM data.scoreC=m_nCom;// 计算机成绩赋予结构体中的变量scoreJ m_List.SetItemDataPtr(nIndex,new SCORE(data)); } 说 明: 1、SetItemDataPtr()或SetItemData()是将一个32位的指针(或一个DWORD的值)同列表框中的一 个条目联系起来,并且在设置后可以通过调用GetItemDataPtr()或GetItemData()而获取。这样 做的目的是可以将列表框中的条目同外部数据建立联系。这里是姓名和他的成绩联系起来。 2、DWORD:32位无符号整数,段地址和相关的偏移地址 (6)用ClassWizard在CMyDlg类为按钮IDC_DEL(删除记录)添加BN_CLICKED的消息映射,并增加下列代码: void CMyDlg::OnDel() { int nIndex=m_List.GetCurSel(); //获得当前选项的索引 if(nIndex!=LB_ERR) { m_List.DeleteString(nIndex); //删除当前选择项 m_strName.Empty(); 第3章 对话框与控件 m_nEng=m_nMat=m_nCom=0; UpdateData(FALSE); // 将成员变量值赋值给控件,将编辑框的成绩充0 } else MessageBox("当前没有选择项或列表框操作失败!"); } 说 明: 1、m_List.GetCurSel();获取当前选择项基于零的索引,若有, 则在单选列表框中。 ...如果nIndex不是指定的有效索引,则返回LB_ERR。 参数: nIndex 指定获取的字符串的基于零的索引。 2、if(nIndex!=LB_ERR):如果当前无选择项或列表框是多选框时,为LB_ERR。 (7)用ClassWizard在CMyDlg类为按钮IDC_LIST添加LBN_SELCHANGE(当前选择项发生改变)的映射消息,并增加下列代码: void CMyDlg::OnSelchangeList1() { int nIndex=m_List.GetCurSel(); //获得当前项索引 if(nIndex!=LB_ERR) //有效索引,则: { m_List.GetText(nIndex,m_strName); //获得索引号对应的姓名 SCORE *data=(SCORE *)m_List.GetItemDataPtr(nIndex);//获得索引号对应的数据 m_nEng=data->scoreE; //将输入到编辑框的英语成绩赋给对应的成员变量 m_nMat=data->scoreM; //将输入到编辑框的数学成绩赋给对应的成员变量 m_nCom=data->scoreC; //将输入到编辑框的计算机成绩赋给对应的成员变量 UpdateData(FALSE); // 再将成员变量值赋给编辑框,用做显示 } } (8)用ClassWizard在CMyDlg类为对话框添加WM_DESTROY消息映射,并加代码: void CMyDlg::OnDestroy() { for(int nIndex=m_List.GetCount()-1; nIndex>=0; nIndex--) { //删除所有与列表相关联的SCORE结构数据,并释放内存 delete(SCORE *)m_List.GetItemDataPtr(nIndex); } CDialog::OnDestroy();//关闭对话框 } 第3章 对话框与控件 (9)编译运行,结果见3.5.24所示。 说 明: 1、getCount():返回编辑器内容的字数统计。 nIndex =m_List.GgetCount(int nType); 如果有参数 这是唯一的参数,是一个表示获取字数统计的类型,数字型。必需是下面值之一: 0:表示统计英文字数 1:表示统计中文字数 2:表示统计中文和英文的字数 2、对话框被清除时发送WM_DESTROY消息。用户在此消息的映射函数中添加一些对象删除代码, 以便在对话框清除前有效地释放内存空间。 3.5.5 组合框 组合框(Combo Box)控件是一个列表框和一个编辑框(或静态控件)结合而成的特殊控件,因此,组合框控件可分为编辑区和列表区两个部分。用户当前在列表区里选中的选项将被显示在控件的编辑区中。其中,组合框控件的列表区可以始终处于打开显示状态,也可以仅当用户单击组合框控件旁边的下拉箭头按钮时才被打开。下面我们先举一简单实例,看一看组合框的应用。 例3.16 编写一个程序,其功能是:如果在组合框中选择“可视化教室”所在的房间,则在组合框下方显示用户的选择。例如,在组合框中选择了“20号楼202房间”,则在组合框下方显示“可视化教室位于20号楼202房间”,如图3.5.25所示。 (1)利用MFC AppWizard[exe]向导创建一个基于对话框的应用程序,名为:组合框 (2)参照图3.5.25的控件布局,向对话 框添加表3.19的控件和关联的成员变量。 (3)在“组合框Dlg.h”头文件public下 添加如下成员变量: CString msg; int m_change; 在“组合框Dlg.cpp”实现文件的构造函数 中为如上成员变量赋初值: CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/) 图3.5.25 运行结果 : CDialog(CMyDlg::IDD, pParent) { ……………… 第3章 对话框与控件 msg = “ ”; m_change =0; } 表3.19 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 静态文本 IDC_STATIC 可视化教室位于 组合框 IDC_ROOM CComboBox m_Room 按钮 IDC_EXITBUTTON 退出 (4)右键单击组合框控件(IDC_ROOM),在Data属性中输入: 20号楼201房间、20号楼202房间、20号楼203房间、20号楼204房间、 20号楼205房间、20号楼206房间 注 意:输入时,一定要每个房间号占一行,如:“20号楼201房间”要占一行,用Ctrl+Enter换行后再写:20号楼202房间……。 (5)添加消息映射函数 1)在CMyDlg 类为退出按钮控件(IDC_EXITBUTTON),添加BN_CLICKED消息映射,并加代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } 2)在CMyDlg 类为组合框控件(IDC_ROOM),添加CBN_SELCHANGE消息映射,加代码: void CMyDlg::OnSelchangeRoom() { CString roomname; int index = m_Room.GetCurSel(); //获得当前选项的索引 if(index == CB_ERR) { AfxMessageBox("出现错误"); return; } m_Room.GetLBText(index,roomname); //获得当前选项的文本-> roomname(教室编号) msg = "可视化教室位于"; msg += roomname; m_change = 1; 第3章 对话框与控件 Invalidate(); //使用户视图区无效,以清除上一次画的图 } (6)在实现文件CMyDlg.cpp的OnPaint函数中添加显示文本的代码: void CMyDlg::OnPaint() { if (IsIconic()) { … … … } else { if(m_change == 1) { CPaintDC dc(this); // 设置透明模式 dc.SetBkMode(TRANSPARENT); dc.TextOut(50,100,msg); // 输出文本 } CDialog::OnPaint(); } } (7)编译运行,结果见图3.5.25所示。 说 明: 1、当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的dc对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。 1、CPaintDC类是一个来自CDC的设备环境类。它在构造期间执行CWnd::BeginPaint,在析构期间执行CWnd::EndPaint。一个CPaintDC对象只在响应一个WM_PAINT消息的时候被使用,通常是在你的OnPaint消息处理成员函数中。 2、SetBkMode(OPAQUE)用当前的背景颜色填充背景,或者SetBkMode(TRANSPAARENT)使背景保持不变,这两种方法都可以设置背景模式 1、组合框常见操作 MFC的CComboBox类封装了组合框控件的各种操作,组合框的操作可以调用CComboBox的成员函数来实现。其操作大致分为两类,一类是对组合框中的列表框进行操作,另一类是对组合框中的编辑框进行操作,如表3.20所示。由于组合框的一些编辑操作与编辑框CEdit的成员函数相似,如:GetEditSet,SetEditSel等,因此这些成员函数没有在上述表中列出。组合框的操作与列表框的操作类似。 2、组合框的风格 第3章 对话框与控件 按照组合框的主要风格特征,可把其分为3类:简单组合框、下拉式组合框和下拉式列表框。简单组合框和下拉式组合框都包含列表框和编辑框,但是简单组合框中的列表框不需要下拉,是直接显示出来的,而当用户单击下拉式组合框中的下拉按钮时,下拉的列表框才被显示出来。图3.5.26是组合框属性对话框的“Styles”风格对话框,表3.21列出了其各项含义。 图3.5.26 “Styles”对话框 3. 20 CComboBox类常用成员函数 成员函数 作 用 说明 int AddString(LPCTSTR lpszString); 向组合框添加字符串。 错误返回CB_ERR,空间不够时, 返回CB_ERRSPACE。 int DeleteString(UINT nIndex); 删除指定的索引项。 返回剩下的列表项总数,错误时 返回CB_ERR。 int InsertString(int nIndex, 在指定的位置处插入字 返回插入字符的索引,错误时返回 LPCTSTR lpszString); 符串,若nIndex=-1时, CB_ERR; 空间不够时,返回 向组合框尾部添加。 CB_ERRSPACE。 void ResetContent(); 删除组合框的全部项 和编辑文本。 int FindString(int nStartAfter, 查找字符串。 参数1=搜索起始项的索引,-1 LPCTSTR lpszString)const; 时从头开始,参数2=被搜索字符串。 int FindStringExact(int nIndexStart, 精确查找字符串 返回匹配项的索引,错误时返回 LPCTSTR lpszFind)const; CB_ERR int SelectString(int nStartAfter, 选定指定字符串 返回选择项的索引,若当前选择 第3章 对话框与控件 LPCTSTR lpszString); 项没有改变,则返回CB_ERR int GetCurSel()const; 获得当前选择项的索引 当没有当前选择项时,返回CB_ERR int SetCurSel(int nSelect); 设置当前选择项 参数为当前选择项的索引,-1时没 有选择项。错误时返回CB_ERR int GetCount(); 获取组合框的项数 错误时返回CB_ERR int SetDroppedWidth(UINT nWidt); 设置下拉组合框的最小 成功时返回新的组合框宽度, 像素宽度 否则返回CB_ERR int SetItemData(int nIndex, 将一个32位值和指定 错误时返回CB_ERR DWORD dwItemData); 列表项关联 int SetItemDataPtr(int nIndex, 将一个值的指针和指定 错误时返回CB_ERR void*pData); 列表项关联 DWORD GetItemData(int nIndex)const; 获取和指定列表项关联 错误时返回CB_ERR 的一个32位值 void *GetItemDataPtr(int nIndex)const; 获取和指定列表项关联 错误返回-1 的一个值的指针 int GetLBText(int nIndex, 获取指定项的字符串。 返回字符串的长度,若第1个参 LPTSTR lpszText); 数无效时返回CB_ERR int GetLBText(int nIndex, CString&rString) int GetLBTextLen(int nIndex)const; 获取指定项的字符串长度 若参数无效时返回CB_ERR 表3.21 组合框的Styles属性 项 目 说 明 类型(Type) 设置组合框的类型:Simple(简单)、Dropdown(下拉)、Drop List(下拉列表框) Owner draw 自画组合框,默认为No Has strings 选中时,在自画组合框中的项目中含有字符串文本 Sort 选中时,组合框的项目按字母顺序排列 Vertical scroll 选中时,在组合框中创建一个垂直滚动条 No integral height 选中时,在创建组合框的过程中,系统会把用户指定的尺寸完全作为组 合框的尺寸,而不管是否会有项目在组合框的列表中不能完全显示出来 第3章 对话框与控件 OEM convert 选中时,实现对特定字符集的字符转换 Auto HScroll 当用户在行尾输入一个字符时,文本自动向右滚动 Disable no scroll 选中时,即使组合框的列表项能全部显示,垂直滚动条也会显示,但 此时是禁用的(灰色的) Uppercase 选中时,输入在编辑框的字符全部转换成大写形式 Lowercase 选中时,输入在编辑框的字符全部转换成大写形式 3、组合框的通知消息 在组合框通知消息中,有的是操作列表框发出的,有的是操作编辑框发出的,如表3.22所示。 表3.22 组合框的通知消息 通知消息 说明 CBN_CLOSEUP 当组合框的列表关闭时发送此消息 CBN_DBLCLK 用户双击组合框的某项字符串时发送此消息 CBN_DROPDOWN 当组合框的列表打开时发送此消息 CBN_EDITCHANGE 同编辑框的EN_CHANGE消息 CBN_EDITUPDATE 同编辑框的EN_UPDATE消息 CBN_SELENDCANCEL 当前选择项被取消时发送此消息 CBN_SELENDOK 当用户选择一个项并按下ENTER键或单击下拉箭头隐藏 列表框时发送此消息 CBN_KILLFOCUS 组合框失去键盘输入焦点时发送此消息 CBN_SELCHANGE 组合框中的当前选择项将要改 变时发送此消息 CBN_SETFOCUS 组合框获得键盘输入焦点时发送此消息 3.5.6 旋转按钮控件 旋转按钮控件(Spin)又称为上下控件(Up Down Control)或微调控件,其主要功能是利用一对标有相反方向箭头的小按钮,通过点击它,在一定范围内改变当前的数值。旋转按钮控件的当前值通常显示在一个称为伙伴窗口(Buddy Window)的控制中,可以是一个编辑框等。旋转按钮控件的各种操作封装在MFC的CSpinButtonCtrl类中。下面我们先看看旋转按钮控件的用法。 例3.18 根据我们选择的长和宽的值,单 第3章 对话框与控件 击“应用”按钮后在对话框的右边画一个 矩形。例如长选180,宽选160,单击 “应用”后的对话框如图5.5.27所示。 (1)创建一个基于对话框的应用程序,名 为:旋转按钮 (2)参照图5.5.27的控件布局,按表3.23 图5.5.27运行结果 所示添加控件和在CMyDlg类添加关联的 成员变量。为了把旋转按钮和编辑框关联 在一起,需要设置如图5.5.28所示的 TabOrder顺序,并且需要设置两个旋转按 钮的Vertical、Right、Auto buddy、Sel buddy integer、Wrap和Arrow keys属性。 (3) 在CMyDlg.cpp实现文件的 OnInitDialog函数中,添加如下代码: BOOL CMyDlg::OnInitDialog() 图3.5.28 对话框控件的TabOrder顺序 { CDialog::OnInitDialog(); m_WidthSpin.SetRange(10,100); //设置旋转按钮的范围10-100 m_WidthSpin.SetPos(0); //设置旋转按钮的当前位置0 m_LengthSpin.SetRange(10,100);//设置旋转按钮的范围10-100 m_LengthSpin.SetPos(0); //设置旋转按钮的当前位置0 return TRUE; // return TRUE unless you set the focus to a control } 表3.23 添加的控件和关联的成员变量 控件名称 ID值 Caption 成员变量 静态文本 IDC_STATIC 矩形长 编辑框 IDC_LENGTH int m_Length 旋转按钮 IDC_SPIN1 CSpinButtonCtrl m_LengthSpin 静态文本 IDC_STATIC 矩形宽 编辑框 IDC_WIDTH int m_Width 旋转按钮 IDC_SPIN2 CSpinButtonCtrl m_WidthSpin 按钮 IDC_APPLY 应用 CButton m_Room 按钮 IDC_EXITBUTTON 退出 第3章 对话框与控件 (4)在CMyDlg类为IDC_APPLY的BN_CLICKED消息添加映射函数,并加代码: void CMyDlg::OnApply() { UpdateData(); //将控件显示的数据传给成员变量 Invalidate(); //使用户视图区无效,以清除上一次画的图 UpdateWindow(); //刷新用户视图区域 CClientDC dc(this); dc.Rectangle(180,10,180+m_Length,10+m_Width); //画矩形 } (5)为IDC_EXITBUTTON的BN_CLICKED消息添加映射函数,并加代码: void CMyDlg::OnExitbutton() { OnOK(); //退出 } 说 明: 在对话框上的排序(Tab Order),对于旋转按钮控件非常重要,例如:该例旋转按钮的排序为3,而左边是编辑控件(矩形长)排序为2,只有这两个控件紧靠在一起,旋转按钮的增减才对编辑控件的数值起作用。从旋转按钮设置的属性“Right”也可看出是位于伙伴控件(编辑框)的右边。 1、旋转按钮控件的基本操作 旋转按钮控件的基本操作包括基数、范围、位置的设置和获取等。 (1)基数的设置和获取 CSpinButtonCtrl类的成员函数SetBase用来设置基数,这个基数值决定了窗口显示的数字是十进制数还是十六进制数。如果成功则返回先前的基数值;如果给出一个无效的基数则返回0,该函数的原型如下: int SetBase(int nBase); 参 数:nBase表示控件的新基数,可以取10或16,分别用于设置基数为十进制或十六进制。 与SetBase函数相应的函数是GetBase,它用于获取旋转按钮控件的基数,该函数的原型为:UINT GetBase()const; (2)范围及位置的设置和获取 默认时,旋转按钮控件的最大值是100,最小值是0,可用成员函数SetRange或SetRange32来设置旋转按钮的范围,SetRange32函数是设置旋转按钮的32位范围,这两个函数的原型如下: void SetRange(int nLower,int nUpper); 第3章 对话框与控件 void SetRange32(int nLower,int nUpper); 参 数:nLower和nUpper表示控件的上限和下限。任何一个界限值不能大于0x7fff或小于-0x7fff。 与SetRange和SetRange32函数相应的函数是GetRange和GetRange32,这两个函数的原型如下: DWORD GetRange()const; void GetRange(int &lower,int &upper)const; void GetRange32(int &lower,int &upper)const; 参 数:lower和upper表示旋转按钮控件的下限和上限。 成员函数SetPos用来设置旋转按钮控件的当前位置,函数的原型:int SetPos(int nPos); 参 数:nPos表示控件的新位置,它必须在控件的上限和下限指定的范围内。 与SetPos函数相应的函数是GetPos,函数的原型:int GetPos()const; 2、旋转按钮的通知消息 旋转按钮控件的通知消息只有一个:UDN_DELTAPOS,它是在控件的当前数值将要改变时向其父窗口发送的。 3、旋转按钮控件的常用风格 旋转按钮的风格可以通过其属性对话框进行设置,如图3.5.29所示,其各项含义见表3.24所示。 图3.5.29 旋转按钮的属性对话框 表3.24 旋转按钮控件的Styles属性 项 目 说 明 方位(Orientation) 控件放置方向:Vertical(垂直),Horizontal(水平) 对齐(Alignment) 控件在伙伴窗口的位置安排:Unattached(不相干),Right(右边),Left(左边) 自动伙伴(Auto buddy) 选中此项,自动选择一个Z-order中的前一个窗口作为控件的伙伴窗口 第3章 对话框与控件 Set buddy integer 选中此项,使控件设置伙伴窗口数值,这个值可以是十进制或十六进制 No thousands 选中此项,不在每隔三个十进制数字的地方加上千分隔符 换行(Wrap) 选中此项,当增加或减小的数值超出范围时,则从最小值或最大值开始回绕 箭头键(Arrow keys) 选中此项,当按下向上和向下方向键时,也能增加或减少 Hot track 选中此项,当光标移过控件时,突出显示控件的上下按钮 例3.19 学生成绩输入 (1)创建一个单文档的应用程序,名为:输入成绩 (2)添加一个新的对话框资源,将ID号改为IDD_INPUT,标题为:学生成绩输入,将OK和Cancel按钮标题改为“确定“和“取消”, 并将其移到下边。 (3)参照图3.5.30的控件布局,向对话框添加如表 3.25所示的控件 (4)选择LayoutàTab Order命令或按快捷键Ctrl+D, 此时每个控件的左上方都有一个数字,表明了当前 Tab键的次序,单击对话框中的控件,重新设置控件 的Tab键次序,以保证旋转按钮控件的Tab键次序在 相对的编辑框(伙伴窗口)之后,结果如图3.5.31所示。 (5)双击对话框模板空白处,为该对话框模板创建一 个对话框类CInputDlg。 图3.5.30 学生成绩输入对话框 (6)在ClassWizard的Member Variables页面的 Class name中选择CInputDlg,选中所需的控件ID号,双击鼠标或单击Add Variables按钮,依次按表3.26所示控件增加成员变量。 (7)在MFC ClassWizard的Message Maps页面中, 为CInputDlg添加:WM_INITDIALOG消息映射,并 添加下列代码: BOOL CInputDlg::OnInitDialog() { CDialog::OnInitDialog(); m_spinSeng.SetRange(0,100); m_spinSmat.SetRange(0,100); m_spinScom.SetRange(0,100); 第3章 对话框与控件 return TRUE; } (8)用MFC ClassWizard为CinputDlg类添加 IDC_SPIN_SENG控件的:UDN_DELTAPOS 图3.5.31 改变控件的Tab键次序 消息映射(用来处理浮点数)并加代码: void CInputDlg::OnDeltaposSpinSeng(NMHDR* pNMHDR, LRESULT* pResult) { NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; //将控件的内容保存到变量中 UpdateData(TRUE); //将控件显示的数据传给成员变量 m_fSeng +=(float)pNMUpDown->iDelta * 0.5f; if(m_fSeng<0.0) m_fSeng=0.0f; if(m_fSeng>100.0) m_fSeng=100.0f; UpdateData(FALSE); // 将成员变量的内容显示到控件上 *pResult = 0; } 表3.25 添加的控件 添加的控件 ID号 标题 属 性 组框 默认 学生成绩输入 静态文本 默认 姓名 编辑框 IDC_EDIT_NAME 静态文本 默认 学号 编辑框 IDC_EDIT_NO 静态文本 默认 英语 编辑框 IDC_EDIT_SENG 旋转按钮 IDC_SPIN_SENG Auto buddy,Right 静态文本 默认 数学 编辑框 IDC_EDIT_SMAT 旋转按钮 IDC_SPIN_SMAT Auto buddy,Set butty integer, Right 静态文本 默认 计算机 编辑框 IDC_EDIT_SCOM 旋转按钮 IDC_SPIN_SCOM Auto buddy,Set buddy integer, Right 第3章 对话框与控件 表3.26 添加成员变量 控件ID号 变量类别 变量类型 变量名 范围和大小 IDC_EDIT_NAME Value CString m_strName 20 IDC_EDIT_NO Value CString m_strNo 20 IDC_EDIT_SENG Value float m_fSeng 0.0~100.0 IDC_SPIN_SENG control CSpinButtonCtrl m_spinSeng IDC_EDIT_SMAT Value float m_fSmat 0.0~100.0 IDC_SPIN_SMAT control CSpinButtonCtrl m_spinSmat IDC_EDIT_SCOM Value float m_fScom 0.0~100.0 IDC_SPIN_SCOM control CSpinButtonCtrl m_spinScom 说 明:程序代码中,NM_UPDOWN结构用于反映旋转按钮控件的当前位置(由成员iPos指定)和增量大小(由成员iDelta指定)。 (9)打开项目工作区的ResourceView页面,点开Menu,双击IDR_MAINFRAME,添加顶层菜单项:测试(&T),在其下面添加一个子菜单项:学生成绩输入(&I),ID为ID_TEST_INPUT。 (10)用MFC ClassWizard为CMainFrame 类添加菜单项ID_TEST_INPUT的COMMAND消息映射,并添加下列代码: void CMainFrame::OnTestInput() { CInputDlg dlg; if(IDOK == dlg.DoModal()) { CString str; str.Format("%s, %s, %4.1f, %4.1f, %4.1f", dlg.m_strName, dlg.m_strNo, dlg.m_fSeng, dlg.m_fSmat, dlg.m_fScom); AfxMessageBox(str); //将对话框上的编辑框内输入的数据用消息对话框显示出来 } 第3章 对话框与控件 } 说 明: 程序代码中,if语句是判断用户是否单击对话框的“确定”按钮。Format是CString类的一个经常使用的成员函数,它通过格式操作使任意类型的数据转换成一个字符串。该函数的第一个参数是带格式的字符串,其中的“%”就是一个格式符,每一个格式符依次对应于该函数的后面参数表中的参数项。例如格式字符串中第一个“%s”对应于Dlg.m_strName。CString类的Format和C语言中的库函数printf()十分相似。 (11)在文件MainFrm.cpp的前面将类CInputDlg类包含进来: #include “InputDlg.h” (12)编译并运行,如图3.5.30所示。单击英语的旋转按钮控件,将以0.5增量来改变它的伙伴窗口的数值。而数学、计算机的旋转按钮控件由于设置了Set buddy integer属性,因此它按默认增量1自动改变伙伴窗 口的数值。按确定按钮出现图3.5.32所示 对话框。 3.5.7 进展条 进展条控件常用于向用户反映一个 操作或任务的执行进度情况。进展条最 图3.5.32运行结果 初是一个空白的矩形区域,随着时间的 推移、任务的执行,该矩形区域将会逐渐填入颜色快。直到任务执行完毕,进展条也被颜色块所填满。下面就是使用进展条的一个实例。 例3.20 用程序语句创建一个进展条和一个按钮,当点击按钮时进展条就向前推进。 (1)用AppWizard创建一个单文档的应用程序,名为:进展条 (2)在视图类“进展条View.h”文件的public下,定义进展条类和按钮类的变量: CProgressCtrl m_progress; CButton m_progressBtn; (3)在视图类中,自行编写创建进展条的成员函数CreateProgressBar()步骤是:单击项目工作区的ClassView,鼠标对准CMyView单击右键,在出现的快捷菜单中选择Add Member Function…在弹出的对话框的Function Type写:void,在Function Declaration处写:CreateProgressBaràOK,这样就将该函数加到了视图类中。 第3章 对话框与控件 void CMyView::CreateProgressBar() { //创建进展条 m_progress.Create(WS_CHILD|WS_BORDER|WS_VISIBLE, CRect(100,30,400,60),this,IDC_PROGRESS); //设置属性 m_progress.SetRange(1,100); //设置进展条范围1-100之间 m_progress.SetStep(10); //设置进展条的步长10 m_progress.SetPos(0); //设置进展条的初始位置0 } (4)在视图类中,自行编写创建按钮的成员函数CreateButton()的步骤是:单击项目工作 区的ClassView,鼠标对准CMyView单击右键,在出现的快捷菜单中选择Add Member Function…在弹出的对话框的Function Type写:void,在Function Declaration写: CreateButtonàOK,这样就将该函数加到了视图类中。 void CMyView::CreateButton() { m_progressBtn.Create("Start",WS_CHILD|WS_BORDER|WS_VISIBLE, CRect(210,70,300,100),this,IDC_START); } (5)添加资源符号 我们在程序中所添加的进展条和按钮控件标识符必须加以说明,否则Visual C++ 将不予承认。 1)单击菜单项ViewàResource Symbols, 打开如图3.5.33所示的Resource Symbols 对话框。 2)单击New按钮,打开New Symbol对 话框,如图3.5.34所示 3)在New Symbol对话框的Name栏中 键入控件资源标志符:IDC_PROGRESS (默认Value为:101)。 4)按如上1)、2)、3)的步骤将资源 图3.5.33 Resource Symbols对话框 第3章 对话框与控件 符号IDC_START(Value为:102)也加进去。 (6)我们希望在视图创建的同时,创建控件,因此应当将添加控件的操作放置到视图类的OnCreate函数中去。使用ClassWizard为视图类的WM_CREATE消息添加处理函数OnCreate(),并将CreateProgressBar()、CreateButton()两个函数加入其中: int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct) //创建视图 { if (CView::OnCreate(lpCreateStruct) == -1) if (CView::OnCreate(lpCreateStruct) == -1) return -1; CreateProgressBar(); //创建进展条 CreateButton(); //创建”Start”按钮 return 0; } 图3.5.34 New Symbol (7)当我们按下Start按钮时,Start按钮与 进展条之间将进行通讯,因此还需为按钮控件IDC_START编写消息处理函数OnStart()。 由于MFC没为我们设置该消息映射,因此需要我们自己添加,过程如下: 1)在视图类“进展条View.h”文件中加: protected: //{{AFX_MSG(CMy1View) …….. …… ………. afx_msg void OnStart(); afx_msg void OnTimer(UINT nIDEvent); //}}AFX_MSG 2)在视图类“进展条View.cpp”文件的消息入口处加: BEGIN_MESSAGE_MAP(CMy1View, CView) //{{AFX_MSG_MAP(CMy1View) ON_WM_CREATE() ON_COMMAND(IDC_START,OnStart) ON_WM_TIMER() //}}AFX_MSG_MAP 3) 在视图类“进展条View.cpp”文件中加函数:(全用手写) void CMyView::OnStart() 第3章 对话框与控件 { SetTimer(IDC_START,1000,NULL); } 4)在按钮控件单击消息处理函数中调用了SetTimer函数创建了一个定时器,规定每隔1秒钟(1000毫秒)就触发定时器一次,执行定时例程OnTimer()。因此要为视图类WM_TIMER消息添加处理函数OnTimer()如下: void CMy1View::OnTimer(UINT nIDEvent) { m_progress.StepIt(); CView::OnTimer(nIDEvent); } (8)编译运行,如图3.5.35所示。 由此看出,每次触发定时器时,程序将 调用StepIt函数使进度条向前推进。 图3.5.35运行结果 1、进展条的基本操作 进展条的基本操作是通过CProgressCtrl类的相关函数来实现的。 (1)Create函数 该函数用于创建新的进展条并将其与CProgressCtrl对象相联系,其格式为: BOOL Create(DWORD dwStyle,const RECT &rect,CWnd *pParentWnd,UINT nID); 参 数: dwStyle参数用于指定进展条控件的风格; rect参数为一个CRect类型或RECT结构的量,用来指定进度条控件的大小和位置; pParentWnd参数为指向进度条控件父窗口的指针; nID参数指定进展条控件的ID值; (2)SetRange函数 该函数用于设置进度条控件的最小范围和最大范围,格式为: void SetRange(int nLower,int nUpper); 参 数: nLower参数用于指定进度条控件的下届; nUpper参数用于指定进度条控件的上届; (3)SetPos参数 该函数用于为进度条控件设置新位置,格式为:int SetPos(int nPos); (4)OffsetPos函数 该函数用于将进度条控件的位置向前推进指定单位,格式为:int OffsetPos(int nPos); 参 数:nPos参数用于指定推进的单位 第3章 对话框与控件 (5)SetStep函数 该函数用于设置进度条空件的步长,格式为: int SetStep(int nStep); 使用此函数可以将进度条控件的步长设置为nStep个单位,以后当用户调用CProgessCtrl::StepIt函数时,进度条控件的位置将向前推进nStep个单位。 (6)StepIt函数 该函数用于根据进展条控件的步长将进度条的位置向前推进一步,格式如下:int StepIt(); 2、进展条的风格 打开进展条的Styles属性对话框,如图3.5.36所示。可以看到它的风格属性并不多,其中,边框(Border)用来指定进展条是否有边框,垂直(Vertical)用来指定进展条是水平还是垂直的,不选中该属性,表示进展条从左到右水平显示。平滑(Smooth)表示平滑地填充进展条。若不选中则表示将用块来填充,如图3.5.37所示。 图3.5.36进展条的Styles属性对话框 图3.5.37运行结果 例3.21 编写一个实用程序,功能是:当选择浮动菜单的“打开”项时,出现如图3.5.37所示的对话框。程序的编写过程如下: (1)创建一个基于对话框的应用程序,名为:打开 (2)参照图3.5.37的控件布局,按表3.27所示,向对话框添加控件和关联的成员变量 (3)添加一个浮动菜单资源 1)ResourceView->右键单击最上面的“打开resource*”->在弹出的快捷菜单中选择“Insert…”->弹出“Insert Resource”对话框->选择Menu->单击New(默认的ID号为IDR_MENU1)。将此默认的菜单资源ID号改为IDR_MYFLOATMENU。 表3.27 添加的控件和关联的成员变量 第3章 对话框与控件 控件名称 ID值 Caption 成员变量名 类型 静态文本 IDC_STATIC 进展条 IDC_PROGRESS m_progress CProgressCtrl 按钮 IDC_EXITBUTTON 退出 2)用菜单编辑器为该菜单资源中的顶层菜单的第一项加一任意标题:浮动菜单(实际上该标题无用),在此菜单下添加如表3.28所示的菜单项。 表3.28 浮动菜单项 菜单ID Caption 属性 ID_OPENFLIE 打开文件(&O) ID_CLOSE 关闭文件(&C) ID_PRINT 打印文件(&P) 其他(&O) Pop_up (4)打开ClassWizard,将出现一对话框,询问是“选择一个已存在的类,还是“创建一个新类”;选择“选择一个已存在的类(Select an existing class)”项并选定CMyDlg类。 (5)在CMyDlg类中,选择表3.28所示的菜单ID,分别双击COMMAND消息,增加处理菜单项的函数。这里仅为菜单ID_OPENFLIE添加消息映射函数,并加下列代码: void CMyDlg::OnOpenflie() { m_progress.ShowWindow(SW_SHOW); //显示进展条空件 UpdateWindow(); //刷新 SetDlgItemText(IDC_STATIC,"正在打开文件..."); //设置静态文本的显示内容 for(int i=0;i<20;i++) { Sleep(100); //等待 m_progress.StepIt(); //填充一个蓝色块 } m_progress.ShowWindow(SW_HIDE); //隐藏进展条控件 SetDlgItemText(IDC_STATIC," "); //设置静态文本的显示内容 } 第3章 对话框与控件 (6)打开“打开文件Dlg.cpp”文件,在其OnInitDialog函数中添加如下代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … m_progress.ShowWindow(SW_HIDE); //隐藏进展条控件 m_progress.SetRange(1,200); //设置进展条范围 m_progress.SetStep(10); //设置进展条的步长 m_progress.SetPos(0); //设置进展条的初始位置 return TRUE; // return TRUE unless you set the focus to a control } (7)当我们右键单击对话框的任何位置,便弹出刚才我们设计的浮动菜单,在CMyDlg类加入WM_CONTEXTMENU消息处理函数,添加代码: void CMainFrame::OnContextMenu(CWnd *pWnd,CPoint point) { CMenu menu; menu.LoadMenu(IDR_MYFLOATMENU);//刚才加上的菜单资源 menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN| TPM_RIGHTBUTTON,point.x,point.y,this); } (8)为IDC_EXITBUTTON的BN_CLICKED消息添加消息映射函数,并加代码: void CMyDlg::OnExitbutton() { OnOK(); } (9)编译运行,见图3.5.38所示,右键单击对话框的任何位置弹出快捷菜单,如图3.5.39所示,单击“打开文件”便出现图3.5.37所示的最终结果。 第3章 对话框与控件 图3.5.38 运行后的对话框 图3.5.39 右键单击对话框 例3.22 用编辑器向对话框添加一个进展条 和“继续”、“后退”两个按钮,当单击“继 续”按钮时,进展条向前进,当单击“后退” 按钮时,进展条向后退,对话框上的静态文 本中还能显示出进展条的百分比。 (1)创建一个单文档的应用程序,名为: 使用进展条。 图3.5.40 运行结果 (2)向应用程序添加一个对话框资源,ID为: IDD_PROGRESS,标题为:进展条对话框,为该对话框建类,类名为:CProgressDlg (3)删除原来的Cancel按钮,将OK按钮标题改为“退出”。 (4)参照图3.5.40的控件布局,用编辑器为对话框添加如表3.29所示的一些控件。 表3.29 添加的控件 添加的控件 ID号 标题 其他属性 静态文本 IDC_STATIC_TEXT 默认 AlignText:设为Center,其余默认 进展条 IDC_PROGRESS1 默认 按钮(后退) IDC_BUTTON_BACK 默认 按钮(继续) IDC_BUTTON_GOON 默认 (5)为静态文本IDC_STATIC_TEXT控件添加一个CString类型的成员变量m_strPercent,为进展条IDC_PROGRESS1控件添加一个CProgressCtrl类型的成员变量m_Progress (6)单击项目工作区ClassView,右键对准对话框类CProgressDlg,单击Add Member Function…添加一个void类型的成员函数UpdatePercentText(),用来当进展条位置变化后更新静态文本控件显示的百分比,并加代码: void CProgressDlg::UpdatePercentText() { int nPos = m_Progress.GetPos(); //获取进展条的当前位置 int nLow,nUp; //进展条起初和最末位置 m_Progress.GetRange(nLow,nUp); //获取进展条范围 第3章 对话框与控件 m_strPercent.Format("%4.0f%%",(float)nPos/(float)(nUp-nLow)*100.0); UpdateData(FALSE); // 将成员变量的数据赋给控件 } (7)用MFC ClassWizard为CProgressDlg类添加WM_INITDIALOG消息映射,并添加下列代码: BOOL CProgressDlg::OnInitDialog() { CDialog::OnInitDialog(); m_Progress.SetRange(0,100); //设置进展条范围 m_Progress.SetStep(5); //设置进展条步长 m_Progress.SetPos(30); //设置进展条的当前位置 UpdatePercentText();//自定义函数,当进展条位置变化后更新静态文本控件显示的百分比 return TRUE; // return TRUE unless you set the focus to a control } (8)为按钮IDC_BUTTON_BACK添加BN_CLICKED的消息映射,并增加代码: void CProgressDlg::OnButtonBack() { int nPos=m_Progress.GetPos(); //获取进展条的当前位置 int nLow,nUp; m_Progress.GetRange(nLow,nUp); //获取进展条范围 nPos=nPos-5;//进展条当前位置-步长(后退) if(nPosUpdateWindow(); Draw(); } (5)在CMyDlg.cpp文件中的OnInitDialog()函数中增加下列代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); ……. ……… ……. 第3章 对话框与控件 m_Scroll.SetScrollRange(0,255); m_Scroll.SetScrollPos(m_RValue); UpdateData(FALSE); //将成员变量数据传给控件 m_bEditOK=TRUE; return TRUE; // return TRUE unless you set the focus to a control } (6)用ClassWizard为对话框类CMyDlg添加WM_HSCROLL映射消息,并加代码: void CMyDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int nID = pScrollBar->GetDlgCtrlID(); //获取对话框中控件的ID号 if(nID == IDC_SCROLLBAR1) { switch(nSBCode) { case SB_LINELEFT: m_RValue--; break; //单击滚动条左箭头 case SB_LINERIGHT: m_RValue++; break; //单击滚动条右箭头 case SB_PAGELEFT: m_RValue -=10; break; case SB_PAGERIGHT: m_RValue +=10; break; case SB_THUMBTRACK: m_RValue = nPos; break; } if(m_RValue<0) m_RValue = 0; if(m_RValue>255) m_RValue = 255; m_Scroll.SetScrollPos(m_RValue); } UpdateData(FALSE); //将成员变量数据传给控件 Draw(); CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (7)为第5步添加的Draw()函数,加下列代码: void CMyDlg::Draw() { CWnd *pWnd=GetDlgItem(IDC_DRAW);//获得静态文本的起始地址,即ID标识 CDC *pDC=pWnd->GetDC(); //获得窗口当前的设备环境指针 CBrush drawBrush; //定义画刷变量 drawBrush.CreateSolidBrush(m_RValue);//建立画刷颜色值 CBrush *pOldBrush=pDC->SelectObject(&drawBrush); 第3章 对话框与控件 CRect rcClient; pWnd->GetClientRect(rcClient); pDC->Rectangle(rcClient); pDC->SelectObject(pOldBrush); } (8)编译运行,结果见图3.5.42所示。 说 明:由于滚动条中间的滚动块在默认时是不会停止在用户操作的位置处的,因此需要调用 SetScrollPos函数来进行相应位置的设定 1、滚动条(CScrollBar类)的操作函数 (1)GetScrollPos()函数,格式:int GetScrollPos() const; 该函数返回滚动框的当前位置,若操作失败则返回0 (2)SetScrollPos()函数,格式:int SetScrollPos(int nPos,BOOL bRedraw = TRUE); 该函数将滚动框移动到指定位置。 参 数: nPos指定了新的位置。 bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘。 函数返回滚动框原来的位置,若操作失败返回0。 (3)GetScrollRange()函数,格式: void GetScrollRange(LPINT lpMinPos,LPINT lpMaxPos)const; 该函数对滚动条的滚动范围进行查询。 参 数: lpMinPos和lpMaxPos分别指向滚动范围的最小和最大值 (4)SetScrollRange()函数,格式: void SetScrollRange(int nMinPos,int nMaxPos,BOOL bRedraw=TRUE); 该函数用于指定滚动条的滚动范围。 参 数: nMinPos和nMaxPos分别指定了滚动范围的最小和最大值,这两者指定的滚动范围不得超过32767当两者都为0时,滚动条将被隐藏。 bRedraw 表示是否需要重绘滚动条,如果为TRUE,则重绘。 (5)GetScrollnfo()函数,格式: BOOL GetScrollInfo(LPSCROLLINFO lpScrollInfo,UINT nMask); 该函数用来获取滚动条的各种状态,包括滚动范围、滚动框的位置和页尺寸。 第3章 对话框与控件 参 数: 1)nMask的意义与SCROLLINFO结构中的fMask相同,函数在获得有效值后返回TRUE,否则返回FALSE。 2)lpScrollInfo指向一个SCROLLINFO结构,该结构定义如下: Typedf struct tagSCROLLINFO { UINT cbSize; 结构的尺寸 UINT fMask; 说明结构中的哪些参数是有效的,可以是屏蔽值的组合,如:SIF_POS|SIF_PAGE若为SIF_ALL则整个结构都有效 int nMin; 滚动范围最大值,当fMask中包含SIF_RANGE时有效 int nMax; 滚动范围最小值,当fMask中包含SIF_RANGE时有效 UINT nPage; 页尺寸,用来确定比例滚动框的大小,当fMask中包含SIF_PAGE时有效 int nPos; 滚动框的位置,当fMask中包含SIF_POS有效 int nTrackPos; 滚动时滚动框的位置,当fMask中包含SIF_TRACKPOS时有效,该参数只能查询,不能设置,最好不要用该函数来查询拖动时滚动框的位置 }SCROLLINFO; typedef SCROLLINFO FAR *LPSCROLLINFO; (6)SetScrollInfo()函数,格式: BOOL SetScrollInfo(LPSCROLLINFO lpScrollnfo,BOOL bRedraw=TRUE); 该函数用于设置滚动条的各种状态,如设定页尺寸,从而实现比例滚动框。 参 数: 1)lpScrollInfo指向一个SCROLLINFO结构。 2)bRedraw表示是否需要重绘滚动条,如果为TRUE,则重绘,若操作成功,返回TRUE,否则返回FALSE。 说 明:CWnd类也提供了一些函数来查询和设置所属的标准滚滚动条。这些函数与CScrollBar类的函数同名,且功能相同,但每个函数都多了一个参数,用来选择滚动条。 (7)GetScrollPos函数,格式:int GetScrollPos(int nBar)const; 参 数: nBar用来选择滚动条,可以为下列值: SB_CTL:表示滚动条为控件滚动条 SB_HORZ:指定水平滚动条 SB_VERT:指定垂直滚动条 (8)OnHScroll()和OnVScroll()函数 无论是标准滚动条,还是滚动条控件,滚动通知消息都是用WM_HSCROLL和WM_VSCROLL消息发送出去的。对这两个消息的默认处理函数是CWnd::OnHScroll和CWnd::OnVScroll,一般需要在派生类中对这两个函数从新设计,以实现滚动功能。这两个函数的声明格式为: 第3章 对话框与控件 afx_msg void OnHScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar); afx_msg void OnVScroll(UINT nSBCode,UINT nPos,CScrollBar *pScrollBar); 参 数: 1)nSBCode是通知消息码,主要通知码如表3.31所示 2)nPos是滚动框的位置,只有在nSBCode为SB_THUMBPOSITION或SB_THUMBTRACK时,该参数才有意义。如果通知消息是滚动条控件发来的,那么pScrollBar是指向该控件的指针,如果是标准滚动条发来的,则pScrollBar为NULL。 2、WM_HSCROLL和WM _VSCROLL消息 用户对滚动条进行操作时,滚动条就会向父窗口发送WM_HSCROLL或WM_VSCROLL消息,这些消息是通过ClassWizard在滚动条父窗口中进行映射的,并产生相应消息映射函数OnHScroll和OnVScroll。表3.31列出了滚动条控件的通知消息。 表3.31 滚动条控件的通知消息 通知消息 说明 SB_ENDSCROLL 结束滚动 SB_LEFT、SB_RIGHT 滚动到最左端或最右端 SB_LINELEFT、SB_LINERIGHT 向左或向右滚动一行(或一个单位) SB_PAGELEFT、SB_PAGERIGHT 向左或向右滚动一页 SB_THUMBPOSITION 滚动到某绝对位置 SB_TOP、SB_BOTTOM 滚动到最上端或最下端 SB_LINEUP、SB_LINEDOWN 向上或向下滚动一行(或一个单位) SB_PAGEUP、SB_PAGEDOWN 向上或向下滚动一页 SB_THUMBTRACK 拖动滚动块 3.5.9 滑动条 一个滑动条(Slider)(也称滑块或游标)是包含一个滑动块和可选刻度线的窗口。当用鼠标或方向键拖动滑动块时,该控件会发送通知消息来表明这些改变,图3.5.43所示的 是水平滑动条和垂直滑动条的样子。 滑动条是按照应用程序中指定的增量来 第3章 对话框与控件 移动的。例如:如果指定滑动条的范围为8, 则滑动条只能有9个位置,既在滑动条最左 边的一个位置和另外8个在此范围内每隔一 个增量的位置。通常,这些位置都是由相应 的刻度线来标识的。滑动条控件的各种操作 封装在了MFC的CSliderCtrl类中。 图3.5.43 垂直与水平滑动条 例3.24 设置对话框背景颜色 我们通过映射WM_CTLCOLOR(当子窗口将要绘制时发送的消息,以便能使用指定的颜色来绘制)消息达到改变背景颜色的目的。本例用三个滑动条来调整,即Visual C++所使用的RGB 颜色的三个分量:R(红色分量)、G(绿色分量)、B(蓝色分量)。如图3.5.44所示。 (1)创建一个单文档应用程序名为:滑动条 (2)向应用程序中添加一个对话框资源ID 为IDD_COLOR,标题为:调整背景颜色, 为此对话框建类,类名为:CBkColorDlg (3)删除原来的Cancel按钮,将OK按钮 的标题改为“退出”。 (4)参照图3.5.44的控件布局,用编辑器为对 话框添加表3.32所示的控件和成员变量。 (5)在对话框类CBkColorDlg.h文件中,添 图3.5.44 调整对话框背景颜色 加一个画刷类成员变量:CBrush m_Brush;用来设置对话框背景所需要的画刷。 (6)用MFC ClassWizard为对话框CBkColorDlg类添加WM_INITDIALOG消息映射,并加下列代码: BOOL CBkColorDlg::OnInitDialog() { CDialog::OnInitDialog(); m_sliderRed.SetRange(0,255); //红色滑动条的最小和最大位置 m_sliderGreen.SetRange(0,255); //绿色滑动条的最小和最大位置 m_sliderBlue.SetRange(0,255); //蓝色滑动条的最小和最大位置 m_nRed=200; //最初红色滑动条的位置在200处 UpdateData(FALSE); //将成员变量数据传给控件 return TRUE; } 第3章 对话框与控件 表3.32 添加的控件和关联的成员变量 控件 ID号 变量类别 变量类型 变量名 标题 静态文本 默认 -- -- -- R(红色) 滑动条(红色) IDC_SLIDER_RED Control CSliderCtrl m_sliderRed 滑动条(红色) IDC_SLIDER_RED Value int m_nRed 静态文本 默认 -- -- -- G(绿色) 滑动条(绿色) IDC_SLIDER_GREEN Control CSliderCtrl m_sliderGreen 滑动条(绿色) IDC_SLIDER_GREEN Value int m_nGreen 静态文本 默认 -- -- -- B(蓝色) 滑动条(蓝色) IDC_SLIDER_BLUE Control CSliderCtrl m_sliderBlue 滑动条(蓝色) IDC_SLIDER_BLUE Value int m_nBlue (7)用MFC ClassWizard为对话框CBkColorDlg类添加WM_HSCROLL消息映射,并添加下列代码: void CBkColorDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int nID = pScrollBar->GetDlgCtrlID(); //获取对话框中控件的ID号 if(nID == IDC_SLIDER_RED) // 如果操作的是红色滑动条 { switch(nSBCode) // 按是哪一种操作进行处理 { case SB_LINELEFT: m_nRed--; //向左滚动一行(或一个单位)表3.32所示 break; case SB_LINERIGHT: m_nRed++; //向右滚动一行(或一个单位)表3.32所示 break; case SB_PAGELEFT: m_nRed -=10; //向左滚动一页, 表3.32所示 break; case SB_PAGERIGHT: m_nRed +=10; //向右滚动一页, 表3.32所示 break; case SB_THUMBTRACK: m_nRed = nPos; //拖动滚动块, 表3.32所示 break; } if(m_nRed<0) m_nRed = 0; // 红颜色值若小于零,当零处理 if(m_nRed>255) m_nRed = 255; //红颜色值若大于255,当255处理 m_sliderRed.SetScrollPos(0,m_nRed);//将红色滑块的初始位置定在m_nRed=200处 第3章 对话框与控件 } Invalidate(); CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (8)用MFC ClassWizard为对话框CBkColorDlg类添加WM_CTLCOLOR(设置对话框的颜色)消息映射,并添加下列代码: HBRUSH CBkColorDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); UpdateData(TRUE); // 将控件的数据传给成员变量 COLORREF color = RGB(m_nRed,m_nGreen,m_nBlue);//定义红、绿、蓝三种颜色 m_Brush.Detach(); // 使画刷和对象分离 m_Brush.CreateSolidBrush(color);//建立COLOR颜色画刷(红、绿、蓝) pDC->SetBkColor(color); //设置COLOR背景颜色(红、绿、蓝) return (HBRUSH) m_Brush; //返回画刷句柄,以便系统使此画刷绘制对话框 // return hbr; } 说 明:在上述代码中,COLORREF是用来表示RGB颜色的一个32位的数据类型,它是Visual C++中一种专门用来定义颜色的数据类型,至于画刷的用法以后还要讨论。 (9)打开菜单资源,在顶层菜单项中建个名为:“测试(&C)”的菜单,再在其下面建个名为:调整背景颜色(&O),ID为ID_TEST_COLOR (10)用MFC ClassWizard为CMainFrame类添加菜单项ID_TEST_COLOR的COMMAND消息映射,取默认的映射函数名,并添加下列代码: void CMainFrame::OnTestColor() { CBkColorDlg dlg; dlg.DoModal(); } (11)在文件MainFrm.cpp的前面添加CBkColorDlg类的包含语句: #include “BkColorDlg.h” (12)编译运行,结果如图3.5.44所示。 1、滑动条操作函数 (1)范围和位置的设置和获取 SetRange和SetPos函数分别用来设置滑块的范围和位置,其原型如下: void SetRange(int nMin,int nMax,BOOL Bredraw=FALSE); 第3章 对话框与控件 void SetPos(int nPos); 参 数: 1)nMin和nMax表示滑块的最大和最小位置 2)bRedraw是重画标志 3)nPos表示新的滑块 还可以使用SetRangeMax和SetRangeMin函数设置滑块的最大和最小位置,这两个函数的原型如下: void SetRangeMax(int nMax,BOOL bRederw=FALSE); void SetRangeMin(int nMin, BOOL bRedraw=FALSE); 参 数: 1)nMax和nMin表示滑块的最大和最小位置 2)bRedraw是重画标志 与函数SetRange、SetPos、SetRangeMax和SetRangeMin相应的函数是GetRange、GetPos、GetRangeMax和GetRangeMin,这四个函数的原型如下: void GetRange(int &nMin,int &nMax) const; int GetPos() const; int GetRangeMin() const; int GetRangeMax() const; (2)选择范围的设置 SetSelection函数是设置一个滑动块控件中当前选择的开始和结束位置,其原型如下: void SetSelection(int nMin,int nMax); 参 数: nMin和nMax分别表示滑块的开始和结束位置 (3)刻度线尺寸的设置和清除 SetTic函数是设置滑块控件的一个刻度线的位置,函数成功调用后返回非零值,否则返回0,其原型:BOOL SetTic(int nTic); 参 数:nTic表示刻度线的位置 SetTicFreq设置显示在滑块中刻度线的疏密程度,其原型如下:void SetTicFreq(int nFreq); 参 数:nFreq表示刻度线的疏密程度,例如,如果参数设置为1,则在滑块的范围内每一个增量显示一个刻度线。若使这个函数有效,必须在属性对话框中选中Auto ticks项。 ClearTics函数,用来从滑块控件中删除当前的刻度线,函数原型如下: void ClearTics(BOOL bRedraw = FALSE); 第3章 对话框与控件 参 数:bRedraw表示重画标志,若该参数为TRUE,则在选择被清除后重画滑动条。 2、滑动条的通知消息 滑动条的通知消息有: TB_BOTTON、TB_ENDTRACK、TB_LINEDOWN、TB_LINEUP、TB_PAGEDOWN、 TB_PAGEUP、TB_THUMBPOSITION、TB_THUMBTRACK、TB_TOP,其各自的含义见表3.31。这些消息代码都来自于 WM_HSCROLL或WM_VSCROLL消息。 注意:编写程序时用TB_不行,说是没定义, 而要与滚动条的通知消息一样用SB_就行了。 3、滑动条的风格 滑动条控件有许多风格,它们都可以 通过滑动条控件的属性对话框进行设置, 图3.5.45 滑动条属性对话框 如图3.5.45所示。表3.33列出了该属性对话框的各项含义。 表3.33 滑动条控件的Styles属性 项 目 说 明 方位(Orientation) 控件放置方向:Vertical(垂直)、Horizontal(水平,默认) 点(point) 刻度线在滑动条控件中放置的位置:Both(两边都有)、Top/Left(水平滑动条 的上边或垂直滑动条的左边,同时滑动块的尖头指向右刻度线的那一边)、 Bottom/Right(水平滑动条的下边或垂直滑动条的右边,同时滑动块的尖头 指向有刻度线的那一边) Tick marks 选中此项,在滑动条控件上显示刻度线 Auto ticks 选中此项,滑动条控件上的每个增量位置处都有刻度线,并且增量大小自 动根据其范围来确定 边框(Border) 选中此项,控件周围有边框 允许选择(Enable selection) 选中此项,控件中供用户选择的数值范围高亮显示 第3章 对话框与控件 例3.25 用滑动条选择画线的宽度是2,用编辑框和旋转按钮选择矩形的长和宽是 120、100,单击“应用”后,在对话框的右边画一个矩形,如图3.5.46所示。 (1)创建一个基于对话框的应用程序,名为:绘制矩形 (2)参照图3.5.46的控件布局,按表3.34所示,向对话框添加控件和关联的成员变量。 注 意:对于滑动条(IDC_LINEWIDTHSLIDER)控件,需要设置Styles属性:置“Tick marks”、 “Auto ticks”、“Enable selection” Point(下拉):置“Botton/Right”。对于旋转按钮Style属性(IDC_SPIN1, IDC_SPIN2) Alignment: (下拉)置Right,并置Auto buddy,Set buddy integer,Wrap。 (3)在“绘制矩形Dlg.cpp”实现文件中的OnInitDialog函数中添加如下代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); … … … //设置旋转按钮宽的范围10-100 m_WidthSpin.SetRange(10,100); //设置旋转按钮的当前位置0 m_WidthSpin.SetPos(0); //设置旋转按钮的长范围10-100 m_LengthSpin.SetRange(10,100); //设置旋转按钮的当前位置0 m_LengthSpin.SetPos(0); //设置滑动块的范围1-10 图3.5.46 运行结果 m_LineWidthSlider.SetRange(1,10); //设置滑动块的当前位置 m_LineWidthSlider.SetPos(0); m_LineWidthSlider.SetTicFreq(1);//设置滑动块刻度标尺,每1个单位一个标记 SetDlgItemText(IDC_LINEWIDTH,"线宽:1");//设置静态文本显示的内容 return TRUE; // return TRUE unless you set the focus to a control } 表3.34 添加的控件和关联的成员变量 控件名称 ID值 Caption(标题) 成员变量 第3章 对话框与控件 静态文本 长 编辑框 IDC_LENGHT int m_Length 旋转按钮 IDC_SPIN1 CSpinButtonCtrl m_LengthSpin 静态文本 宽 编辑框 IDC_WIDTH int m_Width 旋转按钮 IDC_SPIN2 CSpinButtonCtrl m_WidthSpin 静态文本 IDC_LINEWIDTH (将该控件拉长一些) 滑动条 IDC_LINEWIDTHSLIDER int m_LineWidth 滑动条 IDC_LINEWIDTHSLIDER CsliderCtrl m_LineWidthSlider 按钮 IDC_APPLY 应用 CButton m_Room 按钮 EXITBUTTON 退出 (4)为按钮(应用)IDC_APPLY的BN_CLICKED消息添加消息映射函数,并加代码: void CMyDlg::OnApply() { UpdateData(); //用屏幕上的当前值更新控制变量 Invalidate(); //使用户视图区无效,以清除上一次画的图 UpdateWindow(); //刷新用户视图区域 CClientDC dc(this); CPen mypen;//定义画笔类对象 mypen.CreatePen(PS_SOLID,m_LineWidth,RGB(255,0,0));//创建画笔 CPen *pOldpen=dc.SelectObject(&mypen);//将画笔选入设备环境 dc.Rectangle(70,10,150+m_Length,30+m_Width);//画一个矩形 dc.SelectObject(pOldpen);//恢复原先的画笔 } (5)为按钮IDC_EXITBUTTON的BN_CLICKED消息添加消息映射函数,并加代码: void CMyDlg::OnExitbutton() { OnOK(); } (6)为对话框CMyDlg类添加WM_HSCROLL水平滚动消息的处理函数OnHScroll,当滑块的位置改变后,静态文本的内容随之改变。 第3章 对话框与控件 void CMyDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { UpdateData(); // 将控件的数据传给成员变量 CString LineWidth; LineWidth.Format(" 线宽:%d",m_LineWidth); SetDlgItemText(IDC_LINEWIDTH,LineWidth);//设置静态文本的显示内容 CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } (7)编译运行,结果见图3.5.46所示 3.6 标签控件、图像列表、属性表及属性页 标签(Tab)控件也称为选项卡,是一个分割成多个页面的窗口,各页按顺序排列,每次只能看到一个页(当前页)中的控件,其他页只有标题可见。单击页的标题,可使相应的页可见(置为当前页),使用【Tab】键可顺序显示各页内容。卡片式对话框适用于管理多内容多输入的信息。使用卡片式对话框比使用多个对话框来完成同样的功能编程接口更简洁,如MFC ClassWizard类向导对话框就是一个卡片式对话框。 编程时使用标签控件,用户可以在一 个窗口的相同区域定义多个页面,每个页 面上包括一些不同的控件,实现不同的功 能。需要注意的是,标签作为一个控件使 用,由于它不是对话框,因此不能直接在 各个标签页上添加控件,只能在选中不同 标签时,在相同位置显示含有不同控件的 对话框。标签控件实现的是在不同对话框 窗口之间的切换,而不只是标签页上控件 的现实切换。下面我们通过一个例子,看 (1)基本信息 看标签控件的应用。 3.6.1 标签控件 例3.26 使用无模式对话框来构造标签 页面,开始将所有的标签页面创建好, 第3章 对话框与控件 然后根据所有选择的当前标签选项决定 哪个页面需要显示,哪个页面需要隐藏 和禁用。图3.6.1是本程序的运行结果。 (2)成绩 (1)用MFC AppWizard(exe)创建一个基 于对话框的应用程序,名为:标签控件 (2)参照图3.6.1(3),添加一个默认 的标签控件IDC_TAB1,调整其大小。 (3)为CMyDlg类添加标签控件 (IDC_TAB1)所关联的CTabCtrl类型 的成员变量m_Tab (4)添加3个对话框资源IDD_DIALOG1, IDD_DIALOG2,DIALOG3,分别设置这3 (3)备注 个对话框的Styles属性为:Styles Child, 图3.6.1 运行结果的三个页面 Border None,并用ClassWizard依次为这 3个对话框建类,类名分别为:CTab1Dlg、CTab2Dlg、CTab3Dlg。 (5)将这3个对话框尽量缩小,参照图3.6.1和3.6.2,按表3.35所示,分别向这3个 对话框添加控件。 图3.6.2 设置对话框属性 第3章 对话框与控件 表3. 35 向这3个对话框添加的控件 控 件 ID号 标题 属性 对话框 静态文本 IDC_STATIC_NAME 姓名 默认 编辑框 IDC_EDIT_NAME -- 默认 静态文本 IDC_STATIC_SEX 性别 默认 单选按钮 IDC_RADIO_MAN 男 默认 IDD_DIALOG1 单选按钮 IDC_RADIO_WOMAN 女 默认 静态文本 IDC_STATIC_ID 学号 默认 编辑框 IDC_EDIT_ID -- 默认 静态文本 IDC_STATIC_ENG 英语 默认 编辑框 IDC_EDIT_ENG -- 默认 静态文本 IDC_STATIC_MAT 数学 默认 IDD_DIALOG2 编辑框 IDC_EDIT_MAT -- 默认 静态文本 IDC_STATIC_COM 计算机 默认 编辑框 IDC_EDIT_COM -- 默认 静态文本 IDC_STATIC_REMARK 备注 默认 编辑框 IDC_EDIT_REMARK -- Multiline Vertical scroll IDD_DIALOG3 Horizontal scroll (6)打开项目工作区的类页面(ClassView),点开“标签控件classes”,右键单击CMyDlg, 从弹出的快捷菜单中选择:Add Member Variables,添加下列成员变量: public: Variable Type: CImageList Variable Name: m_ImageList Variable Type: CTab3Dlg Variable Name: *m_pTab3Dlg Variable Type: CTab2Dlg Variable Name: *m_pTab2Dlg Variable Type: CTab1Dlg Variable Name: *m_pTab1Dlg (7)打开项目工作区的类页面 (ClassView),点开“标签控件classes”,右键单击CMyDlg,从弹出的快捷菜单中选择Add Member Function,添加成员函数:void SetDlgState(CWnd *pWnd,BOOL bShow)和void DoTab(int nTab),并加代码: 第3章 对话框与控件 void CMyDlg::SetDlgState(CWnd *pWnd, BOOL bShow) { pWnd->EnableWindow(bShow); if(bShow) { pWnd->ShowWindow(SW_SHOW);//显示 pWnd->CenterWindow();//居中显示 } else pWnd->ShowWindow(SW_HIDE);//隐藏 } void CMyDlg::DoTab(int nTab) { if(nTab>2) nTab=2; //确定nTab值本能超过范围 if(nTab<0) nTab=0; BOOL bTab[3]; bTab[0]=bTab[1]=bTab[2]=FALSE; bTab[nTab]=TRUE; //切换对话框的显示和隐藏 SetDlgState(m_pTab1Dlg,bTab[0]); SetDlgState(m_pTab2Dlg,bTab[1]); SetDlgState(m_pTab3Dlg,bTab[2]); } (8)用ClassWizard为IDC_TAB1映射TCN_SELCHANGE消息,并添加代码: void CMyDlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) { int nSelect = m_Tab.GetCurSel();//获得当前选择的标签项 if(nSelect>=0) DoTab(nSelect); *pResult = 0; } (9)InsertàResourceà选中IconàImport向程序中添加3个图标文件(.ico),并取默认的图标ID号,即:IDI_ICON1、IDI_ICON2、IDI_ICON3。 (10)在CMyDlg::OnInitDialog中添加下列代码: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); m_pTab1Dlg=new CTab1Dlg();//为无模式对话框分配空间 第3章 对话框与控件 m_pTab2Dlg=new CTab2Dlg(); m_pTab3Dlg=new CTab3Dlg(); //创建无模式对话框,指定标签控件为无模式对话框的父窗口 m_pTab1Dlg->Create(IDD_DIALOG1,&m_Tab); m_pTab2Dlg->Create(IDD_DIALOG2,&m_Tab); m_pTab3Dlg->Create(IDD_DIALOG3,&m_Tab); m_ImageList.Create(16,16,ILC_COLOR|ILC_MASK,3,0); //创建图像列表 m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1)); //从图标加到图像列表中 m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2)); //从图标加到图像列表中 m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3)); //从图标加到图像列表中 m_Tab.SetImageList(&m_ImageList); //设置TAB控件所适用的图像列表 m_Tab.InsertItem(0, "基本信息", 0); m_Tab.InsertItem(1, "成绩", 1); m_Tab.InsertItem(2, "备注", 2); m_Tab.SetMinTabWidth(80); //设置标签的最小宽度 m_Tab.SetPadding(CSize(12,3));//设置标签项和图标周围的间隔 m_Tab.SetCurSel(0); DoTab(0); return TRUE; } 注 :该函数中原代码可全注释掉 做到此,可运行一下,中间结果如图3.6.3所示. (11)用CassWizard为CMyDlg类添 加WM_DESTROY的消息映射,当 CMyDlg退出时,删除分配给无模式对 话框的内存,代码如下: void CMyDlg::OnDestroy() { CDialog::OnDestroy(); if(m_pTab1Dlg) delete m_pTab1Dlg; if(m_pTab2Dlg) delete m_pTab2Dlg; 第3章 对话框与控件 if(m_pTab3Dlg) delete m_pTab3Dlg; 图 3.6.3 中间结果 } (12)用ClassWizard为CTab1Dlg类添加WM_INITDIALOG消息映射,并加下列代码: BOOL CTab1Dlg::OnInitDialog() { CDialog::OnInitDialog(); CheckRadioButton(IDC_RADIO_MAN,IDC_RADIO_WOMAN,IDC_RADIO_MAN); return TRUE; } (13)编译运行,结果见图3.6.1所示。 通过使用标签控件,应用程序可以将一个窗口或对话框的相同区域定义为多个页面。每一页包含了一套信息或一组控件,当用户选择了相应的标签时,应用程序就会显示相应的信息或控件。 1、标签控件的风格 标签控件有许多风格,这些风 格用来定义标签控件的外观及操作, 方式它们可以在标签控件属性对话 框中进行设置,如图3.6.4所示,表 3.36列出了该属性对话框的各项含义。 图3.6.4 标签控件的属性对话框 表3. 36 标签控件的Styles属性 项 目 说 明 Alignment(对齐) 标签的调整方式:Right Justify(向右调整)、Fixed Width(宽度相 同)、Ragged Right(不拉伸每一行标签来使之适合标签控件的整 个宽度)当选择Fixed Width方式后,Force label left和Force icon left两个选项被激活。 Focus(焦点) 焦点方式:Default(默认)、On Button Down(当点击一个标签时 第3章 对话框与控件 接受输入焦点)、Never(永远不会接收输入焦点) Button(按钮) 选中此项,标签看起来像按钮一样 ToolTips(工具提示) 选中此项,表示该标签控件具有一个与之关联的工具提示 Border(边框) 选中此项,控件周边有边框 MuItiline(使用多行标签) 选中此项,可使一个标签控件显示多行标签,因此所有的标签 都可以同时显示(默认时,一个标签控件只显示一行标签) Owner draw fixed(所有者固定) 选中此项,使自画标签具有相同的高度 Force label left(强制标号左) 选中此项,左对齐图标和标签 Force icon left(强制图标左) 选中此项,将图标集中在左边,但标签仍然在中间(默认时, 标签控件将图标和标签都放在中间,图标在标签的左边)。 2、标签控件的基本操作 MFC的CTabCtrl类封装了标签控件的各种操作,其操作函数的原型如下: (1)添加标签:CTabCtrl类成员函数InsertItem用来向标签控件中增加标签: BOOL InsertItem(int nItem,TCITEM *pTabCtrlItem); BOOL InsertItem(int nItem,LPCTSTR lpszItem); BOOL InsertItem(int nItem,LPCTSTR lpszItem,int nImage); BOOL InsertItem(UINT nMask,int nItem,LPCTSTR lpszItem,int nImage,LPARAM lParam); 参 数: nItem指定新标签的索引 pTabCtrlItem是指定标签属性的TCITEM结构的指针,TCITEM结构的定义如下: typedef struct tagTCITEM { UINT mask; #if( _WIN32_IE >=Ox0300 DWORD dwState; DWORD dwStateMask; #else UINT lpReserved1; UINT lpReserved2; 第3章 对话框与控件 #endif LPTSTR pszText; int cchTextMax; int iImage; LPARAM lParam; } TCITEM,FAR *LPTCITEM; lpszItem 表示插入标签的指针 nImage表示插入图像的索引 nMask指定设置的TCITEM结构属性,它的取值可以是0或者下列值的组合: TCIF_TEXT、TCIF_IMAGE、TCIF_PARAM、TCIF_RTLREADING、TCIF_STATE lParam是与标签关联的应用程序定义的数据 (2)成员函数SetItemSize、SetPadding及SetMinTabWidth分别用来设置某个项的宽度和高度、图标和标签周围的间隔以及设置标签项的最小宽度。而GetItemRect用来获取标签的边界大小,它们的原型如下: CSize SetItemSize(CSize size); void SetPadding(CSize size); int SetMinTabWidth(int cx); BOOL GetItemRect(int nItem,LPRECT lpRect)const; (3)成员函数DeleteItem表示在一个标签控件中删除某一标签项,而DeleteAlltems则删除所用的项。它们的原型如下: BOOL DeleteItem(int nItem); BOOL DeleteAllItem(); 参 数:nItem用来指定一个标签索引(0表示第一个标签) (4)在标签控件中使用图像列表时,必须调用CTabCtrl::SetImageList函数来指定一个已创建的图像列表,函数原型如下: CImageList *SetImageLise(CImageList *pImageList); 参 数:pImegeList:用来表示一个图像列表指针,函数返回以前的图像列表指针,若以前没有图像列表指针,则返回NULL. (5)成员函数SetCurSel和GetCurSel分别用来设置和获取当前选项的标签项,它们的原型如下: int SetCurSel(int nItem); int GetCurSel()const; 参 数:nItem:用来表示当前选项的索引,函数GetCurSel返回当前选项的标签的从零开始的索引,如果没有标签被选择,则返回-1。 第3章 对话框与控件 (6)成员函数DeselectAll用来重新排列一个标签控件中的全部标签项,而函数HighlightItem是使一个标签项处于高亮(选择)状态。它们的原型如下: void DeselectAll(BOOL FExcludeFocus); BOOL HighlightItem(int idItem,BOOL fHighlight = TRUE); 参 数: fExcludeFocus:用来指定一个重排标志,如果这个参数被设置为FALSE,则所有的标签按钮都将被重新排列;如果被设置为TRUE,则除了当前选择的标签外,其他所有的标签都将被重印排列。 idItem: 用来指定一个标签的索引。 fHighlight:指定要设置的高亮状态;如果这个值是TRUE,则该标签被选择;如果这个值是FALSE,则该标签被设置为默认状态。 3、标签控件的通知消息 标签控件的通知消息常见的有:TCN_KEYDOWN,TCN_SELCHANGE和TCN_SELCHANGING,分别表示用户按下某键、当前标签选项已被改变和当前标签选项将要改变。 3.6.2 图像列表控件 图像列表控件常常用来有效地管理多个位图和标签。在MFC中,图像列表控件使用CImageList类来创建、显示或管理图像的。 1、图像列表的创建 图像列表的创建不像其他控件,它不能通过对话框编辑器来创建。因此,创建一个图像列表首先要声明一个CImageList对象,然后调用Create函数。由于Create函数的重载很多,故这里给出最常用的一个原型: BOOL Create(int cx,int cy,UINT nFlags,int nInitial,int nGrow); 参 数: cx、cy用来指定图像的像素大小; nFlags表示要创建的图像类型,一般取其ILC_COLOR和ILC_MASK(指定屏蔽图像)的组合,默认的ILC_COLOR为ILC_COLOR4(16色),当然也可以是ILC_COLOR8(256色)、ILC_COLOR16(16位色)等; nInitial用来指定图像列表中最初的图像数目; nGrow表示当图像列表的大小发生改变时图像可以增加的数目。 2、图像列表的基本操作 第3章 对话框与控件 常见的图像列表的基本操作有:增加、删除和绘制等,其相关成员函数如下: int Add(CBitmap *pbmImage,CBitmap *pbmMask); int Add(CBitmap *pbmImage,COLORREF crMask); int Add(HICON hIcon); 上述函数用来向一个图像列表添加一个图标或多个位图。成功时返回第一个新图像的索引号,否则返回-1。 参 数: pbmImage表示包含图像的位图指针 pbmMask表示包含屏蔽的位图指针 crMask表示屏蔽色 hIcon表示图标句柄 BOOL Remove(int nImage); 该函数用来从图像列表中删除一个由nImage指定的图像,成功时返回非0,否则返回0。 BOOL Draw(CDC *pdc,int nImage,POINT pt,UINT nStyle); 该函数用来在由pt指定的位置处绘制一个图像。 参 数: pdc表示绘制的设备环境指针。 nImage表示要绘制的图像的索引号。 nStyle用来指定绘制图像时所采用的方式。 HICON ExtractIcon(int nImage); 该函数用来将nImage指定的图像扩展为图标。 COLORREF SetBkColor(COLORREF cr); 该函数用来设置图像列表的背景色,它可以是CLR_NONE。成功时返回先前的背景色,否则为CLR_NONE。 图像列表控件的应用见例3.26第10步中的代码。 3.6.3 属性表及属性页 属性表(property sheet)也称为属性对话框,类似于标签对话框。一个属性表由一个CpropertySheet(属性表)类对象和多CpropertyPag(属性页)类对象构成。其中,CPropertySheet类或其派生类对象代表一个属性表,CPropertyPage派生类对象代表每个属性页。每个属性页对应一个对话框,用于进行数据的输入输出。CPropertyPage类派生于CDialog类,因此每个属性页就是一个对话框。属性表编程的主要步骤如下: 第3章 对话框与控件 1)为每个属性页创建属性对话框。 2)为属性对话框添加控件。 3)为每个属性对话框创建CPropertyPage类的派生类,并添加成员变量和消息处理函数。 4)声明一个CPropertySheet(属性表)类对象和所有的CPropertyPage(属性页)派生类对象,调用函数AddPage将属性页加入到属性表,显示该属性表。 5)对于模式对话框属性表,调用CPropertySheet::Domodal函数来显示,对于非模式对话框属性表,调用CPropertySheet::Create函数来显示。 在资源编辑器中创建属性页类似于创建对话框资源模板,但它们在以下几个方面属性的设置是与对话框不同的: 1)Caption(标题)属性的值出现在选项卡上,而不是出现在对话框的标题上。 2)属性页的Style属性必须被设置为Child(子类)。 3)页的Border(边框)属性必须被设置为Thin(细薄,无边框)。 4)属性页的Disable()复选框必须被选中。 说 明:一般不必记住这些不同的风格,这是因为在如图3.6.5对话框中有三个不同的选项用于创建属性页,它们唯一的差别是属性单的初始大小不同。 例3.27 创建属性表 (1)创建一个SDI(单文档)的应用程序,名为:创建属性表 (2)添加菜单资源 打开项目工作区的资源页面(ResourceView),点开Menuà双击IDR_MAINFRAMEà在“查看”下面添加菜单,名为:显示属性表(&X),其ID为ID_SHEET (3)添加属性页对话框和属性页对话框类并生成相应的消息相应函数: 1)InsertàResourceà打开Dialogà选中IDD_PROPPAGE_MEDIUM[English](一个中等大小的属性页,用于属性对话框) 如图3.6.5 所示àNew,右键对准该对话框的标识 符单击->在弹出的快捷菜单中单击属性 (Properties)->将其ID改为: IDD_PROGRESSDLG1,Language属性 改成:Chinese(P.RC),将Caption改成: 步进页。将控件工具栏上的进展条(ID改 第3章 对话框与控件 为IDC_PROGRESS)和按钮“步进”(ID改 为IDC_STEP)添加到该对话框上,如图 3.6.6所示。 图3.6.5 添加资源对话框 2)利用ClassWizard为IDD_PROGRESSDLG1 对话框建立对应的类:Cpage1,基类为:CpropertyPage,同时增加与IDC_PROGRESS 进展条对应的成员变量m_progress,类型为:CprogressCtrl 3)利用ClassWizard为Cpage1加入WM_INITDIALOG映射消息,并加代码: BOOL Cpage1::OnInitDialog() { CPropertyPage::OnInitDialog(); m_progress.SetRange(1,100); m_progress.SetStep(15); m_progress.SetPos(30); return TRUE; } 4)为Cpage1类添加单击按钮“步进” (ID_STEP)的消息映射BN_CLICKED,并 加代码: 图3.6.6 控件布局 void CPage1::OnStep() { m_progress.StepIt( ); } 说 明: 1、void SetRange( int nLower, int nUpper ); 该函数用来设置进度条的范围; 参 数:nLow和nUpper分别指定了最小值和最大值,缺省时进度条的范围是0-100。 2、int SetPos( int nPos ); 用来设置进度条的当前进度函数返回的是进度条的前一个进度。 3、int StepIt( ); 使进度增加一个步长,步长值是由SetStep函数设置的,缺省的步长值是10,函数返回进度条的前一个进度。 4、int SetStep( int nStep ); 用来设置步长值.函数返回原来的步长值。见第3、5、7节的进展条的基本操作 5)用创建Cpage1属性页同样的方法创建Cpage2属性页。InsertàResourceà Dialogà IDD_PROPPAGE_MEDIUM[English]->New,将其ID改为:IDD_PROGRESSDLG2,并将其Language改成:Chinese(P.RC),将Caption改成:颜色选择页,将控件工具栏上的Group Box 第3章 对话框与控件 (组框)拖到该对话框上,Caption(标题)写:颜色选择,将Radio Botton(单选按钮)拖到对话框的组框里,设置属性:Caption:Red(红), ID为:IDC_RED,属性选中Group。接着再将2个单选按钮拖到对话框的组框里,Caption:分别写:Green(绿) ,ID为IDC_GREEN、Blue(蓝) ID为IDC_BLUE,如图3.6.7所示。再为该对话框建类,类名为:Cpage2,基类为CpropertyPage。 6)在Cpage2类中为IDC_RED建立成员变量, 名为:m_red,类型为:int (4)创建一个属性表类来显示已创建的属性页 1)打开ClassWizard选项并单击AddClass按钮: 一个小菜单将出现在按钮下方,选择“New”菜 单,出现New Class对话框。在Name框中键入 类名为:CSheet,基类设置:CPropertySheet,再 单击“OK” 按钮,属性表类(CSheet)就建立了。 图3.6.7 添加的对话框模板 2)打开CSheet类的头文件(CSheet.h),在其前面加: #include “Page1.h” #include “Page2.h” 3)通过Class View标签在CSheet类中添 加成员变量: Cpage1 m_page1; Cpage2 m_page2; 4)展开CSheet类,在它的2个构造函数 中分别加入以下代码: (1) 选择步进页 AddPage(&m_page1);//增加第一页到属性单中 AddPage(&m_page2); //增加第一页到属性单中 (5)在视图类(CMyView)中加入前面第 (2)步设置好的菜单项(ID_SHEET)的 command消息映射函数OnSheet(),并加 下列代码: void CMyView::OnSheet() { CSheet sheet("PropertySheet",this ,0); (2) 选择颜色选择页 sheet.m_page2.m_red=1; 图3.6.8 运行结果 sheet.DoModal(); 第3章 对话框与控件 } (6)在CMyView.cpp前面加入: #include “Sheet.h” (7)编译运行,结果见图3.6.8所示, 单击菜单”查看”à显示属性表,便出现图(1)步进页,再单击”颜色选择页”,便出现图(2)”颜色选择页”,再单击”步进页”,又回到步进页,可循环显示。

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ]
1 人已下载

下载文档

相关文档