C#基础教程-c#实例教程,适合初学者

C#基础教程-c#实例教程,适合初学者。 第一章 C#语言基础 本章介绍C#语言的基础知识,但愿具备C语言的读者可以基本掌握C#语言,并以此为基础,可以进一步学习用C#语言编写window应用程序和Web应用程序。固然仅靠一章的内容就彻底掌握C#语言是不可能的,如需进一步学习C#语言,还须要认真阅读有关C#语言的专著。 1.1 C#语言特色 Microsoft.NET(如下简称.NET)框架是微软提出的新一代Web软件开发模型,C#语言是.NET框架中新一代的开发工具。C#语言是一种现代、面向对象的语言,它简化了C++语言在类、命名空间、方法重载和异常处理等方面的操做,它摒弃了C++的复杂性,更易使用,更少出错。它使用组件编程,和VB同样容易使用。C#语法和C++和JAVA语法很是类似,若是读者用过C++和JAVA,学习C#语言应是比较轻松的。 用C#语言编写的源程序,必须用C#语言编译器将C#源程序编译为中间语言(MicroSoft Intermediate Language,MSIL)代码,造成扩展名为exe或dll文件。中间语言代码不是CPU可执行的机器码,在程序运行时,必须由通用语言运行环境(Common Language Runtime,CLR)中的既时编译器(JUST IN Time,JIT)将中间语言代码翻译为CPU可执行的机器码,由CPU执行。CLR为C#语言中间语言代码运行提供了一种运行时环境,C#语言的CLR和JAVA语言的虚拟机相似。这种执行方法使运行速度变慢,但带来其它一些好处,主要有:  通用语言规范(Common Language Specification,CLS):.NET系统包括以下语言:C#、C++、VB、J#,他们都遵照通用语言规范。任何遵照通用语言规范的语言源程序,均可编译为相同的中间语言代码,由CLR负责执行。只要为其它操做系统编制相应的CLR,中间语言代码也可在其它系统中运行。  自动内存管理:CLR内建垃圾收集器,当变量实例的生命周期结束时,垃圾收集器负责收回不被使用的实例占用的内存空间。没必要象C和C++语言,用语句在堆中创建的实例,必须用语句释放实例占用的内存空间。也就是说,CLR具备自动内存管理功能。  交叉语言处理:因为任何遵照通用语言规范的语言源程序,均可编译为相同的中间语言代码,不一样语言设计的组件,能够互相通用,能够从其它语言定义的类派生出本语言的新类。因为中间语言代码由CLR负责执行,所以异常处理方法是一致的,这在调试一种语言调用另外一种语言的子程序时,显得特别方便。  增长安全:C#语言不支持指针,一切对内存的访问都必须经过对象的引用变量来实现,只容许访问内存中容许访问的部分,这就防止病毒程序使用非法指针访问私有成员。也避免指针的误操做产生的错误。CLR执行中间语言代码前,要对中间语言代码的安全性,完整性进行验证,防止病毒对中间语言代码的修改。  版本支持:系统中的组件或动态联接库可能要升级,因为这些组件或动态联接库都要在注册表中注册,由此可能带来一系列问题,例如,安装新程序时自动安装新组件替换旧组件,有可能使某些必须使用旧组件才能够运行的程序,使用新组件运行不了。在.NET中这些组件或动态联接库没必要在注册表中注册,每一个程序均可以使用自带的组件或动态联接库,只要把这些组件或动态联接库放到运行程序所在文件夹的子文件夹bin中,运行程序就自动使用在bin文件夹中的组件或动态联接库。因为不须要在注册表中注册,软件的安装也变得容易了,通常将运行程序及库文件拷贝到指定文件夹中就能够了。  彻底面向对象:不象C++语言,即支持面向过程程序设计,又支持面向对象程序设计,C#语言是彻底面向对象的,在C#中再也不存在全局函数、全局变量,全部的函数、变量和常量都必须定义在类中,避免了命名冲突。C#语言不支持多重继承。 1.2 编写控制台应用程序 使用SDK命令行工具编写控制台程序 第一个程序老是很是简单的,程序首先让用户经过键盘输入本身的名字,而后程序在屏幕上打印一条欢迎信息。程序的代码是这样的: using System;//导入命名空间。//为C#语言新增解释方法,解释到本行结束 class Welcome//类定义,类的概念见下一节 { /*解释开始,和C语言解释用法相同 解释结束*/ static void Main()//主程序,程序入口函数,必须在一个类中定义 { Console.WriteLine("请键入你的姓名:");//控制台输出字符串 Console.ReadLine();//从键盘读入数据,输入回车结束 Console.WriteLine("欢迎!"); } } 能够用任意一种文本编辑软件完成上述代码的编写,而后把文件存盘,假设文件名叫作welcome.cs,C#源文件是以cs做为文件的扩展名。和C语言相同,C#语言是区分大小写的。高级语言老是依赖于许多在程序外部预约义的变量和函数。在C或C++中这些定义通常放到头文件中,用#include语句来导入这个头文件。而在C#语言中使用using语句导入名字空间,using System语句意义是导入System名字空间,C#中的using语句的用途与C++中#include语句的用途基本相似,用于导入预约义的变量和函数,这样在本身的程序中就能够自由地使用这些变量和函数。若是没有导入名字空间的话咱们该怎么办呢?程序还能保持正确吗?答案是确定的,那样的话咱们就必须把代码改写成下面的样子: class Welcome { static void Main() { System.Console.WriteLine("请键入你的姓名:"); System.Console.ReadLine(); System.Console.WriteLine("欢迎!"); } } 也就是在每一个Console前加上一个前缀System.,这个小原点表示Console是做为System的成员而存在的。C#中抛弃了C和C++中繁杂且极易出错的操做符象::和->等,C#中的复合名字一概经过.来链接。System是.Net平台框架提供的最基本的名字空间之一,有关名字空间的详细使用方法将在之后详细介绍,这里只要学会怎样导入名字空间就足够了。 程序的第二行class Welcome声明了一个类,类的名字叫作Welcome。C#程序中每一个变量或函数都必须属于一个类,包括主函数Main(),不能象C或C++那样创建全局变量。C#语言程序老是从Main()方法开始执行,一个程序中不容许出现两个或两个以上的Main()方法。请牢记C#中Main()方法必须被包含在一个类中,Main第一个字母必须大写,必须是一个静态方法,也就是Main()方法必须使用static修饰。static void Main()是类Welcome中定义的主函数。静态方法意义见之后章节。 程序所完成的输入输出功能是经过Console类来完成的,Console是在名字空间System中已经定义好的一个类。Console类有两个最基本的方法WriteLine和ReadLine。ReadLine表示从输入设备输入数据,WriteLine则用于在输出设备上输出数据。 若是在电脑上安装了Visual Studio.Net,则能够在集成开发环境中直接选择快捷键或菜单命令编译并执行源文件。若是您不具有这个条件,那么至少须要安装Microsoft.Net Framework SDK,这样才可以运行C#语言程序。Microsoft.Net Framework SDK中内置了C#的编译器csc.exe,下面让咱们使用这个微软提供的命令行编译器对程序welcome.cs进行编译。假设已经将welcome.cs文件保存在d:\Charp目录下,启动命令行提示符,在屏幕上输入一行命令:d:回车,cd Charp回车,键入命令: C:\WINNT\Microsoft.NET\Framework\v1.0.3705\csc welcome.cs 若是一切正常welcome.cs文件将被编译,编译后生成可执行文件Welcome.exe。能够在命令提示符窗口运行可执行文件Welcome.exe,屏幕上出现一行字符提示您输入姓名:请键入你的姓名:输入任意字符并按下回车键,屏幕将打印出欢迎信息:欢迎! 注意,和咱们使用过的绝大多数编译器不一样,在C#中编译器只执行编译这个过程,而在C和C++中要通过编译和连接两个阶段。换而言之C#源文件并不被编译为目标文件.obj,而是直接生成可执行文件.exe或动态连接库.dll,C#编译器中不须要包含连接器。 使用Visual Studio.Net创建控制台程序 (1) 运行Visual Studio.Net程序,出现如图1.2.2A界面。 (2) 单击新建项目按钮,出现如图1.2.2B对话框。在项目类型(P)编辑框中选择Visual C#项目,在模板(T)编辑框中选择控制台应用程序,在名称(N)编辑框中键入e1,在位置(L)编辑框中键入D:\csarp,必须预先建立文件夹D:\csarp。也能够单击浏览按钮,在打开文件对话框中选择文件夹。单击肯定按钮,建立项目。出现如图1.2.2C界面。编写一个应用程序,可能包含多个文件,才能生成可执行文件,全部这些文件的集合叫作一个项目。 (3) 修改class1.cs文件以下,有阴影部分是新增长的语句,其他是集成环境自动生成的。 using System; namespace e1 { /// <summary> /// Class1 的摘要说明。 /// </summary> class Class1 { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main(string[] args) { // // TODO: 在此处添加代码以启动应用程序 // Console.WriteLine("请键入你的姓名:"); Console.ReadLine(); Console.WriteLine("欢迎!"); } } } (4) 按CTRL+F5键,运行程序,如右图,和1.2.1节运行效果相同。屏幕上出现一行字符,提示您输入姓名:请键入你的姓名:输入任意字符并按下回车键,屏幕将打印出欢迎信息:欢迎!输入回车退出程序。 图1.2.2A 图1.2.2B 1.3 类的基本概念 C#语言是一种现代、面向对象的语言。面向对象程序设计方法提出了一个全新的概念:类,它的主要思想是将数据(数据成员)及处理这些数据的相应方法(函数成员)封装到类中,类的实例则称为对象。这就是咱们常说的封装性。 类的基本概念 类能够认为是对结构的扩充,它和C中的结构最大的不一样是:类中不但能够包括数据,还包括处理这些数据的函数。类是对数据和处理数据的方法(函数)的封装。类是对某一类具备相同特性和行为的事物的描述。例如,定义一个描述我的状况的类Person以下: using System; class Person//类的定义,class是保留字,表示定义一个类,Person是类名 { private string name="张三";//类的数据成员声明 private int age=12;//private表示私有数据成员 public void Display()//类的方法(函数)声明,显示姓名和年龄 { Console.WriteLine("姓名:{0},年龄:{1}",name,age); } public void SetName(string PersonName)//修改姓名的方法(函数) { name=PersonName; } public void SetAge(int PersonAge) { age=PersonAge; } } Console.WriteLine("姓名:{0},年龄:{1}",name,age)的意义是将第二个参数变量name变为字符串填到{0}位置,将第三个参数变量age变为字符串填到{1}位置,将第一个参数表示的字符串在显示器上输出。 你们注意,这里咱们实际定义了一个新的数据类型,为用户本身定义的数据类型,是对我的的特性和行为的描述,他的类型名为Person,和int,char等同样为一种数据类型。用定义新数据类型Person类的方法把数据和处理数据的函数封装起来。类的声明格式以下: 属性 类修饰符 class 类名{类体} 其中,关键字class、类名和类体是必须的,其它项是可选项。类修饰符包括new、public、protected、internal、private、abstract和sealed,这些类修饰符之后介绍。类体用于定义类的成员。 1.3.2 类成员的存取控制 通常但愿类中一些数据不被随意修改,只能按指定方法修改,既隐蔽一些数据。一样一些函数也不但愿被其它类程序调用,只能在类内部使用。如何解决这个问题呢?可用访问权限控制字,经常使用的访问权限控制字以下:private(私有),public(公有)。在数据成员或函数成员前增长访问权限控制字,能够指定该数据成员或函数成员的访问权限。 私有数据成员只能被类内部的函数使用和修改,私有函数成员只能被类内部的其它函数调用。类的公有函数成员能够被类的外部程序调用,类的公有数据成员能够被类的外部程序直接使用修改。公有函数实际是一个类和外部通信的接口,外部函数经过调用公有函数,按照预先设定好的方法修改类的私有成员。对于上述例子,name和age是私有数据成员,只能经过公有函数SetName()和SetAge()修改,既它们只能按指定方法修改。 这里再一次解释一下封装,它有两个意义,第一是把数据和处理数据的方法同时定义在类中。第二是用访问权限控制字使数据隐蔽。 1.3.3 类的对象 Person类仅是一个用户新定义的数据类型,由它能够生成Person类的实例,C#语言叫对象。用以下方法声明类的对象:Person OnePerson=new Person();此语句的意义是创建Person类对象,返回对象地址赋值给Person类变量OnePerson。也能够分两步建立Person类的对象:Person OnePerson;OnePerson=new Person();OnePerson虽然存储的是Person类对象地址,但不是C中的指针,不能象指针那样能够进行加减运算,也不能转换为其它类型地址,它是引用型变量,只能引用(表明)Person对象,具体意义参见之后章节。和C、C++不一样,C#只能用此种方法生成类对象。 在程序中,能够用OnePerson.方法名或OnePerson.数据成员名访问对象的成员。例如:OnePerson.Display(),公用数据成员也能够这样访问。注意,C#语言中不包括C++语言中的->符号。 1.3.4 类的构造函数和析构函数 在创建类的对象时,需作一些初始化工做,例如对数据成员初始化。这些能够用构造函数来完成。每当用new生成类的对象时,自动调用类的构造函数。 所以,能够把初始化的工做放到构造函数中完成。构造函数和类名相同,没有返回值。例如能够定义Person类的构造函数以下: public Person(string Name,int Age)//类的构造函数,函数名和类同名,无返回值。 { name=Name; age=Age; } 当用Person OnePerson=new Person(“张五”,20)语句生成Person类对象时,将自动调用以上构造函数。请注意如何把参数传递给构造函数。 变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。类的对象被撤销时,将自动调用析构函数。一些善后工做可放在析构函数中完成。析构函数的名字为~类名,无返回类型,也无参数。Person类的析构函数为~ Person()。C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时自动调用的。 1.3.5 类的构造函数的重载 在C#语言中,同一个类中的函数,若是函数名相同,而参数类型或个数不一样,认为是不一样的函数,这叫函数重载。仅返回值不一样,不能看做不一样的函数。这样,能够在类定义中,定义多个构造函数,名字相同,参数类型或个数不一样。根据生成类的对象方法不一样,调用不一样的构造函数。例如能够定义Person类没有参数的构造函数以下: public Person()//类的构造函数,函数名和类同名,无返回值。 { name="张三"; age=12; } 用语句Person OnePerson=new Person("李四",30)生成对象时,将调用有参数的构造函数,而用语句Person OnePerson=new Person()生成对象时,调用无参数的构造函数。因为析构函数无参数,所以,析构函数不能重载。 1.3.6 使用Person类的完整的例子 下边用一个完整的例子说明Person类的使用:(VisualStudio.Net编译经过) using System; namespace e1//定义如下代码所属命名空间,意义见之后章节 { class Person { private String name="张三";//类的数据成员声明 private int age=12; public void Display()//类的方法(函数)声明,显示姓名和年龄 { Console.WriteLine("姓名:{0},年龄:{1}",name,age); } public void SetName(string PersonName)//指定修改姓名的方法(函数) { name=PersonName; } public void SetAge(int PersonAge)//指定修改年龄的方法(函数) { age=PersonAge; } public Person(string Name,int Age)//构造函数,函数名和类同名,无返回值 { name=Name; age=Age; } public Person()//类的构造函数重载 { name="田七"; age=12; } } class Class1 { static void Main(string[] args) { Person OnePerson=new Person("李四",30);//生成类的对象 OnePerson.Display(); //下句错误,在其它类(Class1类)中,不能直接修改Person类中的私有成员。 //OnePerson.name="王五"; //只能经过Person类中公有方法SetName修改Person类中的私有成员name。 OnePerson.SetName("王五"); OnePerson.SetAge(40); OnePerson.Display(); OnePerson=new Person(); OnePerson.Display(); } } } 键入CTRL+F5运行后,显示的效果是: 姓名: 李四,年龄:30 姓名: 王五,年龄:40 姓名: 田七,年龄:12 1.4 C#的数据类型 从大的方面来分,C#语言的数据类型能够分为三种:值类型,引用类型,指针类型,指针类型仅用于非安全代码中。本节重点讨论值类型和引用类型。 1.4.1 值类型和引用类型区别 在C#语言中,值类型变量存储的是数据类型所表明的实际数据,值类型变量的值(或实例)存储在栈(Stack)中,赋值语句是传递变量的值。引用类型(例如类就是引用类型)的实例,也叫对象,不存在栈中,而存储在可管理堆(Managed Heap)中,堆其实是计算机系统中的空闲内存。引用类型变量的值存储在栈(Stack)中,但存储的不是引用类型对象,而是存储引用类型对象的引用,即地址,和指针所表明的地址不一样,引用所表明的地址不能被修改,也不能转换为其它类型地址,它是引用型变量,只能引用指定类对象,引用类型变量赋值语句是传递对象的地址。见下例: using System; class MyClass//类为引用类型 { public int a=0; } class Test { static void Main() { f1(); } static public void f1() { int v1=1;//值类型变量v1,其值1存储在栈(Stack)中 int v2=v1;//将v1的值(为1)传递给v2,v2=1,v1值不变。 v2=2;//v2=2,v1值不变。 MyClass r1=new MyClass();//引用变量r1存储MyClass类对象的地址 MyClass r2=r1;//r1和r2都表明是同一个MyClass类对象 r2.a=2;//和语句r1.a=2等价 } } 存储在栈中的变量,当其生命周期结束,自动被撤销,例如,v1存储在栈中,v1和函数f1同生命周期,退出函数f1,v1不存在了。但在堆中的对象不能自动被撤销。所以C和C++语言,在堆中创建的对象,不使用时必须用语句释放对象占用的存储空间。.NET系统CLR内建垃圾收集器,当对象的引用变量被撤销,表示对象的生命周期结束,垃圾收集器负责收回不被使用的对象占用的存储空间。例如,上例中引用变量r1及r2是MyClass类对象的引用,存储在栈中,退出函数f1,r1和r2都不存在了,在堆中的MyClass类对象也就被垃圾收集器撤销。也就是说,CLR具备自动内存管理功能。 1.4.2 值类型变量分类 C#语言值类型能够分为如下几种:  简单类型(Simple types) 简单类型中包括:数值类型和布尔类型(bool)。数值类型又细分为:整数类型、字符类型(char)、浮点数类型和十进制类型(decimal)。  结构类型(Struct types)  枚举类型(Enumeration types) C#语言值类型变量不管如何定义,老是值类型变量,不会变为引用类型变量。 1.4.3 结构类型 结构类型和类同样,能够声明构造函数、数据成员、方法、属性等。结构和类的最根本的区别是结构是值类型,类是引用类型。和类不一样,结构不能从另一个结构或者类派生,自己也不能被继承,所以不能定义抽象结构,结构成员也不能被访问权限控制字protected修饰,也不能用virtual和abstract修饰结构方法。在结构中不能定义析构函数。虽然结构不能从类和结构派生,但是结构可以继承接口,结构继承接口的方法和类继承接口的方法基本一致。下面例子定义一个点结构point: using System; struct point//结构定义 { public int x,y;//结构中也能够声明构造函数和方法,变量不能赋初值 } class Test { static void Main() { point P1; P1.x=166; P1.y=111; point P2; P2=P1;//值传递,使P2.x=166,P2.y=111 point P3=new point();//用new生成结构变量P3,P3仍为值类型变量 }//用new生成结构变量P3仅表示调用默认构造函数,使x=y==0。 } 1.4.4 简单类型 简单类型也是结构类型,所以有构造函数、数据成员、方法、属性等,所以下列语句int i=int.MaxValue;string s=i.ToString()是正确的。即便一个常量,C#也会生成结构类型的实例,所以也能够使用结构类型的方法,例如:string s=13.ToString()是正确的。简单类型包括:整数类型、字符类型、布尔类型、浮点数类型、十进制类型。见下表: 保留字 System命名空间中的名字 字节数 取值范围 sbyte System.Sbyte 1 -128~127 byte System.Byte 1 0~255 short System.Int16 2 -32768~32767 ushort System.UInt16 2 0~65535 int System.Int32 4 -2147483648~2147483647 uint System.UInt32 4 0~4292967295 long System.Int64 8 -9223372036854775808~9223372036854775808 ulong System.UInt64 8 0~18446744073709551615 char System.Char 2 0~65535 float System.Single 4 3.4E-38~3.4E+38 double System.Double 8 1.7E-308~1.7E+308 bool System.Boolean (true,false) decimal System.Decimal 16 正负1.0到7.9之间 C#简单类型使用方法和C、C++中相应的数据类型基本一致。须要注意的是:  和C语言不一样,不管在何种系统中,C#每种数据类型所占字节数是必定的。  字符类型采用Unicode字符集,一个Unicode标准字符长度为16位。  整数类型不能隐式被转换为字符类型(char),例如char c1=10是错误的,必须写成:char c1=(char)10,char c='A',char c='\x0032';char c='\u0032'。  布尔类型有两个值:false,true。不能认为整数0是false,其它值是true。bool x=1是错误的,不存在这种写法,只能写成x=true 或x=false。  十进制类型(decimal)也是浮点数类型,只是精度比较高,通常用于财政金融计算。 1.4.5 枚举类型 C#枚举类型使用方法和C、C++中的枚举类型基本一致。见下例: using System; class Class1 { enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri}; //使用Visual Studio.Net,enum语句添加在[STAThread]前边 static void Main(string[] args) { Days day=Days.Tue; int x=(int)Days.Tue;//x=2 Console.WriteLine("day={0},x={1}",day,x);//显示结果为:day=Tue,x=4 } } 在此枚举类型Days中,每一个元素的默认类型为int,其中Sun=0,Mon=1,Tue=2,依此类推。也能够直接给枚举元素赋值。例如: enum Days{Sat=1,Sun,Mon,Tue,Wed,Thu,Fri,Sat}; 在此枚举中,Sun=1,Mon=2,Tue=3,Wed=4,等等。和C、C++中不一样,C#枚举元素类型能够是byte、sbyte、short、ushort、int、uint、long和ulong类型,但不能是char类型。见下例: enum Days:byte{Sun,Mon,Tue,Wed,Thu,Fri,Sat};//元素为字节类型 1.4.6 值类型的初值和默认构造函数 全部变量都要求必须有初值,如没有赋值,采用默认值。对于简单类型,sbyte、byte、short、ushort、int、uint、long和ulong默认值为0,char类型默认值是(char)0,float为0.0f,double为0.0d,decimal为0.0m,bool为false,枚举类型为0,在结构类型和类中,数据成员的数值类型变量设置为默认值,引用类型变量设置为null。 能够显示的赋值,例如int i=0。而对于复杂结构类型,其中的每一个数据成员都按此种方法赋值,显得过于麻烦。因为数值类型都是结构类型,可用new语句调用其构造函数初始化数值类型变量,例如:int j=new int()。请注意,用new语句并非把int变量变为引用变量,j还是值类型变量,这里new仅仅是调用其构造函数。全部的数值类型都有默认的无参数的构造函数,其功能就是为该数值类型赋初值为默认值。对于自定义结构类型,因为已有默认的无参数的构造函数,不能再定义无参数的构造函数,但能够定义有参数的构造函数。 1.4.7 引用类型分类 C#语言中引用类型能够分为如下几种:  类:C#语言中预约义了一些类:对象类(object类)、数组类、字符串类等。固然,程序员能够定义其它类。  接口。  表明。 C#语言引用类型变量不管如何定义,老是引用类型变量,不会变为值类型变量。C#语言引用类型对象通常用运算符new创建,用引用类型变量引用该对象。本节仅介绍对象类型(object类型)、字符串类型、数组。其它类型在其它节中介绍。 1.4.8 对象类(object类) C#中的全部类型(包括数值类型)都直接或间接地以object类为基类。对象类(object类)是全部其它类的基类。任何一个类定义,若是不指定基类,默认object为基类。继承和基类的概念见之后章节。C#语言规定,基类的引用变量能够引用派生类的对象(注意,派生类的引用变量不能够引用基类的对象),所以,对一个object的变量能够赋予任何类型的值: int x =25; object obj1; obj1=x; object obj2= 'A'; object关键字是在命名空间System中定义的,是类System.Object的别名。 1.4.9 数组类 在进行批量处理数据的时候,要用到数组。数组是一组类型相同的有序数据。数组按照数组名、数据元素的类型和维数来进行描述。C#语言中数组是类System.Array类对象,好比声明一个整型数数组:int[] arr=new int[5];实际上生成了一个数组类对象,arr是这个对象的引用(地址)。 在C#中数组能够是一维的也能够是多维的,一样也支持数组的数组,即数组的元素仍是数组。一维数组最为广泛,用的也最多。咱们先看一个一维数组的例子: using System; class Test { static void Main() { int[] arr=new int[3];//用new运算符创建一个3个元素的一维数组 for(int i=0;i<arr.Length;i++)//arr.Length是数组类变量,表示数组元素个数 arr[i]=i*i;//数组元素赋初值,arr[i]表示第i个元素的值 for (int i=0;i<arr.Length;i++)//数组第一个元素的下标为0 Console.WriteLine("arr[{0}]={1}",i,arr[i]); } } 这个程序建立了一个int类型3个元素的一维数组,初始化后逐项输出。其中arr.Length表示数组元素的个数。注意数组定义不能写为C语言格式:int arr[]。程序的输出为: arr[0] = 0 arr[1] = 1 arr[2] = 4 上面的例子中使用的是一维数组,下面介绍多维数组: string[] a1;//一维string数组类引用变量a1 string[,] a2;//二维string数组类引用变量a2 a2=new string[2,3]; a2[1,2]="abc"; string[,,] a3;//三维string数组类引用变量a3 string[][] j2;//数组的数组,即数组的元素仍是数组 string[][][][] j3; 在数组声明的时候,能够对数组元素进行赋值。看下面的例子: int[] a1=new int[]{1,2,3};//一维数组,有3个元素。 int[] a2=new int[3]{1,2,3};//此格式也正确 int[] a3={1,2,3};//至关于int[] a3=new int[]{1,2,3}; int[,] a4=new int[,]{{1,2,3},{4,5,6}};//二维数组,a4[1,1]=5 int[][] j2=new int[3][];//定义数组j2,有三个元素,每一个元素都是一个数组 j2[0]=new int[]{1,2,3};//定义第一个元素,是一个数组 j2[1]=new int[]{1, 2, 3, 4, 5, 6};//每一个元素的数组能够不等长 j2[2]=new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; 1.4.10 字符串类(string类) C#还定义了一个基本的类string,专门用于对字符串的操做。这个类也是在名字空间System中定义的,是类System.String的别名。字符串应用很是普遍,在string类的定义中封装了许多方法,下面的一些语句展现了string类的一些典型用法:  字符串定义 string s;//定义一个字符串引用类型变量s s="Zhang";//字符串引用类型变量s指向字符串"Zhang" string FirstName="Ming"; string LastName="Zhang"; string Name=FirstName+" "+LastName;//运算符+已被重载 string SameName=Name; char[] s2={'计','算','机','科','学'}; string s3=new String(s2);  字符串搜索 string s="ABC科学"; int i=s.IndexOf("科"); 搜索"科"在字符串中的位置,因第一个字符索引为0,因此"A"索引为0,"科"索引为3,所以这里i=3,如没有此字符串i=-1。注意C#中,ASCII和汉字都用2字节表示。  字符串比较函数 string s1="abc"; string s2="abc"; int n=string.Compare(s1,s2);//n=0 n=0表示两个字符串相同,n小于零,s1<s2,n大于零,s1>s2。此方法区分大小写。也可用以下办法比较字符串: string s1="abc"; string s="abc"; string s2="不相同"; if(s==s1)//还可用!=。虽然String是引用类型,但这里比较两个字符串的值 s2="相同";  判断是否为空字符串 string s=""; string s1="不空"; if(s.Length==0) s1="空";  获得子字符串或字符 string s="取子字符串"; string sb=s.Substring(2,2);//从索引为2开始取2个字符,Sb="字符",s内容不变 char sb1=s[0];//sb1='取' Console.WriteLine(sb1);//显示:取  字符串删除函数 string s="取子字符串"; string sb=s.Remove(0,2);//从索引为0开始删除2个字符,Sb="字符串",s内容不变  插入字符串 string s="计算机科学"; string s1=s.Insert(3,"软件");//s1="计算机软件科学",s内容不变  字符串替换函数 string s="计算机科学"; string s1=s.Replace("计算机","软件");//s1="软件科学",s内容不变  把String转换为字符数组 string S="计算机科学"; char[] s2=S.ToCharArray(0,S.Length);//属性Length为字符类对象的长度  其它数据类型转换为字符串 int i=9; string s8=i.ToString();//s8="9" float n=1.9f; string s9=n.ToString();//s8="1.9" 其它数据类型均可用此方法转换为字符类对象  大小写转换 string s="AaBbCc"; string s1=s.ToLower();//把字符转换为小写,s内容不变 string s2=s.ToUpper();//把字符转换为大写,s内容不变  删除全部的空格 string s="A bc "; s.Trim();//删除全部的空格 string类其它方法的使用请用帮助系统查看,方法是打开Visual Studio.Net的代码编辑器,键入string,将光标移到键入的字符串string上,而后按F1键。 1.4.11 类型转换 在编写C#语言程序中,常常会碰到类型转换问题。例如整型数和浮点数相加,C#会进行隐式转换。详细记住那些类型数据能够转换为其它类型数据,是不可能的,也是没必要要的。程序员应记住类型转换的一些基本原则,编译器在转换发生问题时,会给出提示。C#语言中类型转换分为:隐式转换、显示转换、加框(boxing)和消框(unboxing)等三种。 一. 隐式转换 隐式转换就是系统默认的、不须要加以声明就能够进行的转换。例如从int类型转换到long类型就是一种隐式转换。在隐式转换过程当中,转换通常不会失败,转换过程当中也不会致使信息丢失。例如: int i=10; long l=i; 二. 显示转换 显式类型转换,又叫强制类型转换。与隐式转换正好相反,显式转换须要明确地指定转换类型,显示转换可能致使信息丢失。下面的例子把长整形变量显式转换为整型: long l=5000; int i=(int)l;//若是超过int取值范围,将产生异常 三. 加框(boxing)和消框(unboxing) 加框(boxing)和消框(unboxing)是C#语言类型系统提出的核心概念,加框是值类型转换为object(对象)类型,消框是object(对象)类型转换为值类型。有了加框和消框的概念,对任何类型的变量来讲最终咱们均可以看做是object类型。 1 加框操做 把一个值类型变量加框也就是建立一个object对象,并将这个值类型变量的值复制给这个object对象。例如: int i=10; object obj=i;//隐式加框操做,obj为建立的object对象的引用。 咱们也能够用显式的方法来进行加框操做,例如: int i =10; object obj=object(i);//显式加框操做 值类型的值加框后,值类型变量的值不变,仅将这个值类型变量的值复制给这个object对象。咱们看一下下面的程序: using System class Test { public static void Main() { int n=200; object o=n; o=201;//不能改变n Console.WriteLine("{0},{1}",n,o); } } 输出结果为:200,201。这就证实了值类型变量n和object类对象o都独立存在着。 2. 消框操做 和加框操做正好相反,消框操做是指将一个对象类型显式地转换成一个值类型。消框的过程分为两步:首先检查这个object对象,看它是否为给定的值类型的加框值,如是,把这个对象的值拷贝给值类型的变量。咱们举个例子来看看一个对象消框的过程: int i=10; object obj=i; int j=(int)obj;//消框操做 能够看出消框过程正好是加框过程的逆过程,必须注意加框操做和消框操做必须遵循类型兼容的原则。 3. 加框和消框的使用 定义以下函数: void Display(Object o)//注意,o为Object类型 { int x=(int)o;//消框 System.Console.WriteLine("{0},{1}",x,o); } 调用此函数:int y=20;Display(y);在此利用了加框概念,虚参被实参替换:Object o=y,也就是说,函数的参数是Object类型,能够将任意类型实参传递给函数。 1.5 运算符 C#语言和C语言的运算符用法基本一致。如下重点讲解两者之间不一致部分。 1.5.1 运算符分类 与C语言同样,若是按照运算符所做用的操做数个数来分,C#语言的运算符能够分为如下几种类型:  一元运算符:一元运算符做用于一个操做数,例如:-X、++X、X--等。  二元运算符:二元运算符对两个操做数进行运算,例如:x+y。  三元运算符:三元运算符只有一个:x? y:z。 C#语言运算符的详细分类及操做符从高到低的优先级顺序见下表。 类别 操做符 初级操做符 (x) x.y f(x) a[x] x++ x-- new type of sizeof checked unchecked 一元操做符 + - ! ~ ++x –x (T)x 乘除操做符 * / % 加减操做符 + - 移位操做符 << >> 关系操做符 < > <= >= is as 等式操做符 == != 逻辑与操做符 & 逻辑异或操做符 ^ 逻辑或操做符 | 条件与操做符 && 条件或操做符 || 条件操做符 ?: 赋值操做符 = *= /= %= += -= <<= >>= &= ^= |= 1.5.2 测试运算符is is操做符用于动态地检查表达式是否为指定类型。使用格式为:e is T,其中e是一个表达式,T是一个类型,该式判断e是否为T类型,返回值是一个布尔值。例子: using System; class Test { public static void Main() { Console.WriteLine(1 is int); Console.WriteLine(1 is float); Console.WriteLine(1.0f is float); Console.WriteLine(1.0d is double); } } 输出为: True False True True 1.5.3 typeof运算符 typeof操做符用于得到指定类型在system名字空间中定义的类型名字,例如: using System; class Test { static void Main() { Console.WriteLine(typeof(int)); Console.WriteLine(typeof(System.Int32)); Console.WriteLine(typeof(string)); Console.WriteLine(typeof(double[])); } } 产生以下输出,由输出可知int和System.int32是同一类型。 System.Int32 System.Int32 System.String System.Double[] 1.5.4 溢出检查操做符checked和unchecked 在进行整型算术运算(如+、-、*、/等)或从一种整型显式转换到另外一种整型时,有可能出现运算结果超出这个结果所属类型值域的状况,这种状况称之为溢出。整型算术运算表达式能够用checked或unchecked溢出检查操做符,决定在编译和运行时是否对表达式溢出进行检查。若是表达式不使用溢出检查操做符或使用了checked操做符,常量表达式溢出,在编译时将产生错误,表达式中包含变量,程序运行时执行该表达式产生溢出,将产生异常提示信息。而使用了unchecked操做符的表达式语句,即便表达式产生溢出,编译和运行时都不会产生错误提示。但这每每会出现一些不可预期的结果,因此使用unchecked操做符要当心。下面的例子说明了checked和unchecked操做符的用法: using System; class Class1 { static void Main(string[] args) { const int x=int.MaxValue; unchecked//不检查溢出 { int z=x*2;//编译时不产生编译错误,z=-2 Console.WriteLine("z={0}",z);//显示-2 } checked//检查溢出 { int z1=(x*2);//编译时会产生编译错误 Console.WriteLine("z={0}",z1); } } } 1.5.5 new运算符 new操做符能够建立值类型变量、引用类型对象,同时自动调用构造函数。例如: int x=new int();//用new建立整型变量x,调用默认构造函数 Person C1=new Person ();//用new创建的Person类对象。Person 变量C1对象的引用 int[] arr=new int[2];//数组也是类,建立数组类对象,arr是数组对象的引用 需注意的是,int x=new int()语句将自动调用int结构不带参数的构造函数,给x赋初值0,x还是值类型变量,不会变为引用类型变量。 1.5.6 运算符的优先级 当一个表达式包含多种操做符时,操做符的优先级控制着操做符求值的顺序。例如,表达式x+y*z按照x+(y*z)顺序求值,由于*操做符比+操做符有更高的优先级。这和数学运算中的先乘除后加减是一致的。1.5.1节中的表总结了全部操做符从高到低的优先级顺序。 当两个有相同优先级的操做符对操做数进行运算时,例如x+y-z,操做符按照出现的顺序由左至右执行,x+y-z按(x+y)-z进行求值。赋值操做符按照右接合的原则,即操做按照从右向左的顺序执行。如x=y=z按照x=(y=z)进行求值。建议在写表达式的时候,若是没法肯定操做符的实际顺序,则尽可能采用括号来保证运算的顺序,这样也使得程序一目了然,并且本身在编程时可以思路清晰。 1.6 程序控制语句 C#语言控制语句和C基本相同,使用方法基本一致。C#语言控制语句包括:if语句、swith语句、while语句、do…while语句、for语句、foreach语句、break语句、continue语句、goto语句、return语句、异常处理语句等,其中foreach语句和异常语句是C#语言新增长控制语句。本节首先介绍一下这些语句和C语言的不一样点,而后介绍C#语言新增的控制语句。 1.6.1 和C语言的不一样点  与C不一样,if语句、while语句、do…while语句、for语句中的判断语句,必定要用布尔表达式,不能认为0为false,其它数为true。  switch语句再也不支持遍历,C和C++语言容许switch语句中case标签后不出现break语句,但C#不容许这样,它要求每一个case标签项后使用break语句或goto跳转语句,即不容许从一个case自动遍历到其它case,不然编译时将报错。switch语句的控制类型,即其中控制表达式的数据类型能够是sbyte、byte、short、ushort、uint、long、ulong、char、string或枚举类型。每一个case标签中的常量表达式必须属于或能隐式转换成控制类型。若是有两个或两个以上case标签中的常量表达式值相同,编译时将会报错。执行switch语句,首先计算switch表达式,而后与case后的常量表达式的值进行比较,执行第一个与之匹配的case分支下的语句。若是没有case常量表达式的值与之匹配,则执行dafault分支下的语句,若是没有dafault语句,则退出switch语句。switch语句中能够没有dafault语句,但最多只能有一个dafault语句。见下例: using System; class class1 { static void Main() { System.Console.WriteLine("请输入要计算天数的月份"); string s=System.Console.ReadLine(); string s1=""; switch(s) { case "1": case "3": case "5": case "7": case "8": case "10": case "12"://共用一条语句 s1="31";break; case "2": s1="28";break; case "4": case "6": case "9": goto case "11";//goto语句仅为说明问题,无此必要 case "11": s1="30";break; default: s1="输入错误";break; } System.Console.WriteLine(s1); } } 1.6.2 foreach语句 foreach语句是C#语言新引入的语句,C和C++中没有这个语句,它借用Visual Basic中的foreach语句。语句的格式为: foreach(类型 变量名 in 表达式) 循环语句 其中表达式必须是一个数组或其它集合类型,每一次循环从数组或其它集合中逐一取出数据,赋值给指定类型的变量,该变量能够在循环语句中使用、处理,但不容许修改变量,该变量的指定类型必须和表达式所表明的数组或其它集合中的数据类型一致。例子: using System; class Test() { public static void Main() { int[] list={10,20,30,40};//数组 foreach(int m in list) Console.WriteLine("{0}",m); } } 对于一维数组,foreach语句循环顺序是从下标为0的元素开始一直到数组的最后一个元素。对于多维数组,元素下标的递增是从最右边那一维开始的。一样break和continue能够出如今foreach语句中,功能不变。 1.6.3 异常语句 在编写程序时,不只要关心程序的正常操做,还应该考虑到程序运行时可能发生的各种不可预期的事件,好比用户输入错误、内存不够、磁盘出错、网络资源不可用、数据库没法使用等,全部这些错误被称做异常,不能由于这些异常使程序运行产生问题。各类程序设计语言常常采用异常处理语句来解决这类异常问题。 C#提供了一种处理系统级错误和应用程序级错误的结构化的、统一的、类型安全的方法。C#异常语句包含try子句、catch子句和finally子句。try子句中包含可能产生异常的语句,该子句自动捕捉执行这些语句过程当中发生的异常。catch子句中包含了对不一样异常的处理代码,能够包含多个catch子句,每一个catch子句中包含了一个异常类型,这个异常类型必须是System.Exception类或它的派生类引用变量,该语句只扑捉该类型的异常。能够有一个通用异常类型的catch子句,该catch子句通常在事先不能肯定会发生什么样的异常的状况下使用,也就是能够扑捉任意类型的异常。一个异常语句中只能有一个通用异常类型的catch子句,并且若是有的话,该catch子句必须排在其它catch子句的后面。不管是否产生异常,子句finally必定被执行,在finally子句中能够增长一些必须执行的语句。 异常语句捕捉和处理异常的机理是:当try子句中的代码产生异常时,按照catch子句的顺序查找异常类型。若是找到,执行该catch子句中的异常处理语句。若是没有找到,执行通用异常类型的catch子句中的异常处理语句。因为异常的处理是按照catch子句出现的顺序逐一检查catch子句,所以catch子句出现的顺序是很重要的。不管是否产生异常,必定执行finally子句中的语句。异常语句中没必要必定包含全部三个子句,所以异常语句能够有如下三种可能的形式:  try –catch语句,能够有多个catch语句  try -finally语句  try -catch-finally语句,能够有多个catch语句 请看下边的例子: 1. try–catch-finally语句 using System using System.IO//使用文件必须引用的名字空间 public class Example { public static void Main() { StreamReader sr=null;//必须赋初值null,不然编译不能经过 try { sr=File.OpenText("d:\\csarp\\test.txt");//可能产生异常 string s; while(sr.Peek()!=-1) { s=sr.ReadLine();//可能产生异常 Console.WriteLine(s); } } catch(DirectoryNotFoundException e)//无指定目录异常 { Console.WriteLine(e.Message); } catch(FileNotFoundException e)//无指定文件异常 { Console.WriteLine("文件"+e.FileName+"未被发现"); } catch(Exception e)//其它全部异常 { Console.WriteLine("处理失败:{0}",e.Message); } finally { if(sr!=null) sr.Close(); } } } 2. try -finally语句 上例中,其实能够不用catch语句,在finally子句中把文件关闭,提示用户是否正确打开了文件,请读者本身完成。 3. try -catch语句 请读者把上例修改成使用try-catch结构,注意在每一个catch语句中都要关闭文件。 1.7 类的继承 在1.3节,定义了一个描述我的状况的类Person,若是咱们须要定义一个雇员类,固然能够从头开始定义雇员类Employee。但这样不能利用Person类中已定义的函数和数据。比较好的方法是,以Person类为基类,派生出一个雇员类Employee,雇员类Employee继承了Person类的数据成员和函数成员,既Person类的数据成员和函数成员成为Employee类的成员。这个Employee类叫以Person类为基类的派生类,这是C#给咱们提出的方法。C#用继承的方法,实现代码的重用。 1.7.1 派生类的声明格式 派生类的声明格式以下: 属性 类修饰符 class 派生类名:基类名 {类体} 雇员类Employee定义以下: class Employee:Person//Person类是基类 { private string department;//部门,新增数据成员 private decimal salary;//薪金,新增数据成员 public Employee(string Name,int Age,string D,decimal S):base(Name,Age) {//注意base的第一种用法,根据参数调用指定基类构造函数,注意参数的传递 department=D; salary=S; } public new void Display()//覆盖基类Display()方法,注意new,不可用override { base.Display();//访问基类被覆盖的方法,base的第二种用法 Console.WriteLine("部门:{0} 薪金:{1}",department,salary); } } 修改主函数以下: class Class1 { static void Main(string[] args) { Employee OneEmployee=new Employee("李四",30,"计算机系",2000); OneEmployee.Display(); } } Employee类继承了基类Person的方法SetName()、SetAge(),数据成员name和age,即认为基类Person的这些成员也是Employee类的成员,但不能继承构造函数和析构函数。添加了新的数据成员department和salary。覆盖了方法Display()。请注意,虽然Employee类继承了基类Person的name和age,但因为它们是基类的私有成员,Employee类中新增或覆盖的方法不能直接修改name和age,只能经过基类原有的公有方法SetName()和SetAge()修改。若是但愿在Employee类中能直接修改name和age,必须在基类中修改它们的属性为protected。 1.7.2 base 关键字 base关键字用于从派生类中访问基类成员,它有两种基本用法:  在定义派生类的构造函数中,指明要调用的基类构造函数,因为基类可能有多个构造函数,根据base后的参数类型和个数,指明要调用哪个基类构造函数。参见上节雇员类Employee构造函数定义中的base的第一种用法。  在派生类的方法中调用基类中被派生类覆盖的方法。参见上节雇员类Employee的Display()方法定义中的base的第二种用法。 1.7.3 覆盖基类成员 在派生类中,经过声明与基类彻底相同新成员,能够覆盖基类的同名成员,彻底相同是指函数类型、函数名、参数类型和个数都相同。如上例中的方法Display()。派生类覆盖基类成员不算错误,但会致使编译器发出警告。若是增长new修饰符,表示承认覆盖,编译器再也不发出警告。请注意,覆盖基类的同名成员,并非移走基类成员,只是必须用以下格式访问基类中被派生类覆盖的方法:base.Display()。 1.7.4 C#语言类继承特色 C#语言类继承有以下特色:  C#语言只容许单继承,即派生类只能有一个基类。  C#语言继承是能够传递的,若是C从B派生,B从A派生,那么C不但继承B的成员,还要继承A中的成员。  派生类能够添加新成员,但不能删除基类中的成员。  派生类不能继承基类的构造函数、析构函数和事件。但能继承基类的属性。  派生类能够覆盖基类的同名成员,若是在派生类中覆盖了基类同名成员,基类该成员在派生类中就不能被直接访问,只能经过base.基类方法名访问。  派生类对象也是其基类的对象,但基类对象却不是其派生类的对象。例如,前边定义的雇员类Employee是Person类的派生类,全部雇员都是人类,但不少人并非雇员,多是学生,自由职业者,儿童等。所以C#语言规定,基类的引用变量能够引用其派生类对象,但派生类的引用变量不能够引用其基类对象。 1.8 类的成员 因为C#程序中每一个变量或函数都必须属于一个类或结构,不能象C或C++那样创建全局变量,所以全部的变量或函数都是类或结构的成员。类的成员能够分为两大类:类自己所声明的以及从基类中继承来的。 1.8.1 类的成员类型 类的成员包括如下类型:  局部变量:在for、switch等语句中和类方法中定义的变量,只在指定范围内有效。  字段:即类中的变量或常量,包括静态字段、实例字段、常量和只读字段。  方法成员:包括静态方法和实例方法。  属性:按属性指定的get方法和Set方法对字段进行读写。属性本质上是方法。  事件:表明事件自己,同时联系事件和事件处理函数。  索引指示器:容许象使用数组那样访问类中的数据成员。  操做符重载:采用重载操做符的方法定义类中特有的操做。  构造函数和析构函数。 包含有可执行代码的成员被认为是类中的函数成员,这些函数成员有方法、属性、索引指示器、操做符重载、构造函数和析构函数。 1.8.2 类成员访问修饰符 访问修饰符用于指定类成员的可访问性,C#访问修饰符有private、protected、public和internal4种。Private声明私有成员,私有数据成员只能被类内部的函数使用和修改,私有函数成员只能被类内部的函数调用。派生类虽然继承了基类私有成员,但不能直接访问它们,只能经过基类的公有成员访问。protected声明保护成员,保护数据成员只能被类内部和派生类的函数使用和修改,保护函数成员只能被类内部和派生类的函数调用。public声明公有成员,类的公用函数成员能够被类的外部程序所调用,类的公用数据成员能够被类的外部程序直接使用。公有函数实际是一个类和外部通信的接口,外部函数经过调用公有函数,按照预先设定好的方法修改类的私有成员和保护成员。internal声明内部成员,内部成员只能在同一程序集中的文件中才是能够访问的,通常是同一个应用(Application)或库(Library)。 1.9 类的字段和属性 通常把类或结构中定义的变量和常量叫字段。属性不是字段,本质上是定义修改字段的方法,因为属性和字段的紧密关系,把它们放到一块儿叙述。 1.9.1 静态字段、实例字段、常量和只读字段 用修饰符static声明的字段为静态字段。无论包含该静态字段的类生成多少个对象或根本无对象,该字段都只有一个实例,静态字段不能被撤销。必须采用以下方法引用静态字段:类名.静态字段名。若是类中定义的字段不使用修饰符static,该字段为实例字段,每建立该类的一个对象,在对象内建立一个该字段实例,建立它的对象被撤销,该字段对象也被撤销,实例字段采用以下方法引用:实例名.实例字段名。用const修饰符声明的字段为常量,常量只能在声明中初始化,之后不能再修改。用readonly修饰符声明的字段为只读字段,只读字段是特殊的实例字段,它只能在字段声明中或构造函数中从新赋值,在其它任何地方都不能改变只读字段的值。例子: public class Test { public const int intMax=int.MaxValue;//常量,必须赋初值 public int x=0;//实例字段 public readonly int y=0;//只读字段 public static int cnt=0;//静态字段 public Test(int x1,int y1)//构造函数 { //intMax=0;//错误,不能修改常量 x=x1;//在构造函数容许修改实例字段 y=y1;//在构造函数容许修改只读字段 cnt++;//每建立一个对象都调用构造函数,用此语句能够记录对象的个数 } public void Modify(int x1,int y1) { //intMax=0;//错误,不能修改常量 x=x1; cnt=y1; //y=10;//不容许修改只读字段 } } class Class1 { static void Main(string[] args) { Test T1=new Test(100,200); T1.x=40;//引用实例字段采用方法:实例名.实例字段名 Test.cnt=0;//引用静态字段采用方法:类名.静态字段名 int z=T1.y;//引用只读字段 z=Test.intMax;//引用常量 } } 1.9.2 属性 C#语言支持组件编程,组件也是类,组件用属性、方法、事件描述。属性不是字段,但必然和类中的某个或某些字段相联系,属性定义了获得和修改相联系的字段的方法。C#中的属性更充分地体现了对象的封装性:不直接操做类的数据内容,而是经过访问器进行访问,借助于get和set方法对属性的值进行读写。访问属性值的语法形式和访问一个变量基本同样,使访问属性就象访问变量同样方便,符合习惯。 在类的基本概念一节中,定义一个描述我的状况的类Person,其中字段name和age是私有字段,记录姓名和年龄,外部经过公有方法SetName和SetAge修改这两个私有字段。如今用属性来描述姓名和年龄。例子以下: using System; public class Person { private string P_name="张三";//P_name是私有字段 private int P_age=12;//P_age是私有字段 public void Display()//类的方法声明,显示姓名和年龄 { Console.WriteLine("姓名:{0},年龄:{1}",P_name,P_age); } public string Name//定义属性Name { get { return P_name;} set { P_name=value;} } public int Age//定义属性Age { get { return P_age;} set { P_age=value;} } } public class Test { public static void Main() { Person OnePerson= new Person(); OnePerson.Name="田七";//value="田七",经过set方法修改变量P_Name string s=OnePerson.Name;//经过get方法获得变量P_Name值 OnePerson.Age=20;//经过定义属性,既保证了姓名和年龄按指定方法修改 int x=OnePerson.Age;//语法形式和修改、获得一个变量基本一致,符合习惯 OnePerson.Display(); } } 在属性的访问声明中,只有set访问器代表属性的值只能进行设置而不能读出,只有get访问器代表属性的值是只读的不能改写,同时具备set访问器和get访问器代表属性的值的读写都是容许的。 虽然属性和字段的语法比较相似,但因为属性本质上是方法,所以不能把属性当作变量那样使用,也不能把属性做为引用型参数或输出参数来进行传递。 1.10 类的方法 方法是类中用于执行计算或其它行为的成员。全部方法都必须定义在类或结构中。 1.10.1 方法的声明 方法的声明格式以下: 属性 方法修饰符 返回类型 方法名(形参列表){方法体} 方法修饰符包括new、public、protected、internal、private、static、virtual、sealed、override、abstract和extern。这些修饰符有些已经介绍过,其它修饰符将逐一介绍。返回类型能够是任何合法的C#数据类型,也能够是void,即无返回值。形参列表的格式为:(形参类型 形参1,形参类型 形参2,...),能够有多个形参。不能使用C语言的形参格式。 1.10.2 方法参数的种类 C#语言的方法能够使用以下四种参数(请注意和参数类型的区别):  值参数,不含任何修饰符。  引用参数,以ref修饰符声明。  输出参数,以out修饰符声明。  数组参数,以params修饰符声明。 1. 值参数 当用值参数向方法传递参数时,程序给实参的值作一份拷贝,而且将此拷贝传递给该方法,被调用的方法不会修改实参的值,因此使用值参数时,能够保证明参的值是安全的。若是参数类型是引用类型,例如是类的引用变量,则拷贝中存储的也是对象的引用,因此拷贝和实参引用同一个对象,经过这个拷贝,能够修改实参所引用的对象中的数据成员。 2. 引用参数 有时在方法中,须要修改或获得方法外部的变量值,C语言用向方法传递实参指针来达到目的,C#语言用引用参数。当用引用参数向方法传递实参时,程序将把实参的引用,即实参在内存中的地址传递给方法,方法经过实参的引用,修改或获得方法外部的变量值。引用参数以ref修饰符声明。注意在使用前,实参变量要求必须被设置初始值。 3. 输出参数 为了把方法的运算结果保存到外部变量,所以须要知道外部变量的引用(地址)。输出参数用于向方法传递外部变量引用(地址),因此输出参数也是引用参数,与引用参数的差异在于调用方法前无需对变量进行初始化。在方法返回后,传递的变量被认为通过了初始化。值参数、引用参数和输出参数的使用见下例: using System; class g{public int a=0;}//类定义 class Class1 { public static void F1(ref char i)//引用参数 { i='b';} public static void F2(char i)//值参数,参数类型为值类型 { i='d';} public static void F3(out char i)//输出参数 { i='e';} public static void F4(string s)//值参数,参数类型为字符串 { s="xyz";} public static void F5(g gg)//值参数,参数类型为引用类型 { gg.a=20;} public static void F6(ref string s)//引用参数,参数类型为字符串 { s="xyz";} static void Main(string[] args) { char a='c'; string s1="abc"; F2(a);//值参数,不能修改外部的a Console.WriteLine(a);//因a未被修改,显示c F1(ref a);//引用参数,函数修改外部的a的值 Console.WriteLine(a);//a被修改成b,显示b Char j; F3(out j);//输出参数,结果输出到外部变量j Console.WriteLine(j);//显示e F4(s1);//值参数,参数类型是字符串,s1为字符串引用变量 Console.WriteLine(s1);//显示:abc,字符串s1不被修改 g g1=new g(); F5(g1);//值参数,但实参是一个类引用类型变量 Console.WriteLine(g1.a.ToString());//显示:20,修改对象数据 F6(ref s1);//引用参数,参数类型是字符串,s1为字符串引用变量 Console.WriteLine(s1);//显示:xyz,字符串s1被修改 } } 4. 数组参数 数组参数使用params说明,若是形参表中包含了数组参数,那么它必须是参数表中最后一个参数,数组参数只容许是一维数组。好比string[]和string[][]类型均可以做为数组型参数。最后,数组型参数不能再有ref和out修饰符。见下例: using System; class Class1 { static void F(params int[] args)//数组参数,有params说明 { Console.Write("Array contains {0} elements:",args.Length); foreach (int i in args) Console.Write(" {0}",i); Console.WriteLine(); } static void Main(string[] args) { int[] a = {1,2,3}; F(a);//实参为数组类引用变量a F(10, 20, 30, 40);//等价于F(new int[] {60,70,80,90}); F(new int[] {60,70,80,90});//实参为数组类引用 F();//等价于F(new int[] {}); F(new int[] {});//实参为数组类引用,数组无元素 } } 程序输出 Array contains 3 elements: 1 2 3 Array contains 4 elements: 10 20 30 40 Array contains 4 elements: 60,70,80,90 Array contains 0 elements: Array contains 0 elements: 方法的参数为数组时也能够不使用params,此种方法能够使用一维或多维数组,见下例: using System; class Class1 { static void F(int[,] args)//值参数,参数类型为数组类引用变量,无params说明 { Console.Write("Array contains {0} elements:",args.Length); foreach (int i in args) Console.Write(" {0}",i); Console.WriteLine(); } static void Main(string[] args) { int[,] a = {{1,2,3},{4,5,6}}; F(a);//实参为数组类引用变量a //F(10, 20, 30, 40);//此格式不能使用 F(new int[,] {{60,70},{80,90}});//实参为数组类引用 //F();//此格式不能使用 //F(new int[,] {});//此格式不能使用 } } 程序输出 Array contains 3 elements: 1 2 3 4 5 6 Array contains 4 elements: 60,70,80,90 1.10.3 静态方法和实例方法 用修饰符static声明的方法为静态方法,不用修饰符static声明的方法为实例方法。无论类生成或未生成对象,类的静态方法均可以被使用,使用格式为:类名.静态方法名。静态方法只能使用该静态方法所在类的静态数据成员和静态方法。这是由于使用静态方法时,该静态方法所在类可能尚未对象,即便有对象,因为用类名.静态方法名方式调用静态方法,静态方法没有this指针来存放对象的地址,没法断定应访问哪一个对象的数据成员。在类建立对象后,实例方法才能被使用,使用格式为:对象名.实例方法名。实例方法能够使用该方法所在类的全部静态成员和实例成员。例子以下: using System; public class UseMethod { private static int x=0;//静态字段 private int y=1;//实例字段 public static void StaticMethod()//静态方法 { x=10;//正确,静态方法访问静态数据成员 //y=20;//错误,静态方法不能访问实例数据成员 } public void NoStaticMethod()//实例方法 { x=10;//正确,实例方法访问静态数据成员 y=20;//正确,实例方法访问实例数据成员 } } public class Class1 { public static void Main() { UseMethod m=new UseMethod(); UseMethod.StaticMethod();//使用静态方法格式为:类名.静态方法名 m.NoStaticMethod();//使用实例方法格式为:对象名.实例方法名 } } 1.10.4 方法的重载 在C#语言中,若是在同一个类中定义的函数名相同,而参数类型或参数个数不一样,认为是不相同的函数,仅返回值不一样,不能看做不一样函数,这叫作函数的重载。前边Person类中定义了多个构造函数就是重载的例子。在C语言中,若计算一个数据的绝对值,则须要对不一样数据类型求绝对值方法使用不一样的方法名,如用abc()求整型数绝对值,labs()求长整型数绝对值,fabs()求浮点数绝对值。而在C#语言中,能够使用函数重载特性,对这三个函数定义一样的函数名,但使用不一样的参数类型。下面是实现方法: using System; public class UseAbs { public int abs(int x)//整型数求绝对值 { return(x<0 ? -x:x);} public long abs(long x)//长整型数求绝对值 {return(x<0 ? -x:x);} public double abs(double x)//浮点数求绝对值 {return(x<0 ? -x:x);} } class Class1 { static void Main(string[] args) { UseAbs m=new UseAbs(); int x=-10; long y=-123; double z=-23.98d; x=m.abs(x); y=m.abs(y); z=m.abs(z); Console.WriteLine("x={0},y={1},z={2}",x,y,z); } } 类的对象调用这些同名方法,在编译时,根据调用方法的实参类型决定调用那个同名方法,计算不一样类型数据的绝对值。这给编程提供了极大方便。 1.10.5 操做符重载 操做符重载是将C#语言中的已有操做符赋予新的功能,但与该操做符的原本含义不冲突,使用时只需根据操做符出现的位置来判别其具体执行哪种运算。操做符重载,实际是定义了一个操做符函数,操做符函数声明的格式以下: static public 函数返回类型 operator 从新定义的操做符(形参表) C#语言中有一些操做符是能够重载的,例如:+ - ! ~ ++ -- true false * / % & | ^ << >> == != > < >= <=等等。但也有一些操做符是不容许进行重载的,例如:=, &&, ||, ?:, new, typeof, sizeof, is等。 下边的例子,定义一个复数类,而且但愿复数的加减乘除用符号+,-.*,/来表示。 using System; class Complex//复数类定义 { private double Real;//复数实部 private double Imag;//复数虚部 public Complex(double x,double y)//构造函数 { Real=x; Imag=y; } static public Complex operator - (Complex a)//重载一元操做符负号,注意1个参数 { return (new Complex(-a.Real,-a.Imag));} static public Complex operator +(Complex a,Complex b)//重载二元操做符加号 { return (new Complex(a.Real+b.Real,a.Imag+b.Imag));} public void Display() { Console.WriteLine("{0}+({1})j",Real,Imag);} } class Class1 { static void Main(string[] args) { Complex x=new Complex(1.0,2.0); Complex y=new Complex(3.0,4.0); Complex z=new Complex(5.0,7.0); x.Display();//显示:1+(2)j y.Display();//显示:3+(4)j z.Display();//显示:5+(7)j z=-x;//等价于z=opeator-(x) z.Display();//显示:-1+(-2)j z=x+y;//即z=opeator+(x,y) z.Display();//显示:4+(6)j } } 1.10.6 this关键字 每一个类均可以有多个对象,例如定义Person类的两个对象: Person P1=new Person("李四",30); Person P2=new Person("张三",40); 所以P1.Display()应显示李四信息,P2.Display()应显示张三信息,但不管建立多少个对象,只有一个方法Display(),该方法是如何知道显示那个对象的信息的呢?C#语言用引用变量this记录调用方法Display()的对象,当某个对象调用方法Display()时,this便引用该对象(记录该对象的地址)。所以,不一样的对象调用同一方法时,方法便根据this所引用的不一样对象来肯定应该引用哪个对象的数据成员。this是类中隐含的引用变量,它是被自动被赋值的,能够使用但不能被修改。例如:P1.Display(),this引用对象P1,显示李四信息。P2.Display(),this引用对象P2,显示张三信息。 1.11 类的多态性 在面向对象的系统中,多态性是一个很是重要的概念。C#支持两种类型的多态性,第一种是编译时的多态性,一个类的对象调用若干同名方法,系统在编译时,根据调用方法的实参类型及实参的个数决定调用那个同名方法,实现何种操做。编译时的多态性是经过方法重载来实现的。C#语言的方法重载以及操做符重载和C++语言的基本一致。 第二种是运行时的多态性,是在系统运行时,不一样对象调用一个名字相同,参数的类型及个数彻底同样的方法,会完成不一样的操做。C#运行时的多态性经过虚方法实现。在类的方法声明前加上了virtual修饰符,被称之为虚方法,反之为非虚方法。C#语言的虚方法和C++语言的基本一致。下面的例子说明了虚方法与非虚方法的区别: using System; class A { public void F()//非虚方法 { Console.Write(" A.F");} public virtual void G()//虚方法 { Console.Write(" A.G");} } class B:A//A类为B类的基类 { new public void F()//覆盖基类的同名非虚方法F(),注意使用new { Console.Write(" B.F");} public override void G()//覆盖基类的同名虚方法G(),注意使用override { Console.Write(" B.G");} } class Test { static void F2(A aA)//注意,参数为A类引用变量 { aA.G();} static void Main() { B b=new B(); A a1=new A(); A a2=b;//容许基类引用变量引用派生类对象,a2引用派生类B对象b a1.F();//调用基类A的非虚方法F(),显示A.F a2.F();//F()为非虚方法,调用基类A的F(),显示A.F b.F();//F()为非虚方法,调用派生类B的F(),显示B.F a1.G();//G()为虚方法,因a1引用基类A对象,调用基类A的G(),显示A.G a2.G();//G()为虚方法,因a2引用派生类B对象,调用派生类B的G(),显示B.G F2(b);//实参为派生类B对象,因为A aA=b,调用派生类B的函数G(),显示B.G F2(a1);//实参为基类A对象,调用A类的函数G(),显示A.G } } 那么输出应该是: A.F A.F B.F A.G B.G B.G A.G 注意例子中,不一样对象调用同名非虚方法F()和同名虚方法G()的区别。a2虽然是基类引用变量,但它引用派生类对象b。因为G()是虚方法,所以a2.G()调用派生类B的G(),显示G.F。但因为F()是非虚方法,a2.F()仍然调用基类A的F(),显示A.F。或者说,若是将基类引用变量引用不一样对象,或者是基类对象,或者是派生类对象,用这个基类引用变量分别调用同名虚方法,根据对象不一样,会完成不一样的操做。而非虚方法则不具有此功能。 方法F2(A aA)中,参数是A类类型,F2(b)中形参和实参的关系是:A aA=b,即基类引用变量aA引用派生类对象b,aA.G()调用派生类B的函数G(),显示B.G。同理,F2(a1)实参为基类A对象,调用A类的函数G(),显示A.G。 在类的基本概念一节中,定义一个描述我的状况的类Person,其中公有方法Display()用来显示我的信息。在派生雇员类Employee中,覆盖了基类的公有方法Display(),以显示雇员新增长的信息。咱们但愿隐藏这些细节,但愿不管基类仍是派生类,都调用同一个显示方法,根据对象不一样,自动显示不一样的信息。能够用虚方法来实现,这是一个典型的多态性例子。例子 using System; public class Person { private String name="张三";//类的数据成员声明 private int age=12; protected virtual void Display()//类的虚方法 { Console.WriteLine("姓名:{0},年龄:{1}",name,age); } public Person(string Name,int Age)//构造函数,函数名和类同名,无返回值 { name=Name; age=Age; } static public void DisplayData(Person aPerson)//静态方法 { aPerson.Display();//不是静态方法调用实例方法,如写为Display()错误 } } public class Employee:Person//Person类是基类 { private string department; private decimal salary; public Employee(string Name,int Age,string D,decimal S):base(Name,Age) { department=D; salary=S; } protected override void Display()//重载虚方法,注意用override { base.Display();//访问基类同名方法 Console.WriteLine("部门:{0} 薪金:{1} ", department,salary); } } class Class1 { static void Main(string[] args) { Person OnePerson=new Person("李四",30); Person.DisplayData(OnePerson);//显示基类数据 Employee OneEmployee=new Employee("王五",40,"财务部",2000); Person.DisplayData(OneEmployee); //显示派生类数据 } } 运行后,显示的效果是: 姓名: 李四,年龄:30 姓名: 王五,年龄:40 部门:财务部 薪金:2000 1.12 抽象类和抽象方法 抽象类表示一种抽象的概念,只是但愿以它为基类的派生类有共同的函数成员和数据成员。抽象类使用abstract修饰符,对抽象类的使用有如下几点规定:  抽象类只能做为其它类的基类,它不能直接被实例化。  抽象类容许包含抽象成员,虽然这不是必须的。抽象成员用abstract修饰符修饰。  抽象类不能同时又是密封的。  抽象类的基类也能够是抽象类。若是一个非抽象类的基类是抽象类,则该类必须经过覆盖来实现全部继承而来的抽象方法,包括其抽象基类中的抽象方法,若是该抽象基类从其它抽象类派生,还应包括其它抽象类中的全部抽象方法。 请看下面的示例: abstract class Figure//抽象类定义 { protected double x=0,y=0; public Figure(double a,double b) { x=a; y=b; } public abstract void Area();//抽象方法,无实现代码 } class Square:Figure///类Square定义 { public Square(double a,double b):base(a,b) {} public override void Area()//不能使用new,必须用override { Console.WriteLine("矩形面积是:{0}",x*y);} } class Circle:Figure///类Square定义 { public Circle(double a):base(a,a) {} public override void Area() { Console.WriteLine("园面积是:{0}",3.14*x*y);} } class Class1 { static void Main(string[] args) { Square s=new Square(20,30); Circle c=new Circle(10); s.Area(); c.Area(); } } 程序输出结果为: 矩形面积是:600 园面积是:314 抽象类Figure提供了一个抽象方法Area(),并无实现它,类Square和Circle从抽象类Figure中继承方法Area(),分别具体实现计算矩形和园的面积。 在类的基本概念一节中,定义一个描述我的状况的类Person,它只是描述了一我的最通常的属性和行为,所以不但愿生成它的对象,能够定义它为抽象类。 注意:C++程序员在这里最容易犯错误。C++中没有对抽象类进行直接声明的方法,而认为只要在类中定义了纯虚函数,这个类就是一个抽象类。纯虚函数的概念比较晦涩,直观上不容易为人们接受和掌握,所以C#抛弃了这一律念。 1.13 密封类和密封方法 有时候,咱们并不但愿本身编写的类被继承。或者有的类已经没有再被继承的必要。C#提出了一个密封类(sealed class)的概念,帮助开发人员来解决这一问题。 密封类在声明中使用sealed修饰符,这样就能够防止该类被其它类继承。若是试图将一个密封类做为其它类的基类,C#编译器将提示出错。理所固然,密封类不能同时又是抽象类,由于抽象老是但愿被继承的。 C#还提出了密封方法(sealed method)的概念。方法使用sealed修饰符,称该方法是一个密封方法。在派生类中,不能覆盖基类中的密封方法。 1.14 接口 与类同样,在接口中能够定义一个和多个方法、属性、索引指示器和事件。但与类不一样的是,接口中仅仅是它们的声明,并不提供实现。所以接口是函数成员声明的集合。若是类或结构从一个接口派生,则这个类或结构负责实现该接口中所声明的全部成员。一个接口能够从多个接口继承,而一个类或结构能够实现多个接口。因为C#语言不支持多继承,所以,若是某个类须要继承多个类的行为时,只能使用多个接口加以说明。 1.14.1 接口声明 接口声明是一种类型声明,它定义了一种新的接口类型。接口声明格式以下: 属性 接口修饰符 interface 接口名:基接口{接口体} 其中,关键字interface、接口名和接口体时必须的,其它项是可选的。接口修饰符能够是new、public、protected、internal和private。例子: public interface IExample {//全部接口成员都不能包括实现 string this[int index] {get;set;}//索引指示器声明 event EventHandler E;//事件声明 void F(int value);//方法声明 string P { get; set;}//属性声明 } 声明接口时,需注意如下内容:  接口成员只能是方法、属性、索引指示器和事件,不能是常量、域、操做符、构造函数或析构函数,不能包含任何静态成员。  接口成员声明不能包含任何修饰符,接口成员默认访问方式是public。 1.14.2 接口的继承 相似于类的继承性,接口也有继承性。派生接口继承了基接口中的函数成员说明。接口容许多继承,一个派生接口能够没有基接口,也能够有多个基接口。在接口声明的冒号后列出被继承的接口名字,多个接口名之间用分号分割。例子以下: using System; interface IControl { void Paint(); } interface ITextBox:IControl//继承了接口Icontrol的方法Paint() { void SetText(string text); } interface IListBox:IControl//继承了接口Icontrol的方法Paint() { void SetItems(string[] items); } interface IComboBox:ITextBox,IListBox {//能够声明新方法 } 上面的例子中,接口ITextBox和IListBox都从接口IControl中继承,也就继承了接口IControl的Paint方法。接口IComboBox从接口ITextBox和IListBox中继承,所以它应该继承了接口ITextBox的SetText方法和IListBox的SetItems方法,还有IControl的Paint方法。 1.14.3 类对接口的实现 前面已经说过,接口定义不包括函数成员的实现部分。继承该接口的类或结构应实现这些函数成员。这里主要讲述经过类来实现接口。类实现接口的本质是,用接口规定类应实现那些函数成员。用类来实现接口时,接口的名称必须包含在类声明中的基类列表中。 在类的基本概念一节中,定义一个描述我的状况的类Person,从类Person能够派生出其它类,例如:工人类、公务员类、医生类等。这些类有一些共有的方法和属性,例如工资属性。通常但愿全部派生类访问工资属性时用一样变量名。该属性定义在类Person中不合适,由于有些人无工资,如小孩。如定义一个类做为基类,包含工资属性,但C#不支持多继承。可行的办法是使用接口,在接口中声明工资属性。工人类、公务员类、医生类等都必须实现该接口,也就保证了它们访问工资属性时用一样变量名。例子以下: using System; public interface I_Salary//接口 { decimal Salary//属性声明 { get; set; } } public class Person {…//见1.9.2属性节Person类定义,这里不重复了。 } public class Employee:Person,I_Salary//Person类是基类,I_Salary是接口 {//不一样程序员完成工人类、医生类等,定义工资变量名称可能不一样 private decimal salary; public new void Display() { base.Display(); Console.WriteLine("薪金:{0} ",salary); } //工人类、医生类等都要实现属性Salary,保证使用的工资属性同名 public decimal Salary { get { return salary;} set { salary=value;} } } public class Test { public static void Main() { Employee S=new Employee(); S.Name="田七";//修改属性Name S.Age=20;//修改属性Age S.Salary=2000;//修改属性Salary S.Display(); } } 若是类实现了某个接口,类也隐式地继承了该接口的全部基接口,无论这些基接口有没有在类声明的基类表中列出。所以,若是类从一个接口派生,则这个类负责实现该接口及该接口的全部基接口中所声明的全部成员。 1.15 表明 在这里要介绍的是C#的一个引用类型----表明(delegate),也翻译为委托。它实际上至关于C语言的函数指针。与指针不一样的是C#中的表明是类型安全的。表明类声明格式以下: 属性集 修饰符 delegate 函数返回类型 定义的表明标识符(函数形参列表); 修饰符包括new、public、protected、internal和private。例如咱们能够声明一个返回类型为int,无参数的函数的表明MyDelegate: public delegate int MyDelegate();//只能表明返回类型为int,无参数的函数 声明了表明类MyDelegate,能够建立表明类MyDelegate的对象,用这个对象去表明一个静态方法或非静态的方法,所表明的方法必须为int类型,无参数。看下面的例子: using System; delegate int MyDelegate();//声明一个表明,注意声明的位置 public class MyClass { public int InstanceMethod()//非静态的方法,注意方法为int类型,无参数 { Console.WriteLine("调用了非静态的方法。"); return 0; } static public int StaticMethod()//静态方法,注意方法为int类型,无参数 { Console.WriteLine("调用了静态的方法。"); return 0; } } public class Test { static public void Main () { MyClass p = new MyClass(); //用new创建表明类MyDelegate对象,d中存储非静态的方法InstanceMethod的地址 MyDelegate d=new MyDelegate(p.InstanceMethod);//参数是被表明的方法 d();//调用非静态方法 //用new创建表明类MyDelegate对象,d中存储静态的方法StaticMethod的地址 d=new MyDelegate(MyClass.StaticMethod);//参数是被表明的方法 d();//调用静态方法 } } 程序的输出结果是: 调用了非静态的方法。 调用了静态的方法。 1.16 事件 事件是C#语言内置的语法,能够定义和处理事件,为使用组件编程提供了良好的基础。 1.16.1 事件驱动 Windows操做系统把用户的动做都看做消息,C#中称做事件,例如用鼠标左键单击按钮,发出鼠标单击按钮事件。Windows操做系统负责统一管理全部的事件,把事件发送到各个运行程序。各个程序用事件函数响应事件,这种方法也叫事件驱动。 C#语言使用组件编制Windows应用程序。组件本质上是类。在组件类中,预先定义了该组件可以响应的事件,以及对应的事件函数,该事件发生,将自动调用本身的事件函数。例如,按钮类中定义了单击事件Click和单击事件函数。一个组件中定义了多个事件,应用程序中没必要也不必响应全部的事件,而只需响应其中不多事件,程序员编制相应的事件处理函数,用来完成须要响应的事件所应完成的功能。如今的问题是,第一,如何把程序员编制的事件处理函数和组件类中预先定义的事件函数联系起来。第二,如何使不需响应的事件无动做。这是本节要节的解决问题。 1.16.2 事件的声明 在C#中,事件首先表明事件自己,例如按钮类的单击事件,同时,事件仍是表明类引用变量,能够表明程序员编制的事件处理函数,把事件和事件处理函数联系在一块儿。下面的例子定义了一个Button组件,这个例子不完整,只是说明问题。实际在C#语言类库中已预约义了Button组件,这里的代码只是想说明Button组件中是如何定义事件的。例子以下: public delegate void EventHandler(object sender,EventArgs e);//表明声明 //EventHandler能够表明没有返回值,参数为(object sender,EventArgs e)的函数 public class Button:Control//定义一个按钮类Button组件 {…//按钮类Button其它成员定义 public event EventHandler Click;//声明一个事件Click,是表明类引用变量 protected void OnClick(EventArgs e)//Click事件发生,自动触发OnClick方法 { if(Click!=null)//若是Click已表明了事件处理函数,执行这个函数 Click(this,e); } public void Reset() { Click=null;} } 在这个例子中,Click事件发生,应有代码保证(未列出)自动触发OnClick方法。Click是类Button的一个事件,同时也是表明EventHandler类的引用变量,如令Click表明事件处理函数,该函数完成Click事件应完成的功能,Click事件发生时,执行事件处理函数。 1.16.3 事件的预订和撤消 在随后的例子中,咱们声明了一个使用Button类的登陆对话框类,对话框类含有两个按钮:OK和Cancel按钮。 public class LoginDialog: Form//登陆对话框类声明 { Button OkButton; Button CancelButton; public LoginDialog()//构造函数 { OkButton=new Button();//创建按钮对象OkButton //Click表明OkButtonClick方法,注意+=的使用 OkButton.Click+=new EventHandler(OkButtonClick); CancelButton=new Button();//创建按钮对象OkButton CancelButton.Click += new EventHandler(CancelButtonClick); } void OkButtonClick(object sender, EventArgs e) {…//处理OkButton.Click事件的方法 } void CancelButtonClick(object sender, EventArgs e) {…//处理CancelButton.Click事件的方法 } } 在例子中创建了Button类的两个实例,单击按钮事件Click经过以下语句和事件处理方法联系在一块儿:OkButton.Click+=new EventHandler(OkButtonClick),该语句的意义是使OkButton.Click表明事件处理方法OkButtonClick,这样只要Click事件被触发,事件处理方法OkButtonClick就会被自动调用。撤消事件和事件处理方法OkButtonClick的联系采用以下语句实现:OkButton.Click-=new EventHandler(OkButtonClick),这时,OkButton.Click就再也不表明事件处理方法,Click事件被触发,方法OkButtonClick就不会被调用了。务必理解这两条语句的用法。使用Visual Studio.Net集成环境能够自动创建这种联系,在自动生成的代码中包括这两条语句。 1.17 索引指示器 在C#语言中,数组也是类,好比咱们声明一个整型数数组:int[] arr=new int[5],实际上生成了一个数组类对象,arr是这个对象的引用(地址),访问这个数组元素的方法是:arr[下标],在数组类中,使用索引访问元素是如何实现的呢?是否能够定义本身的类,用索引访问类中的数据成员?索引指示器(indexer)为咱们提供了经过索引方式方便地访问类的数据成员的方法。 首先看下面的例子,用于打印出小组人员的名单: using System class Team { string[] s_name = new string[2];//定义字符串数组,记录小组人员姓名 public string this[int nIndex]//索引指示器声明,this为类Team类的对象 { get//用对象名[索引]获得记录小组人员姓名时,调用get函数 { return s_name[nIndex]; } set//用对象名[索引]修改记录小组人员姓名时,调用set函数 { s_name[nIndex] =value;//value为被修改值 } } } class Test { public static void Main() { Team t1 = new Team(); t1[0]="张三"; t1[1]="李斯"; Console.WriteLine("{0},{1}",t1[0], t1[1]); } } 显示结果以下:张三,李斯 1.18 名字空间 一个应用程序可能包含许多不一样的部分,除了本身编制的程序以外,还要使用操做系统或开发环境提供的函数库、类库或组件库,软件开发商处购买的函数库、类库或组件库,开发团队中其它人编制的程序,等等。为了组织这些程序代码,使应用程序能够方便地使用这些程序代码,C#语言提出了名字空间的概念。名字空间是函数、类或组件的容器,把它们按类别放入不一样的名字空间中,名字空间提供了一个逻辑上的层次结构体系,使应用程序能方便的找到所需代码。这和C语言中的include语句的功能有些类似,但实现方法彻底不一样。 1.18.1 名字空间的声明 用关键字namespace声明一个名字空间,名字空间的声明要么是源文件using语句后的第一条语句,要么做为成员出如今其它名字空间的声明之中,也就是说,在一个名字空间内部还能够定义名字空间成员。全局名字空间应是源文件using语句后的第一条语句。在同一名字空间中,不容许出现同名名字空间成员或同名的类。在声明时不容许使用任何访问修饰符,名字空间隐式地使用public修饰符。例子以下: using System; namespace N1//N1为全局名字空间的名称,应是using语句后的第一条语句 { namespace N2//名字空间N1的成员N2 { class A//在N2名字空间定义的类不该重名 { void f1(){};} class B { void f2(){};} } } 也能够采用非嵌套的语法来实现以上名字空间: namespace N1.N2//类A、B在名字空间N1.N2中 { class A { void f1(){};} class B { void f2(){};} } 也能够采用以下格式: namespace N1.N2//类A在名字空间N1.N2中 { class A { void f1(){};} } namespace N1.N2//类B在名字空间N1.N2中 { class B { void f2(){};} } 1.18.2 名字空间使用 如在程序中,需引用其它名字空间的类或函数等,能够使用语句using,例如需使用上节定义的方法f1()和f2(),能够采用以下代码: using N1.N2; class WelcomeApp { A a=new A(); a.f1(); } using N1.N2其实是告诉应用程序到哪里能够找到类A。请读者从新看一下1.2.1节中的例子。 1.19 非安全代码 在C和C++的程序员看来,指针是最强有力的工具之一,同时又带来许多问题。由于指针指向的数据类型可能并不相同,好比你能够把int类型的指针指向一个float类型的变量,而这时程序并不会出错。若是你删除了一个不该该被删除的指针,好比Windows中指向主程序的指针,程序就有可能崩溃。所以滥用指针给程序带来不安全因素。正由于如此,在C#语言中取消了指针这个概念。虽然不使用指针能够完成绝大部分任务,但有时在程序中还不可避免的使用指针,例如调用Windows操做系统的API函数,其参数多是指针,因此在C#中还容许使用指针,但必须声明这段程序是非安全(unsafe)的。能够指定一个方法是非安全的,例如:unsafe void F1(int * p){…}。能够指定一条语句是非安全的,例如:unsafe int* p2=p1;还能够指定一段代码是非安全的,例如:unsafe{ int* p2=p1;int* p3=p4;}。在编译时要采用以下格式:csc 要编译的C#源程序 /unsafe。 习题 1. 从键盘输入姓名,在显示器中显示对输入姓名的问候。(提示:string为字符串类型,用语句string s=Console.ReadLine()输入姓名) 2. 构造函数和析购函数的主要做用是什么?它们各有什么特性? 3. 定义点类,数据成员为私有成员,增长有参数和无参数构造函数,在主函数中生成点类对象,并用字符显示点类对象的坐标。 4. 定义矩形类,数据成员为私有成员,增长有参数和无参数构造函数,在主函数中生成矩形类对象,并用字符显示矩形类对象的长、宽和矩形左上角的坐标。 5. 设计一个计数器类,统计键入回车的次数,数据成员为私有成员,在主程序中使用此类统计键入回车的次数。 6. 说明值类型和引用类型的区别,并和C语言相应类型比较。 7. 定义点结构,在主函数中生成点结构变量,从键盘输入点的位置,并从新显示坐标。 8. 定义整型一维数组,从键盘输入数组元素数值后,用循环语句显示全部元素的值。 9. 输入字符串,将字符串第一个字母和每一个空格后的字母变为大写,其他字母为小写后输出。 10. 输入5个数,在每两个数之间增长3个空格后输出。 11. 编一个猜数程序,程序设定一个1位十进制数,容许用户猜3次,错了告诉比设定数大仍是小,用switch语句实现。 12. C#语言for语句能够这样使用:for(int i;i<10;i++),请问,i的有效使用范围。 13. 用字符*在CRT上显示一个矩形。 14. 输入一个字符串,用foreach语句计算输入的字符串长度,并显示长度。 15. 输入两个数相加,并显示和。用异常语句处理输入错误。 16. 将1.6.3节中try–catch-finally语句例子改成try-finally和try–catch语句。 17. 定义点类,从点类派生矩形类,数据成员为私有成员,增长有参数和无参数构造函数,在主函数中生成矩形类对象,并用字符显示矩形类对象的长、宽和矩形左上角的坐标。 18. 重作12题,将数据成员用属性表示。 19. 定义一个类,将类外部的char数组元素都变为大写。主程序输入一个字符串,将其变为char数组,变为大写后输出每个char数组元素。分别用类对象和静态函数实现。 20. 定义分数类,实现用符号+,-,*,/完成分数的加减乘除。在主函数中输入两个数,完成运算后输出运算结果。 21. 创建一个sroot()函数,返回其参数的二次根。重载它,让它可以分别返回整数、长整数和双精度参数的二次根。 22. 从新设计complex类,完成复数的+、-、*、/四则运算。 23. 定义点类,从点类派生矩形类和园类,主程序实现用同一个方法显示矩形和园的面积。 24. 重作19题,将点类定义为抽象类。 25. 重作19题,改成接口实现,即将点类改成接口。 第二章 Windows编程的基础知识 2.1 窗口 Windows应用程序通常都有一个窗口,窗口是运行程序与外界交换信息的界面。一个典型的窗口包括标题栏,最小化按钮,最大/还原按钮,关闭按钮,系统菜单图标,菜单,工具条,状态栏,滚动条,客户区等。程序员的工做之一是设计符合本身要求的窗口,C#用控件的方法设计界面。编程另外一个工做是在用户区显示数据和图形。 2.2 Windows的消息系统 2.2.1 消息驱动(事件驱动) Windows应用程序和dos程序(控制台程序)的最大区别是事件驱动,也叫消息驱动。dos程序运行时如要读键盘,则要独占键盘等待用户输入,如用户不输入,则CPU一直执行键盘输入程序,等待用户输入,即dos程序独占外设和CPU。 Windows操做系统是一个多任务的操做系统,容许同时运行多个程序,它不容许任何一个程序独占外设,如键盘,鼠标等,全部运行程序共享外设和CPU,各个运行程序都要随时从外设接受命令,执行命令。 所以必须由Windows操做系通通一管理各类外设。Windows把用户对外设的动做都看做事件(消息),如单击鼠标左键,发送单击鼠标左键事件,用户按下键盘,发送键盘被按下的事件等。Windows操做系通通一负责管理全部的事件,把事件发送到各个运行程序,而各个运行程序用一个函数响应事件,这个函数叫事件响应函数。这种方法叫事件驱动。每一个事件都有它本身的事件响应函数,当接到Windows事件后,自动执行此事件的事件响应函数。程序员编程的主要工做就是编制这些事件的处理函数,完成相应的工做。 2.2.2 事件队列 Windows把用户的动做都看做事件,Windows操做系统负责管理全部的事件,事件发生后,这些事件被放到系统事件队列中,Windows操做系统从系统事件队列中逐一取出事件,分析各个事件,分送事件到相应运行程序的事件队列中。而每一个运行程序,则利用消息循环方法(既循环取得本身事件队列中的事件)获得事件,并把他们送到当前活动窗口,由窗口中的事件函数响应各个事件(消息)。所以,每一个运行程序都有本身的事件队列。 2.2.3 注视窗口 Windows操做系统容许多个程序同时运行,每一个程序可能拥有多个窗口,但其中只有一个窗口是活动的,咱们能从窗口的标题栏的颜色来识别一个活动窗口,这个窗口接收Windows系统发来的大部分的事件。这个应用程序的窗口被称为注视(活动)窗口。 2.3 Windows编程接口和类库 操做系统为了方便应用程序设计,通常都要提供一个程序库,一些设计应用程序的共用代码都包含在这个程序库中。程序员能够调用这些代码,以简化编程。这节介绍一些经常使用程序库。 2.3.1 Windows编程接口(API) API(Application Programming Interface)是Windows9八、2000和XP操做系统中提供的一组函数,这些函数采用C语言调用格式,是为程序员编制Windows应用程序提供的编程接口。程序员用C语言直接调用API也能够编制Windows应用程序,但大量的程序代码必须由程序员本身编写,而API函数很是庞大,给编程者带来很大的困难。 2.3.2 MFC类库 因为API函数十分庞大复杂,看不到函数之间的关系,使程序员不易使用。用C语言使用API函数编写Windows应用程序是十分困难的。微软的VC++6.0用类对API函数进行了封装,为编程提供了MFC类库。使用MFC类库简化了Windows应用程序的编制。可是,MFC类库的使用仍是比较复杂的,所以,VC++一直是一些专业人员的编程工具。 2.3.3 组件库 为了简化Windows应用程序的设计,提出了组件(控件)的概念,组件也是类,按钮、菜单、工具条等均可以封装为组件,组件采用属性、事件、方法来描述,其中属性描述组件的特性,如按钮的标题,标签字体的颜色和大小。方法是组件类提供的函数,经过调用这些方法,能够控制组件的行为。组件经过事件和外界联系,一个组件能够响应若干个事件,能够为事件增长事件处理函数,之后每当发生该事件,将自动调用该事件处理函数处理此事件。不少组件在设计阶段是可见的,支持可视化编程,这些组件又被叫作控件。用控件编制Windows应用程序很象搭积木,将控件放到窗体中,设置好属性,漂亮的界面就设计好了。组件编程的工具备不少,例如:VB6.0、VB.Net、C#、C++Builder、Java、Delphi等快速开发工具(RAD)。这些工具都有本身的组件库。 2.3.4 .NET框架类库 .NET系统为编制Windows应用程序、Web应用程序、Web服务,在.Net框架(.Net FrameWork)中提供了基础类库(Base Class Library)。它是一个统一的、面向对象的、层次化的、可扩展的类库,统一了微软当前各类不一样的框架和开发模式,不管开发Windows应用程序,仍是开发Web应用程序,采用相同的组件名称,组件具备相同的属性、方法和事件,开发模式也相似,方便程序员学习。.Net框架类库支持控件可视化编程,.Net中的VC++.Net、VB.Net、C#语言都使用这个类库,消除了各类语言开发模式的差异。该类库包括如下功能:基础类库(基本功能,象字符串、数组等)、网络、安全、远程化、诊断和调试、I/O、数据库、XML、Web服务、Web编程、Windows编程接口等等。 Windows9八、2000和XP操做系统并不包含.NET框架类库,为了运行C#程序,必须安装.Net FrameWork。 2.4 Windows应用程序的基本结构 Windows应用程序和控制台应用程序的基本结构基本同样,程序的执行老是从Main()方法开始,主函数Main()必须在一个类中。但Windows应用程序使用图形界面,通常有一个窗口(Form),采用事件驱动方式工做。本节介绍Windows应用程序的基本结构。 2.4.1 最简单的Windows应用程序 最简单的Windows应用程序以下: using System;//引入名字空间 using System.Windows.Forms; public class Form1:Form//类定义 { static void Main()//主函数 { Application.Run(new Form1()); } } 自定义类Form1以Form类为基类。Form类是.Net系统中定义的窗体类,Form类对象具备Windows应用程序窗口的最基本功能,有标题栏、系统菜单、最大化按钮、最小化按钮和关闭按钮、用户区。Form类对象仍是一个容器,在Form窗体中能够放置其它控件,例如菜单控件,工具条控件等等。System.Application类中的静态方法Run负责完成一个应用程序的初始化,运行,终止等功能,其参数是本程序使用的窗体Form1类对象,Run方法还负责从操做系统接受事件,并把事件送到窗体中响应。窗体关闭,方法Run退出,Windows应用程序结束。假设已经将文件保存在d:\Charp目录下,文件名为:e1.cs。启动命令行提示符,在屏幕上输入一行命令:d:回车,cd Charp回车,键入命令: C:\WINNT\Microsoft.NET\Framework\v1.0.3705\csc /t:winexe /r:system.dll,System.Windows.Forms.dll e1.cs 命令中的/t:winexe表示要创建一个Windows应用程序,/r表示要引入的命名空间。也能够用记事本创建一个批处理文件g.bat,将以上命令内容拷贝到文件中,运行g.bat,和在命令行提示符键入命令效果相同。以上方法在FrameWork SDK 2000中实现。若是一切正常e1.cs文件将被编译,编译后生成可执行文件e1.exe。运行可执行文件e1.exe,CRT上出现一个窗口如右图。 能够在Form1类中定义新的变量,因为主窗体关闭,程序也就结束了,所以定义在主窗体Form1中的变量的生命周期和程序的生命周期是相同的,从这个意义上说,这些变量是全局变量。能够为Form1类定义构造函数,在构造函数中作一些初始化的工做,例如修改Form1标题栏中的标题。还能够在Form1中定义控件类的对象,这些控件将在Form1的用户区显示出来,换句话讲,在Form1中生成控件对象,也就是把控件放到窗体中。如在窗体中增长了一个按钮(Button)控件,单击按钮,将产生单击按钮事件,完成必定功能,下例说明了如何在窗体中增长控件,如何修改控件属性,如何增长控键的事件处理函数。 using System; using System.Windows.Forms; public class Form1:Form { Button button1;//生成Button类引用变量,和应用程序有相同生命周期 public Form1()//构造函数 {//下句修改主窗体标题,不指明属性(方法)所属对象,默认为Form1类的属性(方法) Text="个人第一个程序";//也可写为:this.Text="个人第一个程序"; button1=new Button();//生成Button类对象 button1.Location=new Point(25,25);//修改button1属性location即按钮位置 button1.Text="肯定";//修改button1属性Text,即按钮的标题 //下句指定button1_Click函数是按钮单击事件的单击事件处理函数 button1.Click+=new System.EventHandler(button1_Click); this.Controls.Add(button1);//按钮增长到窗体中,将在主窗体用户区显示出来 } static void Main() { Application.Run(new Form1()); } private void button1_Click(object sender, System.EventArgs e) {//事件处理函数 this.button1.Text="单击了我";//单击按钮事件执行的语句 } } 请注意在窗体中增长控件类的对象的步骤,首先生成一个引用变量button1,和主窗体Form1有相同的生命周期,第二步在构造函数中用new生成Button类对象,第三步在构造函数中修改button1的属性,增长button1的事件函数。这些步骤对于定义任何一个控件都是相同的。编译运行结果如右图: 2.4.2 用Visual Studio.Net创建Windows应用程序框架 以上所作的工做,都是一些固定的工做,能够使用Visual Studio.Net自动创建,下面介绍使用Visual Studio.Net建立Windows应用程序的具体步骤。 (1) 运行Visual Studio.Net程序,出现如图1.2.2A界面。 (2) 单击新建项目按钮,出现如图1.2.2B对话框。在项目类型(P)编辑框中选择Visual C#项目,在模板(T)编辑框中选Windows应用程序,在名称(N)编辑框中键入e2,在位置(L)编辑框中键入D:\csarp。也能够单击浏览按钮,在打开文件对话框中选择文件夹。单击肯定按钮,建立项目。出现如图2.4.2A界面。生成一个空白窗体(Form1)。 图2.4.2A (3) 在e2文件夹中下有两个文件夹和8个文件,通常只修改Form1.cs文件。右击Form1窗体,在快捷菜单中选择菜单项查看代码(C),可打开Form1.cs文件。Visual Studio.Net生成的Foem1.cs文件以下,这是使用Visual Studio.Net建立Windows应用程序的最基本的形式。底色为黑色的字是做者增长的注解。 using System;//引入名字空间 using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace e2//定义名字空间,///为解释 { //此处可定义其它类 /// <summary> /// Form1 的摘要说明。 /// </summary> public class Form1 : System.Windows.Forms.Form//Forme1类定义 { //此处可定义本身的变量,这些变量和运行程序同生命周期 /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null; public Form1()//构造函数 { // // Windows 窗体设计器支持所必需的 // InitializeComponent();//此函数系统自动生成,不要修改,该函数作一些初始化工做 // // TODO: 在 InitializeComponent 调用后添加任何构造函数代码 //在构造函数增长本身的初始化代码,必须放在InitializeComponent()以后 } /// <summary> /// 清理全部正在使用的资源。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { //此函数系统自动生成,不要修改函数内容,函数作一些初始化工做 // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(292, 273); this.Name = "Form1";//this 是Form1窗体对象 this.Text = "Form1"; } #endregion /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main()//程序入口函数 ,通常不修改 { Application.Run(new Form1());//主程序创建窗体运行 }//程序入口函数以后可定义本身的方法、属性等 } } (4) 下边在窗体中增长一个按钮,并为按钮增长单击事件函数。单击图2.4.2A中标题为Forms.cs[设计]的窗口标签,返回标题为Forms.cs[设计]的窗口。向项目中添加控件须要使用工具箱窗口,若看不到,能够用菜单命令视图/工具箱打开这个窗口(见图2.4.2B左图)。选中工具箱窗口中Windows窗体类型下的Button条目,而后在标题为Forms.cs[设计]的窗口的Form1窗体中按下鼠标左键,拖动鼠标画出放置Button控件的位置,抬起鼠标左键,就将Button控件放到Form1窗体中。选中按钮控件,属性窗口(见图2.4.2B中图)显示按钮属性,其中左侧为属性名称,右侧为属性值,用属性窗口修改Button的Text属性值为:肯定。单击属性窗体上的第4个图标,打开事件窗口(见图2.4.2B右图),显示Button控件所能响应的全部事件,其中左侧为事件名称,右侧为事件处理函数名称,若是为空白,表示尚未事件处理函数,选中Click事件,双击右侧空白处,增长单击事件处理函数。 图2.4.2B 完成以上设计后,集成环境生成的Foem1.cs文件以下,底色为黑色的代码是新增代码。 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace e2 { /// <summary> /// Form1 的摘要说明。 /// </summary> public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Button button1;//定义Button类引用变量 /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null; public Form1() { // // Windows 窗体设计器支持所必需的 // InitializeComponent(); // // TODO: 在 InitializeComponent 调用后添加任何构造函数代码 // } /// <summary> /// 清理全部正在使用的资源。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button();//生成对象 this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(96, 56);//修改属性 this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(72, 32); this.button1.TabIndex = 0; this.button1.Text = "肯定"; this.button1.Click += new System.EventHandler(this.button1_Click);//增长事件 // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(292, 273); this.Controls.AddRange(new System.Windows.Forms.Control[] {this.button1}); this.Name = "Form1"; this.Text = "Form1"; } #endregion /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); } private void button1_Click(object sender, System.EventArgs e) {//事件处理函数 } } } 请注意按钮放到窗体后,集成环境自动增长的语句。分析这些增长的语句,可知在窗体中增长Button类对象的步骤:首先定义Button类变量button1,这是Form1类的一个字段,因为主窗体关闭,程序也就结束了,所以定义在主窗体Form1中的变量的生命周期和程序的生命周期是相同的,从这个意义上说,这样的变量是全局变量。所以变量button1和主窗体Form1有相同的生命周期。第二步在构造函数中用new生成Button类对象,第三步在构造函数中修改button1的属性,第四步增长button1的事件函数,函数button1_Click()是事件处理函数,语句this.button1.Click += new System.EventHandler(this.button1_Click)把按钮Button1的事件Click和事件处理函数button1_Click()联系到一块儿。程序员应在事件处理函数button1_Click()中增长具体的事件处理语句。这些步骤对于增长任何控件都是相同的。能够比较一下2.4.1节中的步骤,它们基本是相同的。应熟悉以上操做步骤,学会在窗体中增长控件,修改控件属性,增长事件函数。 2.4.3 方案(Solution)和项目(Project) 一个应用(Application)可能包括一个或多个可执行程序,例如,学生信息管理系统,可能包括客户端程序和服务器端程序,全部这些可执行程序的集合叫作一个应用解决方案。为了生成一个可执行程序,可能须要有一个或多个文件,例如,通常须要一个窗体文件,有时还须要一个资源文件,若干图形或图像文件。全部这些文件的集合叫一个项目,所以项目是为了建立一个可执行程序所必需的全部的文件的集合。而一个方案中可能包括多个项目。为了方便管理项目和项目所在的方案,Visual Studio.Net为开发人员提供了解决方案资源管理器窗口(图2.4.3)。它能够为咱们显示一个方案的树形结构,以及它所包含的项目及项目中的文件。 一个项目通常要放在一个文件夹中,例如上边的例子,项目e2的全部文件都在文件夹e2中,共有两个文件夹和8个文件,它们的用途以下:  bin文件夹:包含debug子文件夹,存储生成带调试信息的可执行C#程序。  obj文件夹:包含编译过程当中生成的中间代码。  AssemblyInfo.cs:建立项目自动添加。包含各类属性设置,例如,项目最终建立的可执行文件或DLL文件中的信息,如标题、描述、公司名等。通常用工具修改该程序,不要直接修改。  Form1.cs:窗体文件,程序员通常只修改该文件。  Form1.resx:资源文件。程序员用集成环境提供的工具修改,不要直接修改。  e2.suo:解决方案用户选项文件,记录用户关于解决方案的选项。  e2.csproj:项目文件,记录用户关于项目的选项。  e2.sln:解决方案文件。 为了之后从新用Visual Studio.Net打开该解决方案,必须保存除了两个文件夹之外的全部文件,实际上,因为文件夹e2不太大,能够保存整个e2文件夹。若是从新开始一个解决方案,首先用菜单项文件/关闭解决方案,关闭当前项目,再新建一个项目。为了用Visual Studio.Net修改之前的程序,必须打开保存的项目文件(扩展名为csproj),或者使用菜单项文件/打开项目,打开保存的项目,同时打开项目所在的解决方案。 图2.4.3 习题 (1) Windows应用程序和dos程序有那些不一样。 (2) 以键盘操做为例说明什么是事件驱动。 (3) 那些Windows操做系统提供了.NET框架类库,那些提供了API。 (4) 运行C#程序,应首先安装那些软件。 (5) 定义一个和应用程序同生命周期的变量,该变量应定义在何处,说明该变量的使用范围。 (6) 在窗体中增长一个控件,应如何操做,集成环境增长了那些代码。 (7) 为控件增长事件函数,应如何操做,集成环境增长了那些代码。 (8) 如何为窗体文件增长一个方法,说明该方法的使用范围。 第三章 经常使用控件和类的使用 Visual Studio.Net(简称VS.NET)使用控件(组件)设计Windows应用程序。将VS.NET工具箱窗口中的控件放到窗体中,使用属性窗口改变控件的属性,或在程序中用语句修改属性,为控件增长事件函数,完成指定的功能。 3.1 控件通用属性 大部分控件,例如Label、Button、TextBox等,都是Control类的派生类。Control类中定义了这些派生类控件通用的一组属性和方法,这些属性是:  Name:控件的名称,区别控件类不一样对象的惟一标志,例如创建一个Button控件类对象,可用以下语句,Button button1=new Button(),那么Name属性的值为button1。  Location:表示控件对象在窗体中的位置。本属性是一个结构,结构中有两个变量,x和y,分别表明控件对象左上角顶点的x和y坐标,该坐标系以窗体左上角为原点,x轴向左为正方向,y轴向下为正方向,以像素为单位。修改Location,能够移动控件的位置,例如:button1.Location=new Point(100,200)语句移动按钮button1到新位置。  Left和Top:属性值等效于控件的 Location 属性的 X 和Y。修改Left和Top,能够移动控件的位置,例如:button1.Left=100语句水平移动按钮button1。  Size:本属性是一个结构,结构中有两个变量,Width和Height分别表明控件对象的宽和高,例如可用语句button1.Size.Width=100修改Button控件对象button1的宽。  BackColor:控件背景颜色。  Enabled:布尔变量,为true表示控件能够使用,为false表示不可用,控件变为灰色。  Visible:布尔变量,为true控件正常显示,为false控件不可见。  Modifier:定义控件的访问权限,能够是private、public、protected等。默认值为private。  Cursor:鼠标移到控件上方时,鼠标显示的形状。默认值为Default,表示使用默认鼠标形状,即为箭头形状。 3.2 Form类 Form类是.Net系统中定义的窗体类(WinForm),它属于System.Windows.Forms名字空间。Form类对象具备Windows应用程序窗口的最基本功能。它能够是对话框、单文档或多文档应用程序窗口的基类。Form类对象仍是一个容器,在Form窗体中能够放置其它控件,例如菜单控件,工具条控件等等,还能够放置子窗体。 1. Form类经常使用属性  AutoScroll:布尔变量,表示窗口是否在须要时自动添加滚动条。  FormBorderStyle:窗体边界的风格,若有无边界、单线、3D、是否可调整等。  Text:字符串类对象,窗体标题栏中显示的标题。  AcceptButton:记录用户键入回车时,至关于单击窗体中的那个按钮对象。  CanceButton:记录用户键入ESC键时,至关于单击窗体中的那个按钮对象。以上两个属性多用于对话框,例如打开文件对话框,用户键入回车,至关于单击肯定按钮。  MaxiMizeBox:窗体标题栏右侧最大化按钮是否可用,设置为false,按钮不可用。  MiniMizeBox:窗体标题栏右侧最小化按钮是否可用,设置为false,按钮不可用。若是属性MaxiMizeBox和MiniMizeBox都设置为false,将只有关闭按钮。在不但愿用户改变窗体大小时,例如对话框,将二者都设置为false。 2. Form类经常使用方法  Close():窗体关闭,释放全部资源。如窗体为主窗体,执行此方法,程序结束。  Hide():隐藏窗体,但不破坏窗体,也不释放资源,可用方法Show()从新打开。  Show():显示窗体。 3. Form类经常使用事件  Load:在窗体显示以前发生,能够在其事件处理函数中作一些初始化的工做。 3.3 标签(Label)控件 标签控件用来显示一行文本信息,但文本信息不能编辑,经常使用来输出标题、显示处理结果和标记窗体上的对象。标签通常不用于触发事件。 1. Label控件经常使用属性  Text:显示的字符串  AutoSize:控件大小是否随字符串大小自动调整,默认值为false,不调整。  ForeColor:Label显示的字符串颜色。  Font:字符串所使用的字体,包括所使用的字体名,字体的大小,字体的风格等等,具体修改方法见下边的例子。 2. 例子e3_3:个人第一个程序 下面的例子在窗口中显示一行文本,该例虽然简单,但包括了用Visual Studio.Net创建C# Windows应用程序的基本步骤。具体实现步骤以下: (1) 创建一个新项目,生成一个空白窗体(Form1),见图2.4.2A。能够用属性窗口(图2.4.2B中图)修改窗体的属性,例如修改Form1的属性Text,能够修改窗体的标题。用鼠标拖动窗体的边界小正方形,能够修改窗体打开时的初始大小。 (2) 双击工具箱窗口(图2.4.2B左图)中Windows窗体类型下的Label条目,在窗体Form1放置一个Label控件。该控件用来显示一行文本。能够用鼠标拖放Label到窗体的任意位置,并可拖动Label边界改变控件的大小。 (3) 选中Label控件,在属性窗口中找到属性text,把它的值由“Label1”修改成“个人第一个程序”。接着在属性窗口中选中Font属性,单击Font属性右侧的标题为…的按钮,打开对话框,在对话框中能够修改Label控件显示字符串的字体名称和字号等,也能够单击Font属性左边的+号,在出现的子属性中编辑。编辑完成后,单击Font属性左边的-号,隐藏Font的子属性。修改ForeColor属性能够修改Label控件显示字符串的颜色。这是在设计阶段修改属性。 (4) 编译,运行,能够看到窗口中按指定字体大小和颜色显示:个人第一个程序。运行效果如右图。 (5) 保存项目。生成一个可执行程序须要多个文件,这些文件组成一个项目。通常把一个项目存到一个子目录中。单击文件/存全部文件菜单项,保存全部文件。 (6) 关掉VS.NET,再启动。用文件/打开项目菜单项打开刚才关闭的项目文件(扩展名为sln)。应能看到刚才关闭的设计界面。必须打开项目,才能完成编译工做。 3.4 按钮(Button)控件 用户单击按钮,触发单击事件,在单击事件处理函数中完成相应的工做。 1. Button 控件的经常使用属性和事件  属性Text:按钮表面的标题  事件Click:用户单击触发的事件,通常称做单击事件。 2. 例子e3_4 本例说明如何用程序修改属性,如何使用方法,增长事件函数。该例在窗口中显示一行文字,增长2个按纽,单击标题为红色的按纽把显示的文本颜色改成红色,单击标题为黑色的按纽把显示的文本颜色改成黑色。实现步骤以下: (1) 继续上例,放三个Button控件到窗体,修改属性Text,使标题分别为红色,黑色,退出。设计好的界面如右图。 (2) 选中标题为红色的按纽,打开事件窗口(见图2.4.2B右图),显示该控件所能响应的全部事件,其中左侧为事件名称,右侧为事件处理函数名称,若是为空白,表示尚未事件处理函数,选中Click事件,双击右侧空白处,增长单击(Click)标题为红色的按钮的事件处理函数以下: private void button1_Click(object sender, System.EventArgs e) { label1.ForeColor=Color.Red;//运行阶段修改属性 }//注意label1是控件的名字(label的Name属性),用它来区分不一样的控件。 (3) 单击(Click)标题为黑色的按纽的事件处理函数以下: private void button2_Click(object sender, System.EventArgs e) { label1.ForeColor=Color.Black;} (4) 单击(Click)标题为退出的按纽的事件处理函数以下: private void button3_Click(object sender, System.EventArgs e) { Close();} Close()为窗体(Form)的方法,做用是关闭注窗体。因为关闭了主窗体,程序也就结束了。注意,引用窗体的方法和属性时可不用指定对象名,换句话讲,如不指定属性或方法的对象名,默认为窗体的属性或方法。而使用其它组件的属性及方法要指明所属组件对象,例如label1.ForeColor=Color.Red; (5) 编译,运行,单击标题为红色的按纽,窗体显示字符串颜色变为红色,单击标题为黑色的按纽,窗体显示字符串颜色变为黑色,单击标题为退出的按纽,结束程序。 3.5 事件处理函数的参数 事件处理函数通常有两个参数,第一个参数(object sender)为产生该事件的对象的属性Name的值,例如上例单击标题为红色的按钮,第一个参数sender的值为button1。如上例标题为红色的按钮和标题为黑色的按钮使用同一个单击事件处理函数,其事件处理以下: private void button1_Click(object sender,System.EventArgs e) { if(sender==button1) label1.ForeColor=Color.Red; else label1.ForeColor=Color.Black; } 事件处理函数第二个参数(System.EventArgs e)表明事件的一些附加信息,事件不一样,所表明的信息也不相同,例如在后边的例子中能够看到,按下鼠标的事件处理函数中,e.X和e.Y分别为发生事件时鼠标位置的x坐标和y坐标,e.Button表示用户单击了鼠标那个键,如为MouseButtons.Left,表示单击了鼠标左键。 为了使这两个按钮使用相同的单击事件处理函数,首先为标题为红色的按钮增长单击事件处理函数,便是上边的代码,事件函数名称为:button1_Click。选中标题为黑色的按钮,打开事件窗体(见图2.4.2B右图),选中Click事件,从其右侧下拉列表中选择事件处理函数为button1_Click,这样两个按钮就使用相同的单击事件处理函数了。 3.6 文本框(TextBox)控件 TextBox控件是用户输入文本的区域,也叫文本框。 1. TextBox控件属性和事件  属性Text:用户在文本框中键入的字符串  属性MaxLength:单行文本框最大输入字符数。  属性ReadOnly:布尔变量,为true,文本框不能编辑。  属性PasswordChar:字符串类型,容许输入一个字符,如输入一个字符,用户在文本框中输入的全部字符都显示这个字符。通常用来输入密码。  属性MultiLine:布尔变量,为true,多行文本框,为false,单行文本框。  属性ScrollBars:MultiLine=true时有效,有4种选择:=0,无滚动条,=1,有水平滚动条,=2,有垂直滚动条,=3,有水平和垂直滚动条。  属性SelLength:可选中文本框中的部分或所有字符,本属性为所选择的文本的字符数。  属性SelStart:所选中文本的开始位置。  属性SelText:所选中的文本  属性AcceptsReturn:MultiLine=true时有效,布尔变量,为true,键入回车,换行,为false,键入回车键,至关于单击窗体中的默认按钮。  事件TextChanged:文本框中的字符发生变化时,发出的事件。 2. 例子e3_6 本例要求用户在编辑框中输入两个乘数,单击按钮把相乘的结果在编辑框中显示出来。 (1) 创建一个新的项目。放四个Label控件到窗体,Text属性分别为:被乘数,乘数,积,*,=。 (2) 放三个textBox控件到窗体,属性Name从左到右分别为:textBox一、textBox二、textBox3,属性Text都为空。 (3) 放三个Button控件到窗体,Text属性分别修改成求积,清空,退出。设计的界面如上图。 (4) 标题为求积的按钮的单击事件处理函数以下: private void button1_Click(object sender, System.EventArgs e) { float ss,ee; ss=Convert.ToSingle(textBox1.Text); ee=Convert.ToSingle(textBox2.Text); textBox3.Text=Convert.ToString(ss*ee); } (5) 标题为清空的按钮的单击事件处理函数以下: private void button2_Click(object sender,System.EventArgs e) { textBox1.Text=""; textBox2.Text=""; textBox3.Text=""; } (6) 标题为退出的按钮的单击事件处理函数以下: private void button3_Click(object sender, System.EventArgs e) { Close();} (7) 编译,运行,在文本框textBox1,textBox2分别输入2和3,单击标题为求积的按纽,textBox3中显示6,单击标题为清空的按钮,三个文本框被清空,单击标题为退出的按纽,结束程序。 3.7 Convert类 Convert类中提供了一些静态方法,用来把一种类型数据转换为另外一种类型数据。例如,Convert.ToSingle(textBox1.Text)把字符串textBox1.Text转换为单浮点数。Convert.ToString(3.14)把单浮点数3.14转换为字符串。其它转换函数还有:ToInt、ToInt16等等。 3.8 单选按钮(RadioButton)和GroupBox控件 RadioButton是单选按钮控件,多个RadioButton控件能够为一组,这一组内的RadioButton控件只能有一个被选中。GroupBox控件是一个容器类控件,在其内部可放其它控件,表示其内部的全部控件为一组,其属性Text可用来表示此组控件的标题。例如把RadioButton控件放到GroupBox控件中,表示这些RadioButton控件是一组。有一些特性是互斥的,例如性别,选择这类特性可用RadioButton和GroupBox控件。 1. GroupBox控件经常使用属性 GroupBox控件经常使用属性只有一个,属性Text,指定GroupBox控件顶部的标题。 2. RadioButton控件属性和事件  属性Text:单选按钮控件旁边的标题。  属性Checked:布尔变量,为true表示按钮被选中,为false表示不被选中。  事件CheckedChanged:单选按钮选中或不被选中状态改变时产生的事件。  事件Click:单击单选按钮控件时产生的事件。 3. 例子e3_8 该例用RadioButton控件修改Label控件字符串的字体为:宋体、黑体、楷体。具体实现步骤以下: (1) 创建一个新的项目。 (2) 放Label控件到窗体,属性Text=“不一样的字体”。字体为宋体。 (3) 放GroupBox控件到窗体,其属性Text=“选择字体”。 (4) 放三个RadioButton控件到GroupBox中,其属性Text分别为:宋体、黑体、楷体。宋体RadioButton控件的属性Checked=true。设计好的界面如右图。 (5) 为三个RadioButton控件的CheckedChanged事件增长事件处理函数以下: private void radioButton1_CheckedChanged(object sender, System.EventArgs e) { if(radioButton1.Checked) label1.Font=new Font("宋体",label1.Font.Size); }//label1显示的字体变为宋体,字体大小不变 private void radioButton2_CheckedChanged(object sender, System.EventArgs e) { if(radioButton2.Checked) label1.Font=new Font("黑体",label1.Font.Size); } private void radioButton3_CheckedChanged(object sender, System.EventArgs e) { if(radioButton3.Checked) label1.Font=new Font("楷体_GB2312",label1.Font.Size); } (6) 编译,运行,单击RadioGroup1中的三个RadioButton按钮,能够改变字体。注意三个按钮只能选一个,既只能选一种字体。考虑一下,是否可用Click事件。 3.9 Font类 Font类有两个构造函数:第一个是new Font(字体名称,字号),例如,label1.Font=new Font("黑体",9),用法还可参考例e3_8。第二个是new Font(字体名称,字号,字体风格),其中第三个参数是枚举类型,具体定义以下: enum FontStyle{ Regular =0,//正常字体 Bold =1,//黑体 Italic =2,//斜体 BoldItalic =3,//黑斜体 Underline =4,//下划线,5=黑体下划线,6=斜体下划线,7=黑斜体下划线 Strikeout =8}//删除线,9=黑体删除线,10=斜体删除线,依此类推。 例如修改标签控件字体为斜体: label1.Font=new Font("黑体",9,label1.Font.Style|FontStyle.Italic); 或者:label1.Font=new Font("黑体",9,label1.Font.Style|(FontStyle)2); 修改标签控件字体不为斜体: label1.Font=new Font("黑体",9,label1.Font.Style&~FontStyle.Italic); 或者:label1.Font=new Font("黑体",9,label1.Font.Style&(FontStyle)(~2)); 用法还可参考例e3_11。 3.10 多选框(CheckBox)控件 CheckBox是多选框控件,可将多个CheckBox控件放到GroupBox控件内造成一组,这一组内的CheckBox控件能够多选,不选或都选。可用来选择一些可共存的特性,例如一我的的爱好。 1. CheckBox控件属性和事件  属性Text:多选框控件旁边的标题。  属性Checked:布尔变量,为true表示多选框被选中,为false不被选中。  事件Click:单击多选框控件时产生的事件。  事件CheckedChanged:多选框选中或不被选中状态改变时产生的事件。 2. 例子e3_10A 在窗口中增长2个CheckBox控件,分别用来选择是否爱好音乐和是否爱好文学,用鼠标单击CheckBox控件,改变爱好选择,用Label控件显示所选择的爱好。实现步骤以下: (1) 创建新项目。放Label控件到窗体,属性Text=“你的爱好是:”。 (2) 放GroupBox控件到窗体,属性Text=“爱好”。放两个CheckBox控件到GroupBox中,属性Text分别为:音乐、文学。设计界面以下图。 (3) 标题为音乐的多选框控件的CheckedChanged事件处理函数以下: private void checkBox1_CheckedChanged(object sender, System.EventArgs e) { String text1="你的爱好是:"; if(checkBox1.Checked) text1=text1+checkBox1.Text; if(checkBox2.Checked) text1+=checkBox2.Text; label1.Text=text1; } (4) 将标题为文学的多选框控件的CheckedChanged事件处理函数,设置为标题为音乐的多选框控件的CheckedChanged事件处理函数,具体步骤见3.5节。 (5) 编译,运行。选中音乐将在标签控件中显示:你的爱好是:音乐,再选中文学显示:你的爱好是:音乐文学,…。 3. 例子e3_10B 该例同上例,但按选中音乐和文学的顺序在标签中显示爱好,实现步骤以下: (1) 创建一个新项目。为Form1类增长私有变量String s="你的爱好是:"。 (2) 放Label控件、GroupBox控件、两个CheckBox到窗体,属性设置同上例。 (4)标题为音乐的多选框控件CheckBox1的CheckedChanged事件处理函数以下: private void checkBox1_CheckedChanged(object sender,System.EventArgs e) { int n=s.IndexOf("音乐");//s中有字符串"音乐"吗?n=-1表示没有 if(n==-1)//n=-1,表示上次没选此项,这次选中,应增长"音乐" s+="音乐"; else//不然,表示上次已选此项,这次不选中,应删除"音乐" s=s.Remove(n,2); label1.Text=s; } (5)标题为文学的多选框控件CheckBox2的CheckedChanged事件处理函数以下: private void checkBox2_CheckedChanged(object sender,System.EventArgs e) { int n=s.IndexOf("文学");//s中有字符串"文学"吗?=-1表示没有 if(n==-1)//=-1,表示上次没选此项,这次选中,应增长"文学" s+="文学"; else//不然,表示上次已选此项,这次不选中,应删除"文学" s=s.Remove(n,2); label1.Text=s; } (6)编译,运行。选中音乐在标签中显示:你的爱好是:音乐,再选中文学显示:你的爱好是:音乐文学,不选音乐显示:你的爱好是:文学,再选音乐显示:你的爱好是:文学音乐。 3.11 列表选择控件(ListBox) 列表选择控件列出全部供用户选择的选项,用户可从选项中选择一个或多个选项。 1. 列表选择控件的经常使用属性、事件和方法  属性Items:存储ListBox中的列表内容,是ArrayList类对象,元素是字符串。  属性SelectedIndex:所选择的条目的索引号,第一个条目索引号为0。如容许多选,该属性返回任意一个选择的条目的索引号。如一个也没选,该值为-1。  属性SelectedIndices:返回全部被选条目的索引号集合,是一个数组类对象。  属性SelectedItem:返回所选择的条目的内容,即列表中选中的字符串。如容许多选,该属性返回选择的索引号最小的条目。如一个也没选,该值为空。  属性SelectedItems:返回全部被选条目的内容,是一个字符串数组。  属性SelectionMode:肯定可选的条目数,以及选择多个条目的方法。属性值能够使:none(能够不选或选一个)、one(必须并且必选一个)、MultiSimple(多选)或MultiExtended(用组合键多选)。  属性Sorted:表示条目是否以字母顺序排序,默认值为false,不容许。  方法GetSelected():参数是索引号,如该索引号被选中,返回值为true。  事件SelectedIndexChanged:当索引号(即选项)被改变时发生的事件。 2. 例子e3_11 根据列表框的选择,为字符串加下划线、删除线、变斜体、变粗体。具体步骤以下: (1) 创建一个新项目。放Label控件到窗体,其属性Text=“字体风格”。 (2) 放置ListBox控件到窗体中,属性Name=listBox1。选中ListBox控件,在属性窗口中,单击Items属性右侧的三个小点,打开字符串集合编辑器对话框,在其中输入四项:粗体、斜体、下划线、删除线,注意每一项要换行。如上图。 (3) 设置列表选择控件ListBox1属性SelectionMode为MultiExtended,容许多选。 (4) 为列表选择控件的事件SelectedIndexChenged增长事件处理函数以下: private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) { int Style=0,k=1;//Style=0正常字体,1=黑体,2=斜体,3=黑斜体等,参见3.9节 for(int i=0;i<listBox1.Items.Count;i++)//此例Count=4,为何? {if(listBox1.GetSelected(i))//例如此例GetSelected(0)=true表示粗体被选中 Style=Style|k;//增长指定风格 else Style=Style&(~k);//取消指定风格 k=k*2; } FontStyle m=new FontStyle(); m=(FontStyle)Style; label1.Font=new Font(label1.Font.Name,9,m);} (5) 编译,运行,单选或用Ctrl键多选,看一下效果。运行效果如上图。 3.12 下拉列表组合框(ComboBox)控件 控件ComboBox中有一个文本框,能够在文本框输入字符,其右侧有一个向下的箭头,单击此箭头能够打开一个列表框,能够从列表框选择但愿输入的内容。现介绍该控件用法。 1. ComboBox控件的经常使用属性、事件和方法  属性DropDownStyle:肯定下拉列表组合框类型。为Simple表示文本框可编辑,列表部分永远可见。为DropDown是默认值,表示文本框可编辑,必须单击箭头才能看到列表部分。为DropDownList表示文本框不可编辑,必须单击箭头才能看到列表部分。  属性Items:存储ComboBox中的列表内容,是ArrayList类对象,元素是字符串。  属性MaxDropDownItems:下拉列表能显示的最大条目数(1—100),若是实际条目数大于此数,将出现滚动条。  属性Sorted:表示下拉列表框中条目是否以字母顺序排序,默认值为false,不容许。  属性SelectedItem:所选择条目的内容,即下拉列表中选中的字符串。如一个也没选,该值为空。其实,属性Text也是所选择的条目的内容。  属性SelectedIndex:编辑框所选列表条目的索引号,列表条目索引号从0开始。若是编辑框未从列表中选择条目,该值为-1。  事件SelectedIndexChanged:被选索引号改变时发生的事件。 2. 例子e3_12 选择Windows操做系统提供的全部字体 增长一个ComboBox控件,用来选择字符串使用的字体名。本例提供方法使控件ComboBox的下拉列表中显示Windows操做系统中使用的全部字体名。运行效果如右图。实现步骤以下: (1) 创建新项目。放Label控件到窗体,其属性Text=“选择不一样字体”。 (2) 放ComboBox控件到窗体中,属性Name=comboBox1,属性DropDownStyle=DropDownList,不能在编辑框中输入字体名,只能从下拉列表中选取。 (3) 为窗体Form1的事件Load增长事件处理函数以下: private void Form1_Load(object sender, System.EventArgs e) {//Families是类FontFamily的一个静态属性,获得操做系统中所使用的全部字体名 FontFamily[] families=FontFamily.Families;//静态属性没有类的对象也可以使用 foreach (FontFamily family in families) comboBox1.Items.Add(family.Name);//注意Add方法的使用 } (4) 为comboBox1的事件SelectedIndexChenged增长事件处理函数以下: private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) {label1.Font=new Font(comboBox1.Text,9);} (5) 编译,运行,在下拉列表中选择不一样字体名,标签的字体变为选择的字体。从下拉列表中能够看到操做系统中的全部字体名称已经在列表中。 3.13 ToolTip控件 在一些Windows应用程序中,例如Word程序,当鼠标在工具条的按钮上停留一段时间后,会在旁边出现提示,ToolTip控件就是为实现此功能的。能够用ToolTip控件为任何控件增长提示,本节介绍该控件的使用方法。 例子e3_13 为Button控件增长提示 (1) 创建一个新项目。放Button控件到窗体,Name属性为Button1。 (2) 把toolTip控件放到窗体中,属性Name=ToolTip1。 (3) 在Form1的构造函数中,增长语句以下: toolTip1.SetToolTip(button1,"这是一个按钮"); (4) 编译,运行,当鼠标在Button上停留一段时间后,会在旁边出现提示:这是一个按钮。 3.14 超级连接(LinkLable)控件 控件LinkLable是控件Label的派生类,和控件Label不一样的是显示的字符有下划线,能够为LinkLable控件的LinkClicked事件增长事件处理函数,当鼠标指向LinkLable控件,鼠标形状变为手形,单击该控件,调用这个事件处理函数,能够打开文件或网页。 1. 超级连接控件的属性、方法和事件  属性LinkColor:用户未访问过的连接的字符颜色,默认为蓝色。  属性VisitedLinkColor:用户访问连接后的字符颜色。  属性LinkVisited:若是已经访问过该连接,则为true;不然为false。  属性LinkArea: 是一个结构,变量LinkArea.Start表示字符串中开始加下划线的字符位置,LinkArea.Length表示字符串中加下划线字符的个数。  事件LinkClicked:单击控件LinkLable事件。 2. 例子e3_14:用LinkLabel控件超级连接到微软网站。 (1) 创建一个新工程。放LinkLabel控件到窗体,属性Text=“介绍微软的操做系统”。 (2) 修改LinkLabel控件属性LinkArea.Length=2,LinkArea.Start=2。也可在构造函数用语句修改:linkLabel1.LinkArea=new LinkArea(2,2); (3) 为LinkLabel控件的事件LinkClicked增长事件处理函数: private void linkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) { linkLabel1.LinkVisited=true; System.Diagnostics.Process.Start("http://www.micosoft.com.cn"); } (4) 运行,效果如右图,注意只有字符微软带下划线。单击微软,打开浏览器访问微软主页。 (5) 若是要打开一个窗口,列出C盘根目录下的文件及文件夹,LinkLabel控件事件LinkClicked事件处理函数修改以下: linkLabel1.LinkVisited=true; System.Diagnostics.Process.Start("C:/"); (6) 若是要打开指定程序,例如打开记事本程序,修改LinkClicked事件处理函数以下: linkLabel1.LinkVisited=true; System.Diagnostics.Process.Start("notepad"); 3.15 定时(Timer)控件 定时控件(Timer)也叫定时器或计时器控件,是按必定时间间隔周期性地自动触发事件的控件。在程序运行时,定时控件是不可见的。 3. 定时控件的属性、方法和事件  属性Interval:周期性地自动触发事件的时间间隔,单位为毫秒。  属性Enabled:为true,启动定时器。调用方法Start()也可启动定时器。  方法Start()和Stop():启动和中止定时器。设置属性Enabled=false也可中止定时器。  事件Tick:每间隔属性Interval指定的时间,产生事件Tick。 4. 例子e3_15用标签控件显示当前日期和时间 (1) 创建一个新项目。放Timer组件到窗体,Name属性为timer1。 (2) 放Label控件到窗体,Name属性为label1。 (3) 为窗体Form1的事件Load增长事件处理函数以下: private void Form1_Load(object sender, System.EventArgs e) { this.timer1.Interval=100; this.timer1.Enabled=true; label1.Text=DateTime.Now.ToString(); } (4) 为Timer1的Tick事件增长事件处理函数以下: private void timer1_Tick(object sender, System.EventArgs e) { label1.Text=DateTime.Now.ToString(); } (5) 编译,运行,标签控件位置显示日期和时间。运行效果如上图。 3.16 DateTime类 DateTime类中提供了一些静态方法,能够用来获得日期、星期和时间,下面是一些经常使用的方法。  获得日期和时间,并转换为字符串。 String s=DateTime.Now.ToString();//或DateTime.Today.ToString()  获得年、月和日期 int y=DateTime.Now.Year;//获得年 int m=DateTime.Now.Month;//获得月 int d=DateTime.Now.Day;//获得日期 String s=DateTime.Now.DayOfWeek.ToString();//英文表示的星期  获得小时、分和秒 int h=DateTime.Now.Hour;//获得小时 int m=DateTime.Now.Minute;//获得分 int s=DateTime.Now.Second;//获得秒  定义一个DateTime类对象,表示1999年1月13日3时57分32.11秒 System.DateTime moment=new System.DateTime(1999,1,13,3,57,32,11);  加法和减法(减法请读者本身完成) System.DateTime dTime=new System.DateTime(1980,8,5);//1980年8月5日 //时间间隔,17天4小时2分1秒 System.TimeSpan tSpan=new System.TimeSpan(17,4,2,1); System.DateTime result=dTime+tSpan;//结果是:1980年8月22日4:2:1 AM. 3.17 菜单 Windows应用程序通常都有一个菜单,经过选择菜单中的不一样菜单项,完成指定的功能。使用主菜单控件MainMenu能够很容易创建windows应用程序的主菜单。 1. 菜单的组成及功能 放主菜单控件MainMenu到窗体中,能够为窗体增长一个主菜单。主菜单通常包括若干顶级菜单项,例如,文件、编辑、帮助等。单击顶级菜单项,能够出现弹出菜单,弹出菜单中包含若干菜单项,例如单击文件顶级菜单项,其弹出菜单通常包括打开文件、存文件、另存为等菜单项,用鼠标单击菜单项,能够执行菜单项命令。有的菜单项还包括子菜单。 全部菜单项均可以有快捷键,即菜单项中带有下划线的英文字符,当按住ALT键后,再按顶级菜单项的快捷键字符,能够打开该顶级菜单项的弹出菜单。弹出菜单出现后,按菜单项的快捷键字符,能够执行菜单项命令。增长快捷键的方法是在菜单项的标题中,在要设定快捷键英文字符的前边增长一个字符&,例如,菜单项的标题为:打开文件(&0),菜单项的显示效果为:打开文件(0)。菜单项能够有加速键,通常在菜单项标题的后面显示,例如,菜单项打开文件的加速键通常是Ctrl+O,不打开菜单,按住Ctrl键后,再按O键,也能够执行打开文件命令。设定加速键的方法是修改菜单项的ShortCut属性。 2. 用程序生成菜单 放主菜单控件MainMenu到窗体中,能够为该窗体增长一个主菜单,Visual Studio.Net自动添加以下语句: MainMenu mainMenu1=new MainMenu(); This.Menu=mainMenu1;//指定主窗口的主菜单是mainMenu1。 能够创建多个MainMenu类对象,用第二条语句修改使主窗口使用不一样的主菜单。有了主菜单对象,用以下语句为主菜单增长顶级菜单项: MenuItem myFile=mainMenu1.MenuItem.Add(“文件(&F)”);//顶级菜单项:文件 有了顶级菜单项对象,用以下语句为顶级菜单项的弹出菜单增长菜单项: myFile.MenuItem.Add(“打开(&O)”);//文件顶级菜单项的弹出菜单的菜单项:打开 实际上,这些均可以用Visual Studio.Net自动生成。 3. 菜单项的属性和事件  属性Checked:布尔变量,=true,表示菜单项被选中,其后有标记:∨。  属性ShortCut:指定的加速键,能够从下拉列表中选择。  属性ShowShortCut:布尔变量,true(默认值),表示显示加速键,false,不显示。  属性Text:菜单项标题。如为字符-,为分隔线。如指定字符前加&,例如:颜色(&c),增长快捷键,即用Alt+c访问颜色菜单。  经常使用事件Click:单击菜单项事件。 4. 例子e3_17 增长菜单 本例在窗体中创建主菜单,主菜单包括一个顶级菜单项:颜色,其弹出菜单包括两个菜单项:红色、黑色,单击标题为红色的菜单项,把窗体中显示的字符串变为红色,单击标题为黑色的菜单项,把窗体中显示的字符串变为黑色。实现步骤以下: (1) 创建一个新项目。放Label控件到窗体。 (2) 双击工具箱中Mainmenu控件,在窗体中增长主菜单。右下角有一主菜单图标,在左上角有一方框,其中有文字:请在此处输入,在此方框中输入菜单标题。 (3) 在方框内输入字符”颜色”,在其下部方框内输入字符”红色”为一菜单项,在”红色”下输入字符”黑色”为另外一菜单项,再输入”退出”菜单项。如但愿在选中某一菜单项后出现下一级子菜单,可在菜单项右侧方框中输入子菜单项名。若是菜单项属性Text的值为-,则菜单项为分隔符。能够用鼠标拖动菜单项移动菜单项的位置。集成环境设计界面以下图。 (4) 标题为红色的菜单项的单击(Click)事件处理函数以下: private void menuItem2_Click(object sender,System.EventArgs e) {label1.ForeColor=Color.Red;}//改变字体颜色为红色 (5) 标题为黑色的菜单项的单击(Click)事件处理函数以下: private void menuItem3_Click(object sender, System.EventArgs e) {label1.ForeColor=Color.Black;}//改变字体颜色为黑色 (6) 标题为退出的菜单项的单击(Click)事件处理函数以下: private void menuItem4_Click(object sender, System.EventArgs e) { Close();}//退出程序 (7) 编译,运行,单击红色和黑色菜单项,能改变字符串的颜色。效果如上图。 3.18 工具条 通常Windows应用程序都有一个工具条,能够认为工具条上的按钮为菜单的某一菜单项的快捷按钮,单击工具条按钮至关于单击相应菜单项,完成一样的功能。 1. 工具条的组成及功能 放工具条控件ToolBar到窗体中,能够为该窗体增长一个工具条。在工具条中能够增长Button按钮和其它控件,例如象Word程序的工具条中用下拉列表控件(ComboBox)选择字号、字体等。通常工具条按钮上都有一个图标,提示用户该按钮的使用功能。按钮的全部图标存放到ImageList类对象中。单击任何一个按钮,都产生工具条控件的ButtonClick事件,在这个事件处理事件函数中,要用语句区分用户单击了那一个按钮,以完成相应的功能。 2. 控件ToolBar的属性、事件和方法  属性BorderStyle:边界风格,=None(默认值),无边界;=FixedSingle,单线边界;=Fixed3D,立体风格边界。  属性Button:集合属性,存储ToolBar的按钮对象。单击其后的按钮,能够打开ToolBarButton集合编辑器对话框(见下图),增长或删除按钮,修改按钮属性。  属性ImageList:指定一个ImageList类对象,该对象中能够存储若干图标,这些图标做为ToolBar控件按钮的图标。  属性Wrappable:布尔变量,=true(默认值),当窗体Form水平尺寸小于工具条的水平尺寸时,一行不能显示全部按钮,容许下一行显示;=false,不容许。  事件ButtonClick:ToolBar控件的单击事件。在ButtonClick事件处理事件函数中,要用语句区分用户单击了那一个按钮,以完成相应的功能。  属性ShowToolTips:布尔变量,=true,容许显示提示信息。  方法IndexOF():参数为ToolBar控件中按钮的属性Name,返回其索引值。 3. ToolBar控件中ToolBarButton按钮的属性 ToolBar控件中ToolBarButton按钮能够看做独立的控件,它有本身独立的属性。下面介绍ToolBar控件中ToolBarButton按钮的属性。  属性ImageIndex:ToolBar控件属性ImageList指定一个ImageList类对象,该对象中的图标做为ToolBar控件按钮的图标。这个属性指定本按钮使用ImageList类对象中存储的第几个图标。  属性Style:有4个值,=PushButton,为普通按钮;=Separator,为一分割符,再左边和右边的两个按钮中间增长一个间隙;=ToggleButton,开关按钮,单击该按钮,按钮被按下,不抬起,再单击,抬起。=DropDownButton,下拉按钮,按钮右侧有一个下拉箭头,单击下拉箭头,能够弹出下拉列表。  属性Text:ToolBar控件中按钮除了有图标外,还能够有属性Text指定的文字。  属性ToolTipText:当鼠标在工具条按钮上停留一段时间后,将在工具条按钮旁边出现此属性指定的提示。 4. 例子e3_18 现为上例的菜单增长工具条,有两个按钮,单击按钮分别使字体变红、变黑。步骤以下: (1) 继续菜单的例子,放ImageList控件到窗体。 (2) 放ToolBar控件到窗体。修改属性ImageList=ImageList1。 (3) 单击ImageList属性Images后按钮,打开Image集合编辑器,单击添加按钮,打开选择文件对话框。按指定路径选择图标的文件后,单击肯定按钮,增长图标到ImageList对象中。在C:\Program Files\Microsoft Office\Office\forms\2052文件夹和C:\program files\Microsoft Visual Studio.Net\Common7\Graphics\Icon\Misc文件夹中有若干图标。也可用画笔程序本身设计图标,图标的宽和高应比工具条按钮的宽和高略小,存为.ico文件。也能够用抓图软件抓其它程序的图标。任选以上方法,为ImageList对象增长两个图标。 (4) 单击ToolBar控件属性Buttons后按钮,打开ToolBarButton集合编辑器(见上图),单击添加按钮,增长一个按钮,从其属性ImageIndex后的下拉列表中选择按钮使用的图标,设置按钮的ToolTipText属性为:改变字体为红色,为工具按钮增长提示。一样方法增长第二个按钮,按钮的ToolTipText属性为:改变字体为黑色。 (5) 设定ToolBar控件属性ShowToolTips为true。 (6) 为ToolBar控件的ButtonClick事件增长事件函数以下: private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { int n=toolBar1.Buttons.IndexOf(e.Button);//n为工具条中被单击按钮的序号 switch(n) { case 0://第一个按钮,调用相应的菜单项的事件处理函数。 this.menuItem3_Click(sender,e); break; case 1://第二个按钮 this.menuItem2_Click(sender,e); break; } } (7) 编译,运行,单击两个工具条按钮,能够分别使字体变为红色或黑色。见上图。 3.19 状态栏(StatusBar)控件 Windows应用程序的状态栏通常用来显示一些信息,如时间,鼠标位置等。 1. 状态栏控件的属性  属性Panels:集合属性,存储状态栏中的各个分栏对象。单击其后标题为…的按钮,能够打开StatusBarPanels集合编辑器对话框,增长或删除分栏,修改分栏属性。  属性ShowPanel:布尔变量,=true,容许显示多栏;=false,不容许。 2. 状态栏(StatusBar)控件分栏的属性 状态条能够为单栏,也能够为多栏。属性Text,表示在状态栏中显示的内容。如为单栏,在单栏中显示字符串的语句是:statusBar1.Text=”在单栏中显示的文本”,如为多栏,在第2栏中显示字符串的语句是:statusBar1.Panels[1].Text=”在第2栏中显示的文本”。  属性Alignment:对齐方式,能够为左对齐、右对齐和中间对齐。  属性Text:表示在状态栏中显示的内容。  属性Width:栏的宽度。  属性BorderStyle:指定状态栏控件上 每一个分栏的边框外观。边界风格,=None(默认值),不显示边框;=Raised,三维凸起边框;=Sunken,三维凹陷边框显示。 3. 例子e3_19 为窗体增长状态条,在状态条内显示时间和鼠标位置。 (1) 创建新项目。放StatusBar控件到窗体。单击StatusBar控件属性Panels后按钮,打开StatusBarPanels集合编辑器(以下图),单击添加按钮,增长若2栏。其序号为0、1。 (2) 修改StatusBar控件属性ShowPanel=true。 (3) 放Timer组件到窗体,Name=Timer1,属性Interval=1000,Enabled=true。 (4) 为Timer1的Tick事件增长事件处理函数以下: private void timer1_Tick(object sender, System.EventArgs e) { statusBar1.Panels[0].Text=DateTime.Now.ToString(); } (5) 为Form1的MouseMove事件增长事件处理函数以下: private void Form1_MouseMove(object sender,System.Windows.Forms.MouseEventArgs e) {statusBar1.Panels[1].Text="X:"+e.X.ToString()+",Y:"+e.Y.ToString(); } (6) 编译,运行,如右图,在第1栏中能够看到当前时间,在窗口中移动鼠标,在第2栏中能够看到鼠标的位置不断变化。 3.20 鼠标事件 从类System.Windows.Forms.Control派生的控件都有鼠标事件,控件的Click事件本质上也是鼠标事件。一些控件还有单独的鼠标事件,例如Form。鼠标事件有:  MouseDown:若是鼠标位于控件区域,按下鼠标按键时产生该事件。  MouseUp:若是鼠标位于控件区域,抬起鼠标按键时产生该事件。  MouseMove:若是鼠标在控件区域移动,产生该事件。  MouseEnter:鼠标进入控件区域,产生该事件。  MouseLeave:鼠标离开控件区域,产生该事件。 鼠标事件处理函数通常有两个参数,第一个参数(object sender)是产生该事件的对象的属性Name的值,例如,为Form1的MouseDown事件增长事件函数,单击Form1,第一个参数sender表明Form1对象。(System.Windows.Forms.MouseEventArgs e)是事件处理函数第二个参数,表明事件的一些信息,事件不一样,所表明的信息也不相同,鼠标按下事件处理函数中,e.X为发生事件时鼠标位置的x坐标,e.Y为发生事件时鼠标位置的y坐标,e.Button为MouseButtons.Left,表示单击了鼠标左键等等,Right和Middle则分别表明右键和中间键。e.Clicks为鼠标单击的次数,若是大于2次,则为双击。 例子e3_20:在窗体中的指定区域,双击鼠标左键,用Label控件显示双击鼠标的位置。指定区域的左上角坐标为(20,20),宽为200,高为200。 (1) 创建一个新项目。放Label控件到窗体。属性Name=label1。 (2) Panel控件能够将窗体分为多个区域。放Panel控件到窗体,属性Location.X=20,Location.Y=20,属性Width=200,Height=200,属性Name=p1。 (3) 为Panel的MouseDown事件增长事件函数以下: private void p1_MouseDown(object sender,System.Windows.Forms.MouseEventArgs e) { if(e.Button==MouseButtons.Left&&e.Clicks>1)//若是是双击左键 label1.Text="X:"+e.X.ToString()+",Y:"+e.Y.ToString(); } (4) 编译,运行,分别在指定区域和区域外双击鼠标左键,看一下效果。分别在指定区域和区域外双击鼠标右键,看一下效果。 3.21 快捷菜单(ContextMenu) 使用过Word程序的人都知道,在其程序窗口的不一样位置单击右键,会出现不一样弹出菜单,这个弹出菜单叫快捷菜单,这节介绍如何在应用程序中增长快捷菜单。快捷菜单和主菜单的属性、事件和方法基本一致,只是快捷菜单没有顶级菜单项,所以这里就很少介绍了。 例子e3.21 例子在窗口中显示一行字符串,加入两个按纽,单击按纽button1把字符串变为红色,单击按纽button2把字符串变为黑色。为两个按钮创建快捷菜单,快捷菜单中有2个菜单项,单击菜单项把字符串变为红色或黑色。为窗体创建快捷菜单,菜单中仅有1个退出菜单项,单击退出菜单项,退出程序。具体实现步骤以下: (1) 创建一个新项目。放Label控件到窗体。 (2) 放2个Button控件到窗体,标题(属性Text)分别为红色,黑色。 (3) 标题为红色的按钮的单击事件处理函数以下: private void button1_Click(object sender, System.EventArgs e) { label1.ForeColor=Color.Red;} (4) 标题为黑色的按钮的单击事件处理函数以下: private void button2_Click(object sender, System.EventArgs e) { label1.ForeColor=Color.Black;} (5) 放2个ContextMenu控件到窗体,属性Name分别为contextMenu1,contextMenu2。 (6) 选中contextMenu1控件,在菜单编辑器中增长两个标题分别为红色和黑色的菜单项,它们的单击事件处理函数分别是单击红色按钮和单击黑色按钮的事件处理函数。 (7) 选中contextMenu2控件,在菜单编辑器中增长标题为退出的菜单项,并为其增长单击事件处理函数,为事件处理函数增长语句:Close(); (8) 将红色按钮和黑色按钮的属性ContextMenu指定为contextMenu1。Form的属性ContextMenu指定为contextMenu2。 (9) 编译,运行,右击标题为红色的按钮,快捷菜单contextMenu1打开,单击快捷菜单中标题为红色的菜单项,将使窗体显示的字符串颜色变为红色,右击标题为黑色的按钮,快捷菜单contextMenu1打开,单击快捷菜单中标题为黑色的菜单项,将使窗体显示的字符串颜色变为黑色,右击窗体,快捷菜单contextMenu2打开,单击快捷菜单中标题为退出的菜单项,将退出应用程序。运行效果如上图。 3.22 综合例子:计算器 具体步骤以下: (1) 创建一个新项目。Form属性MaxiMizeBox=false,属性MiniMizeBox=false。属性FormBorderStyle=FixedDialog,窗口不能修改大小。 (2) 放textBox控件到窗体,属性Name=textBox1,属性Text="0",属性ReadOnly=true。 (3) 增长10个Button控件,前9个按钮属性Name分别为:Button1-Button9,最后一个为Button0,属性Text分别为:一、二、三、四、五、六、七、八、九、0。 (4) 增长7个Button控件,属性Name分别为:btn_dot、btn_equ、btn_add、btn_sub、btn_mul、btn_div、btn_C,属性Text分别为:.、=、+、-、*、/、C。设计界面以下图。 (5) 控件Button0单击事件处理函数以下: private void button0_Click(object sender, System.EventArgs e) { if(sender==button0) append_num(0); if(sender==button1) append_num(1); if(sender==button2) append_num(2); if(sender==button3) append_num(3); if(sender==button4) append_num(4); if(sender==button5) append_num(5); if(sender==button6) append_num(6); if(sender==button7) append_num(7); if(sender==button8) append_num(8); if(sender==button9) append_num(9); } (6) 为Form1类增长方法以下: public void append_num(int i) { if(textBox1.Text!="0") textBox1.Text+=Convert.ToString(i); else textBox1.Text=Convert.ToString(i); } (7) 将Button1-Button9的单击事件处理函数设定为Button0单击事件处理函数。 (8) 为标题为.按钮增长事件处理函数以下: private void btn_dot_Click(object sender, System.EventArgs e) { int n=textBox1.Text.IndexOf("."); if(n==-1)//若是没有小数点,增长小数点,不然不增长 textBox1.Text=textBox1.Text+"."; } (9) 编译,单击数字按钮,在textBox1能够看到输入的数字,也能够输入小数。 (10) 先实现加法,必须定义一个浮点类型变量sum,初始值为0,记录部分和。 (11) 输入了第一个加数,而后输入任一运算符(+、-、*、\或=),应首先清除编辑框中显示的第一个加数,才能输入第二个加数。为实现此功能,必须定义一个布尔变量blnClear,初始值为false,表示输入数字或小数点前不清除编辑框中显示,输入运算符(+、-、*、\或=)后,blnClear=true,表示再输入数字或小数点先清除编辑框中显示。修改前边程序,输入数字或小数点前,要判断变量blnClear,如为true,清除编辑框中显示的内容后,再显示新输入的数字或小数点,同时修改blnClear=false。为此修改append_num方法以下: public void append_num(int i) { if(blnClear)//若是准备输入下一个加数,应先清除textBox1显示内容 { textBox1.Text="0";//阴影部分为新增语句 blnClear=false; } if(textBox1.Text!="0") textBox1.Text+=Convert.ToString(i); else textBox1.Text=Convert.ToString(i); } (12) 修改btn_dot_Click方法以下: private void btn_dot_Click(object sender, System.EventArgs e) { if(blnClear) //若是准备输入下一个数,应先清除textBox1显示内容 { textBox1.Text="0";//阴影部分为新增语句 blnClear=false; } int n=textBox1.Text.IndexOf("."); if(n==-1)//若是没有小数点,增长小数点,防止屡次输入小数点 textBox1.Text=textBox1.Text+"."; } (13) 若是计算1+2-3的运算结果,先单击按钮1,编辑框中显示1,再单击按钮+,执行运算sum=sum+1(注意此时sum=0),显示sum到编辑框中(实际显示不变),记住这次输入的运算符,这里为+号。单击按钮2,编辑框中显示2,再单击按钮-,按记录的运算符(这里是+)计算sum=sum+2,显示sum到编辑框中,记住这次输入的运算符,这里为-号,依此类推。为实现此功能,必须定义一个字符串变量strOper,记录输入的运算符,初始值为"+",保证输入第一个运算符后,执行运算sum=sum+第一个加数,因为初始sum=0,也就是sum=第一个加数。标题为+的按钮的单击事件处理函数以下: private void btn_add_Click(object sender, System.EventArgs e) { double dbSecond=Convert.ToDouble(textBox1.Text); if(!blnClear)//若是未输入第二个操做数,不运算 switch(strOper)//按记录的运算符号运算 { case "+": sum+=dbSecond; break; //在此增长其它运算符-、*、\代码 } if(sender==btn_add) strOper="+"; //在此增长运算符-、*、\、=代码 textBox1.Text=Convert.ToString(sum); blnClear=true; } (14) =号处理语句和+号处理基本一致,修改标题为+按钮的事件函数以下: private void btn_add_Click(object sender, System.EventArgs e) { double dbSecond=Convert.ToDouble(textBox1.Text); if(!blnClear)//若是未输入第二个操做数,不运算 switch(strOper)//按记录的运算符号运算 { case "+": sum+=dbSecond; break; //在此增长运算符-、*、\代码 } if(sender==btn_add) strOper="+"; if(sender==btn_equ)//为=号处理增长的语句 strOper="="; textBox1.Text=Convert.ToString(sum); blnClear=true; } 将btn_equ按钮的单击事件函数设定为+按钮的单击事件函数。 (15) 为标题为C按钮增长事件函数以下: private void btn_C_Click(object sender, System.EventArgs e) { textBox1.Text="0"; sum=0; blnClear=false; strOper="+"; } (16) 请读者本身补上减法,乘法,除法运算的语句。 习题: (1) 在窗口中显示一行字符串,加入两个按纽,单击按纽1把字符串改成红色,单击按纽2把字符串改成黑色。使字符串为红色时红色按纽不能使用,字符串为黑色时黑色按纽不能使用。(提示:能够修改按钮的属性Enabled为false使其不能使用。) (2) 将上题改成用按扭修改字体的大小,分别为大字体和小字体。(参见3.9节) (3) 加一文本框控件和一按纽,单击按纽将文本框控件输入内容显示标签控件上。(提示:单击按钮事件处理函数中加语句label1.Text=textBox1.Text)。 (4) 修改上题,使文本框控件和标签控件文本同步显示(提示:文本框控件的TextChanged事件处理函数中加语句label1.Text=textBox1.Text)。 (5) 加一文本框控件和一按纽,单击按纽将文本框控件输入的文本中选中的内容显示在标签控件上(提示:单击按钮事件处理函数中加语句label1.Text=textBox1.SelText。) (6) 加一文本框控件和一按纽,单击按纽将文本框控件输入的文本的字符、选中的内容的字符数和选中的内容的开始位置显示在标签控件上。 (7) 用控件RadioButton选择性别,把选择的结果用Label控件显示出来。 (8) 例子e3_8中如改成响应单击事件Click,可能出现什么问题? (9) 用控件ComboBox修改标签控件字体的大小。(用属性Item在下拉列表中输入大小)。 (10) 放ListBox控件到窗体中,属性Name=listBox1。列表框有三项分别为:苹果,梨子,香蕉。容许多选。标签控件同步显示ListBox控件所作的选择。提示:为ListBox控件的SelectedIndexChenged事件增长事件函数, label1.Text="所选择的是:"; for(int i=0;i<listBox1.SelectedIndices.Count;i++) label1.Text+=listBox1.SelectedItems[i].ToString()+","; (11) 放ListBox、TextBox和3个Button控件到窗体中,属性Name分别为listBox一、textBox一、Button一、Button二、Button3。Button控件属性Text分别为:增长、删除、清空。单击增长按钮,把textBox中输入的内容做为一个条目增长到listBox1中,单击删除按钮,删除listBox1中所选择的条目,单击清空按钮,清除listBox1全部条目。提示:增长用语句:listBox1.Items.Add(textBooooox1.Text)。删除所选择的条目用语句:listBox1.Items.RemoveAt(listBox1.SelectedIndex)。清除listBox1全部条目用语句:listBox1.Items.Clear()。 (12) 在窗体中显示字符,每隔1秒字体变大些,变到必定尺寸后,每隔1秒字体变小些,如此循环。增长一个按钮,能够启动和中止字符串字体大小变化,按钮标题给出正确提示。 (13) 在窗体中显示字符,每隔1秒字符移动必定距离,先右移,移到右边界,再左移,移到左边界,又一次右移,如此循环。(提示:修改Label的Left属性值。) (14) 修改例子e3_17,使显示字符串为红色时,标题为红色的菜单项无效;使显示字符串黑色时,标题为黑色的菜单项无效。 (15) 修改例子e3_17,使显示字符串为红色时,标题为红色的菜单项前增长选中标志;使显示字符串黑色时,标题为黑色的菜单项前增长选中标志。 (16) 为例e3_17的菜单项增长加速键,键入Alt+c打开顶级菜单项颜色的弹出菜单,弹出菜单打开后,键入B执行标题为黑色的菜单项命令,键入R执行标题为红色的菜单项命令。 (17) 为例子e3_17的菜单项定义加速键(属性Shortcut),键入ctrl+r使显示字符串为红色,键入ctrl+b使显示字符串黑色。 (18) 为例子e3_17顶级菜单项颜色增长单击事件处理函数,在事件处理函数中判断显示的字符串的颜色,决定是否为相应的菜单项增长选中标志。 (19) 拖动鼠标左键时,在状态栏中显示鼠标的位置。 (20) 模拟画笔程序,在左侧增长工具按钮,在下部增长颜色按钮。 (21) 在工具栏中加三个按钮,单击按钮时,按钮保持按下状态,再单击按钮,按钮抬起。在按下状态,使标签控件中字符串加下画线、斜体或加粗,抬起则取消。 (21) 工具栏中按钮的属性Style设置为ToolBarButtonStyle.DropDownButton,按钮可有一个下拉菜单。首先建立一个ContextMenu菜单,指定工具栏中按钮的属性DropDownMenu的值为建立的ContextMenu菜单对象,将在按下按钮时显示这个菜单。请用工具栏中按钮的下拉菜单实现使标签控件字符的颜色变为红色、黑色。(提示:工具栏中按钮的属性Style设置为ToggleButton。属性Pushed是一个布尔变量,表示工具栏按钮当前是否处于按下状态) (22) 用工具栏中按钮的下拉菜单实现使标签控件字符的颜色变为红色、黑色。(提示:如工具栏中按钮的属性Style设置为DropDownButton,按钮可有一个下拉菜单。首先建立一个ContextMenu菜单,指定工具栏中按钮的属性DropDownMenu的值为建立的ContextMenu菜单对象,将在按下按钮时显示这个菜单。) (23) 完成计算器的减法和乘除程序。增长求平方,对数等功能。(例如Math.Sqrt()) 第四章 文本编辑器的实现 本章的目的是创建一个文本编辑器,同时继续介绍控件的用法。有两类文本编辑器:单文档文本编辑器和多文档文本编辑器,单文档文本编辑器一次只容许打开一个文件,若是要打开另外一个文件,必须关闭当前打开的文件,微软的写字板程序就是一个典型的单文档字处理程序。多文档文本编辑器同时容许打开多个文件,每一个文件占用一个子窗口,微软的Word程序就是一个典型的多文档字处理程序。本章首先介绍创建一个单文档文本编辑器的方法,而后介绍创建多文档文本编辑器的方法。 4.1 用RichTextBox控件实现文本编辑器 RichTextBox控件能够用来输入和编辑文本,该控件和TextBox控件有许多相同的属性、事件和方法,但比TextBox控件的功能多,除了TextBox控件的功能外,还能够设定文字的颜色、字体和段落格式,支持字符串查找功能,支持rtf格式等。这里只介绍在TextBox控件中没有介绍的属性、事件和方法,相同部分就不介绍了,可参见TextBox控件。RichTextBox控件的属性、事件和方法以下:  属性Dock:不少控件都有此属性,它设定控件在窗体中的位置,能够是枚举类型DockStyle的成员None、Left、Right、Top、Bottom或Fill,分别表示在窗体的任意位置、左侧、右侧、顶部、底部或充满客户区。在属性窗口中,属性DOCK的值用周边5个矩形,中间一个矩形的图形来表示。  属性SelectedText:获取或设置RichTextBox控件内的选定文本。  属性SelectionLength:获取或设置RichTextBox控件中选定文本的字符数。  属性SelectionStart:获取或设置RichTextBox控件中选定的文本起始点。  属性SelectionFont:若是已选定文本,获取或设置选定文本字体,若是未选定文本,获取当前输入字符采用字体或设置之后输入字符采用字体。  属性SelectionColor:若是已选定文本,获取或设置选定文本的颜色,若是未选定文本,获取当前输入字符采用的颜色或设置之后输入字符采用的颜色。  属性Lines:记录RichTextBox控件中全部文本的字符串数组,每两个回车之间字符串是数组的一个元素。  属性Modified:指示用户是否已修改控件的内容。为true,表示已修改。  事件SelectionChange:RichTextBox控件内的选定文本更改时发生的事件。  事件TextChanged:RichTextBox控件内的文本内容改变时发生的事件。  方法Clear():清除RichTextBox控件中用户输入的全部内容,即清空属性Lines。  方法Copy()、Cut()、Paste():实现RichTextBox控件的拷贝、剪贴、粘贴功能。  方法SelectAll():选择RichTextBox控件内的全部文本。  方法Find():实现查找功能。从第二个参数指定的位置,查找第一个参数指定的字符串,并返回找到的第一个匹配字符串的位置。返回负值,表示未找到匹配字符串。第三个参数指定查找的一些附加条件,能够是枚举类型RichTextBoxFinds的成员:MatchCase(区分大小写)、Reverse(反向查找)等。容许有1个、2个或3个参数。  方法SaveFile():存文件,它有2个参数,第一个参数为要存文件的全路径和文件名,第二个参数是文件类型,能够是:纯文本,RichTextBoxStreamType.PlainText;Rtf格式流,RichTextBoxStreamType.RichText;采用Unicode编码的文本流,RichTextBoxStreamType.UnicodePlainText。  方法LoadFile():读文件,参数同方法SaveFile(),注意存取文件的类型必须一致。  方法Undo():撤消RichTextBox控件中的上一个编辑操做。  方法Redo():从新应用RichTextBox控件中上次撤消的操做。 4.2 实现文本编辑器的剪贴板功能 许多程序都支持剪贴板功能。经过剪贴板能够完成数据的剪贴(Cut),复制(Copy),粘贴(Paste)等功能。剪贴板能够理解为一块存储数据的公共区域,用户能够把数据复制或剪贴到剪贴板中,本任务或其它任务要用剪贴板中的数据时,能够用粘贴功能从剪贴板中把数据取出。存入剪贴板中的数据,能够是字符,位图,或者其它格式数据。实现文本编辑器的编辑和剪贴板功能的具体步骤以下: (1) 新建项目。放RichTextBox控件到窗体。属性Name=richTextBox1,Dock=Fill,Text=””。 (2) 放Mainmenu控件到窗体中。增长顶级菜单项:编辑,为其弹出菜单增长菜单项:剪切、复制、粘贴、撤销和恢复,属性Name分别为:mainMenuEdit、menuItemEditCut、menuItemEditCopy、menuItemEditPaste、menuItemEditUndo、menuItemEditRedo。为各个菜单项增长事件处理函数以下: private void menuItemEditCut_Click(object sender, System.EventArgs e) { richTextBox1.Cut();} //剪切 private void menuItemEditCopy_Click(object sender, System.EventArgs e) { richTextBox1.Copy();} //拷贝 private void menuItemEditPaste_Click(object sender, System.EventArgs e) { richTextBox1.Paste();} //粘贴 private void menuItemEditUndo_Click(object sender, System.EventArgs e) { richTextBox1.Undo();} //撤销 private void menuItemEditRedo_Click(object sender, System.EventArgs e) { richTextBox1.Redo();} //恢复 (3) 编译,运行,输入一些字符后,选中一些字符,试验一下剪切、复制、粘贴等功能,并查看一下在剪贴板中字符是否能粘贴到其它字处理软件中,例如写字板。查看一下撤销和恢复功能是否可用。 4.3 实现文本编辑器的存取文件功能 文本编辑器都具备文件存取功能,顶级菜单项文件的弹出菜单中通常包括以下菜单项:新建、打开、关闭、保存和另存为等。本节实现以上菜单项。 4.3.1 OpenFileDialog和SaveFileDialog控件 OpenFileDialog对话框用来选择要打开的文件路径及文件名,SaveFileDialog对话框用来选择要存储文件的路径及文件名。两个对话框的外观以下图,它们的属性和方法基本相同,这里在一块儿介绍。 图4.3.1A 打开文件对话框 图4.3.1B 文件另存为对话框  属性Filter:字符串类型,选择在对话框中显示的文件类型。属性Filter有多项,中间用|分开,每两项是一组,每组的第一项将出如今对话框保存类型(T)下拉列表编辑框的下拉列表中(见图4.3.1A),供用户选择,第二项表示如第一项被选中,对话框实际列出的文件。例如Filter="纯文本文件(*.txt)|*.txt|全部文件(*.*)|*.*",表示打开对话框,对话框的文件类型(T)下拉列表编辑框的下拉列表有两项:纯文本文件(*.txt)和全部文件(*.*),供用户选择。若是从文件类型下拉列表编辑框的下拉列表中选中"纯文本文件(*.txt)",表示打开对话框,只列出全部扩展名为.txt的文件,若是选中"全部文件(*.*)",表示打开对话框,将列出全部文件。  属性FilterIndex:表示打开对话框后,对话框的文件类型(T)下拉列表编辑框的下拉列表中首先被选中的项的索引号。能够在设计阶段在属性窗口修改属性FilterIndex和Filter,也可在程序中用下列语句修改:openFileDialog1.Filter="纯文本文件(*.txt)|*.txt|全部文件(*.*)|*.*",openFileDialog1.FilterIndex=1。  属性FileName:用户选取的文件的路径和文件名。  属性InitialDirectory:打开对话框首先显示该属性指定的文件夹中的文件。  属性DefaultExt:若是用户未指定扩展名,自动增长属性指定的文件扩展名。  方法ShowDialog():打开对话框,根据方法的返回值肯定用户单击了那个按钮,如返回DialogResult.Cancle,用户单击了忽略按钮,如返回DialogResult.OK,用户单击了打开或保存按钮。 4.3.2 存取文件功能的实现 (4) 把OpenFileDialog和SaveFileDialog控件放到窗体中。属性Name分别是openFileDialog1和saveFileDialog1。 (5) 增长顶级菜单项:文件,为其弹出菜单增长菜单项:新建、打开...、保存...、另存为...、退出。修改Name属性分别为:mainMenuFile、menuItemFileNew、menuItemFileOpen、menuItemFileSave、menuItemFileSaveAs、menuItemFileExit。 (6) 为Form1类增长string类型变量记录当前编辑的文件名:string s_FileName="",若是为空,表示还未记录文件名,即编辑的文件尚未名字。当单击菜单项保存,保存文件时,必须请用户输入文件名。 (7) 为新建菜单项增长事件处理函数以下: private void menuItemFileNew_Click(object sender, System.EventArgs e) { richTextBox1.Text="";//或richTextBox1.Clear(); s_FileName="";//新建文件没有文件名。 } (8) 为打开文件菜单项增长事件处理函数以下: private void menuItemFileOpen_Click(object sender, System.EventArgs e) { if(openFileDialog1.ShowDialog()==DialogResult.OK) { s_FileName=openFileDialog1.FileName; richTextBox1.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } (9) 为另存为菜单项增长事件处理函数以下: private void menuItemFileSaveAs_Click(object sender, System.EventArgs e) { if(saveFileDialog1.ShowDialog()==DialogResult.OK) { s_FileName=saveFileDialog1.FileName; richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); }//注意存取文件类型应一致。 } (10) 为保存文件菜单项增长事件处理处理函数以下: private void menuItemSaveFile_Click(object sender, System.EventArgs e) { if(s_FileName.Length!=0) richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText); else menuItemFileSaveAs_Click(sender,e);//调用另存为菜单项事件处理函数 } (11) 把SaveFileDialog控件放到窗体中,将自动建立控件对象,其生命周期等于窗体生命周期,将长期占用存储空间。实际上SaveFileDialog控件对象只在存文件菜单项事件处理函数中有用,其它时间无用。为了节约存储空间,能够在存文件菜单项事件处理函数中创建SaveFileDialog控件对象,退出该事件处理函数时,自动释放该对象。修改另存为菜单项事件处理函数以下(首先删除增长的控件SaveFileDialog): private void menuItemFileSaveAs_Click(object sender, System.EventArgs e) { SaveFileDialog saveFileDialog1=new SaveFileDialog(); saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|全部文件(*.*)|*.*"; saveFileDialog1.FilterIndex=1; if(saveFileDialog1.ShowDialog()==DialogResult.OK) { s_FileName=saveFileDialog1.FileName; richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); }//也能够用此方法修改打开文件菜单项事件处理函数。 } (12) 为退出菜单项增长事件处理函数以下: private void menuItemExit_Click(object sender, System.EventArgs e) { Close();} (13) 编译,运行,能够存取文件。 4.4 修改字体属性 为修改字体属性,首先打开字体对话框FontDialog,选择指定字体。能够按两种方式修改字体,若是未选中字符,表示之后键入的字符将按选定字体输入。若是选中字符,则仅修改选定字符的字体。修改字符颜色也根据一样原则。 4.4.1 FontDialog控件属性和方法 用户能够用FontDialog对话框选定指定字体,FontDialog控件和OpenDialog控件的属性和方法基本相同,这里只介绍不一样部分。属性Font:用户用FontDialog对话框选定的字体。FontDialog对话框显示效果如图4.3.1。 4.4.2 修改字体属性的实现方法 (14) 放FontDialog控件到窗体,属性Name=fontDialog1。增长顶级菜单项:格式,为格式顶级菜单项的弹出菜单增长菜单项:字体,属性Name分别为mainMenuModel和menuItemModelFont,为字体菜单项增长事件处理函数以下: private void menuItemModelFont_Click(object sender, System.EventArgs e) { if(fontDialog1.ShowDialog()==DialogResult.OK) richTextBox1.SelectionFont=fontDialog1.Font; } (15) 编译,运行,在选中字符和不选中字符两种状况下,用字体菜单项修改字体,看是否能实现写字板中一样的功能。 图4.3.1字体对话框 4.5 实现About对话框 前边介绍的SaveDialog、OpenDialog和FontDialog都是类库中预先定义的对话框,本节介绍如何建立知足必定要求的自制对话框。对话框其实就是窗体,其基类和主窗体同样,是System.Windows.Forms.Form。只是通常对话框只有关闭按钮,没有最大化和最小化按钮,对话框的边界是固定的,不能改变。设计本身的对话框是常常遇到的工做。 (16) 选择菜单项项目/添加Windows窗体,弹出对话框(见图4.5),在模板(T)编辑框中选择Windows窗体,在名称栏(N)编辑框中输入窗体文件名称:formAbout.cs,单击打开按钮,能够见到一个新窗体。从文件formAbout.cs能够看到新建窗体类名也为formAbout。 (17) 修改formAbout属性StartPosition=CenterParent,表示打开对话框时,对话框在父窗口的中间。修改属性MaximizeBox=False,MinimizeBox=False,表示没有最大化和最小化按钮,既不能最大化和最小化。属性FormBorderStyle=FixedDialog,窗口不能修改大小。属性Text="关于记事本"。能够在窗体中增长各类控件,例如,小图标,Label控件等。本例仅增长Label控件表示版权信息,其属性Text="版权全部"。一个按钮,属性Text="肯定",按钮单击事件处理函数以下: private void button1_Click(object sender,System.EventArgs e) {Close();} (18) 为Form1窗体增长顶级菜单项:帮助,为帮助顶级菜单项弹出菜单增长菜单项:关于…,属性Name为menuItemAbout。关于…菜单项单击事件处理函数以下: private void menuItemAbout_Click(object sender, System.EventArgs e) { formAbout AboutDialog=new formAbout(); AboutDialog.ShowDialog(this); }//注意不能使用Show()函数 (19) 编译,运行,单击关于…菜单项,将出现一个formAbout对话框(如右图),而且不关闭此对话框,不能回到主窗口,通常把这样的对话框叫作模式对话框。 图4.5 4.6 实现文本编辑器查找替换功能 本节首先介绍模式对话框和非模式对话框的概念。并用非模式对话框实现文本编辑器程序的查找和替换功能。 4.6.1 模式对话框和非模式对话框 模式对话框和非模式对话框的区别是:打开模式对话框后,只有关闭该模式对话框,才能转到其余窗口,例如前边讲到的SaveDialog和OpenDialog都是典型的模式对话框。而打开非模式对话框后,没必要退出该模式对话框,就能够转到其余窗口,例如查找和替换对话框都是典型的非模式对话框。两类对话框本质上都是窗体,是System.Windows.Forms.Form类的派生类,只是打开时使用的方法不同,打开模式对话框,使用方法ShowDialog(),而打开非模式对话框,使用方法Show()。文本编辑器程序中,查找和替换对话框通常是非模式对话框。 4.6.2 写字板查找替换功能的实现 (20) 创建查找替换对话框。对话框其实就是窗体,其基类是System.Windows.Forms.Form。选择菜单项项目/添加Windows窗体,弹出对话框(如图4.5),选择Windows窗体,在名称栏输入窗体文件名称:formFindReplace.cs,单击打开按钮,能够见到一个新窗体。其属性Name=formFindReplace。 (21) 修改formFindReplace窗体属性StartPosition=CenterParent,表示打开对话框时,对话框在父窗口的中间。修改属性MaximizeBox=False,MinimizeBox=False,表示没有最大化和最小化按钮,既不能最大化和最小化。FormBorderStyle=FixedDialog,窗口不能修改大小。属性Text="查找和替换"。在窗体中增长两个Label控件,属性Text分别为"查找字符串"和"替换字符串"。两个TextBox控件,属性Text=""。两个按钮,属性Text分别为"查找下一个"和"替换查到字符"。修改属性TopMost=true,使该窗口打开时总在其它窗体的前边。对话框界面如右图。 (22) 为formFindReplace窗体增长变量:Form1 MainForm1; (23) 修改formFindReplace类构造函数以下(阴影部分是所作的修改): public formAbout(Form1 form1)//增长参数 { //Windows窗体设计器支持所必需的 InitializeComponent(); //TODO:在InitializeComponent调用后添加任何构造函数代码 MainForm1=form1;//新增语句,这里Form1是主窗体的属性Name的值 }//有了Form1,能够在formFindReplace窗体中调用主窗体的公有方法 (24) 为主窗体Form1增长方法以下,该方法将被formFindReplace窗体类调用。 public void FindRichTextBoxString(string FindString) {} //之后步骤将在此方法中增长查找语句 (25) formFindReplace窗体中查找下一个按钮单击事件处理函数以下: private void buttonFind_Click(object sender, System.EventArgs e) { if(textBox1.Text.Length!=0)//若是查找字符串不为空,调用主窗体查找方法 MainForm1.FindRichTextBoxString(textBox1.Text);//上步增长的方法 else MessageBox.Show("查找字符串不能为空","提示",MessageBoxButtons.OK); }//MessageBox时对话框,使用方法见4.7.1节 (26) 为主窗体Form1增长方法以下,该方法将被formFindReplace窗体类调用。 public void ReplaceRichTextBoxString(string ReplaceString) {} //之后步骤将在此方法中增长替换语句 (27) 为替换查到字符按钮单击事件增长事件处理函数以下: private void buttonReplace_Click(object sender, System.EventArgs e) { if(textBox2.Text.Length!=0)//若是查找字符串不为空,调用主窗体替换方法 MainForm1.ReplaceRichTextBoxString(textBox1.Text,textBox2.Text); else//方法MainForm1.ReplaceRichTextBoxString见(26)中定义 MessageBox.Show("替换字符串不能为空","提示", MessageBoxButtons.OK); } (28) 为Form1窗体增长变量:int FindPostion=0,记录查找位置。 (29) 为Form1窗体顶级菜单项编辑的弹出菜单增长菜单项:查找和替换。为查找和替换菜单项单击事件增长事件处理函数以下: private void menuItemFindReplace_Click(object sender, System.EventArgs e) { FindPostion=0; formAbout FindReplaceDialog=new formAbout(this);//注意this FindReplaceDialog.Show();//打开非模式对话框使用Show()方法 } (30) 为在前边定义的Form1主窗体的FindRichTextBoxString方法增长语句以下: public void FindRichTextBoxString(string FindString) { if(FindPostion>=richTextBox1.Text.Length)//已查到文本底部 { MessageBox.Show("已到文本底部,再次查找将从文本开始处查找", "提示",MessageBoxButtons.OK); FindPostion=0; return; }//下边语句进行查找,返回找到的位置,返回-1,表示未找到,参数1是要找的字符串 //参数2是查找的开始位置,参数3是查找的一些选项,如大小写是否匹配,查找方向等 FindPostion=richTextBox1.Find(FindString, FindPostion,RichTextBoxFinds.MatchCase); if(FindPostion==-1)//若是未找到 { MessageBox.Show("已到文本底部,再次查找将从文本开始处查找", "提示", MessageBoxButtons.OK); FindPostion=0;//下次查找的开始位置 } else//已找到 { richTextBox1.Focus();//主窗体成为注视窗口 FindPostion+=FindString.Length; }//下次查找的开始位置在这次找到字符串以后 } (31) 为在前边定义的Form1主窗体的ReplaceRichTextBoxString方法增长语句以下: public void ReplaceRichTextBoxString(string ReplaceString) { if(richTextBox1.SelectedText.Length!=0)//若是选取了字符串 richTextBox1.SelectedText=ReplaceString;//替换被选的字符串 } (32) 编译,运行,输入若干字符,选中菜单项:编辑/查找和替换,打开对话框,注意该对话框能够在不关闭的状况下,转到主窗体,而且老是在其它窗体的前边,所以它是一个典型的非模式对话框。在对话框中输入查找和替换的字符,单击标题为查找下一个的按钮,能够找到所选字符,并被选中,单击标题为替换所选字符按钮,能够看到查找到的字符被替换。运行效果如右图: 4.7 提示用户保存修改的文件 用户在新建文本,打开其余文本或者退出文本编辑器时,若是编辑内容发生了改变,应提示用户是否保存已修改的文本内容。所以就须要在用户关闭当前文件前,弹出提示对话框,提醒用户是否保存当前文件。本节实现此功能。 4.7.1 对话框MessageBox 使用MessageBox能够打开一个对话框,用法以下: MessageBox.Show(this,"要保存当前更改吗?","保存更改吗?", MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question); 第一个参数是父窗口,第二个参数是提示信息,第三个参数是标题栏的内容,第四个参数是有那些按钮,此例有YES,NO,CANCEL按钮,还能够使用AbortRetryIgnore(停止、重试和忽略按钮)、OK(肯定按钮)、OKCancel(肯定和取消按钮)、RetryCance(重试和忽略按钮)、YesNo(是和否按钮)等选项。第五个参数是使用那一个图标,此例是一个问号图标,还能够是Asterisk、Error、Exclamation、Hand、Stop、Warning等图标,如为None则无图标。返回值是System.Windows.Forms.DialogResult变量,表明用户按了那一个按钮。若是返回值是System.Windows.Forms.DialogResult.Yes,则表示按了YES键,表示要存修改的文件。若是返回值是System.Windows.Forms.DialogResult.Cancel,按Cancel键,表示忽略这次操做。若是返回值是System.Windows.Forms.DialogResult.No,则表示按了No键,表示不存修改的文件。以上设计的对话框MessageBox以下图: 4.7.2 提示用户保存修改的文件的实现 (33) 为Form1类增长一个bool变量bSave=false做为标记,用来跟踪RichTextBox中文本内容改变的状况。在程序开始运行、创建和打开一个新文件时,bSave=false,表示没必要保存当前文本。RichTextBox控件有一个TextChanged事件,当文本发生改变的时候,这个事件就会被激活,在该事件处理函数中,使bSave=true。 (34) 首先增长一个函数,其功能是判断是否须要将已修改的文件存盘,之因此要增长这个函数是由于有三处要用到此函数。该函数返回true,表示继续操做,该函数返回false,表示忽略这次操做,该函数定义以下: public bool IfSaveOldFile() { bool ReturnValue=true; if(bSave) { System.Windows.Forms.DialogResult dr; dr=MessageBox.Show(this,"要保存当前更改吗?","保存更改吗?", MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question); switch(dr) { case System.Windows.Forms.DialogResult.Yes://单击了yes按钮,保存修改 bSave=false; if(s_FileName.Length!=0) richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText); else { SaveFileDialog saveFileDialog1=new SaveFileDialog(); saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|全部文件(*.*)|*.*"; saveFileDialog1.FilterIndex=1; if(saveFileDialog1.ShowDialog()==DialogResult.OK) { s_FileName=saveFileDialog1.FileName; richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } ReturnValue=true; break; case System.Windows.Forms.DialogResult.No://单击了no按钮,不保存 bSave=false; ReturnValue=true; break; case System.Windows.Forms.DialogResult.Cancel://单击了Cancel按钮 ReturnValue=false; break; } } return ReturnValue; } (35) 在新建和打开菜单项的事件处理函数的头部增长以下语句: if(!IfSaveOldFile())//若是忽略,退出。 return; (36) 修改存文件菜单项单击事件处理函数以下: private void menuItemSaveFile_Click(object sender, System.EventArgs e) { if(s_FileName.Length!=0) { bSave=false;//阴影为增长的语句 richTextBox1.SaveFile(s_FileName,RichTextBoxStreamType.PlainText); } else menuItemSaveAs_Click(sender,e); } (37) 修改另存为菜单项单击事件处理函数以下: private void menuItemSaveAs_Click(object sender, System.EventArgs e) { SaveFileDialog saveFileDialog1=new SaveFileDialog(); saveFileDialog1.Filter="纯文本文件(*.txt)|*.txt|全部文件(*.*)|*.*"; saveFileDialog1.FilterIndex=1; if(saveFileDialog1.ShowDialog()==DialogResult.OK) { s_FileName=saveFileDialog1.FileName; richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); bSave=false;//阴影为增长的语句 } } (38) 为RichTextBox控件TextChanged事件增长事件处理函数以下: private void richTextBox1_TextChanged(object sender, System.EventArgs e) { bSave=true;} (39) 为Form1窗体Closing事件是在关闭窗口以前发送的事件,此时,窗体中的控件还存在,还能够保存修改的内容,也能够不退出。增长它的事件处理函数以下: private void Form1_Closing(object sender,System.ComponentModel.CancelEventArgs e) { if(!IfSaveOldFile()) e.Cancel=true;//不退出 } (40) 编译,运行,键入若干字符,选中菜单项新建或打开,或退出,将看到提示信息,问是否保存修改的文件。有三种选择:存文件,不存文件,忽略这次操做,试验单击不一样按钮的效果。 4.8 打印和打印预览 打印和打印预览是一个编辑器必须具备的功能,本节介绍实现打印和打印预览的方法。通常要实现以下菜单项:打印、打印预览、页面设置。 4.8.1 PrintDocument类 PrintDocument组件是用于完成打印的类,其经常使用属性、方法和事件以下:  属性DocumentName:字符串类型,记录打印文档时显示的文档名(例如,在打印状态对话框或打印机队列中显示)。  方法Print:开始文档的打印。  事件BeginPrint:在调用Print方法后,在打印文档的第一页以前发生。  事件PrintPage:须要打印新的一页时发生。  事件EndPrint:在文档的最后一页打印后发生。 若要打印,首先建立PrintDocument组件的对象。而后使用页面设置对话框PageSetupDialog设置页面打印方式,这些设置做为要打印的全部页的默认设置。使用打印对话框PrintDialog设置对文档进行打印的打印机的参数。在打开两个对话框前,首先设置对话框的属性Document为指定的PrintDocument类对象,修改的设置将保存到PrintDocument组件对象中。第三步是调用PrintDocument.Print方法来实际打印文档。当调用该方法后,引起下列事件:BeginPrint、PrintPage、EndPrint。其中每打印一页都引起PrintPage事件,打印多页,要屡次引起PrintPage事件。完成一次打印,能够引起一个或多个PrintPage事件。 程序员应为这3个事件编写事件处理函数。BeginPrint事件处理函数进行打印初始化,通常设置在打印时全部页的相同属性或共用的资源,例如全部页共同使用的字体、创建要打印的文件流等。PrintPage事件处理函数负责打印一页数据。EndPrint事件处理函数进行打印善后工做。这些处理函数的第2个参数System.Drawing.Printing.PrintEventArgs e提供了一些附加信息,主要有:  e.Cancel:布尔变量,设置为true,将取消此次打印做业。  e.Graphics:所使用的打印机的设备环境,参见第五章。  e.HasMorePages:布尔变量。PrintPage事件处理函数打印一页后,仍有数据未打印,退出事件处理函数前设置HasMorePages=true,退出PrintPage事件处理函数后,将再次引起PrintPage事件,打印下一页。  e.MarginBounds:打印区域的大小,是Rectangle结构,元素包括左上角坐标:Left和Top,宽和高:Width和Height。单位为1/100英寸。  e.MarginBounds:打印纸的大小,是Rectangle结构。单位为1/100英寸。  e.PageSettings:PageSettings类对象,包含用对话框PageSetupDialog设置的页面打印方式的所有信息。可用帮助查看PageSettings类的属性。 下边为这3个事件编写事件处理函数,具体步骤以下: (41) 在最后一个using语句以后增长语句: using System.IO; using System.Drawing.Printing; (42) 本例打印或预览RichTextBox中的内容,增长变量:StringReader streamToPrint=null。若是打印或预览文件,改成:StreamReader streamToPrint,流的概念参见第六章。增长打印使用的字体的变量:Font printFont。 (43) 放PrintDocument控件到窗体,属性name为printDocument1。 (44) 为printDocument1增长BeginPrint事件处理函数以下: private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e) { printFont=richTextBox1.Font;//打印使用的字体 streamToPrint=new StringReader(richTextBox1.Text);//打印richTextBox1.Text }//如预览文件改成:streamToPrint=new StreamReader("文件的路径及文件名"); (45) printDocument1的PrintPage事件处理函数以下。streamToPrint.ReadLine()读入一段数据,可能打印多行。本事件处理函数将此段数据打印在一行上,所以方法必须改进。 private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { float linesPerPage=0;//记录每页最大行数 float yPos=0;//记录将要打印的一行数据在垂直方向的位置 int count=0;//记录每页已打印行数 float leftMargin=e.MarginBounds.Left;//左边距 float topMargin=e.MarginBounds.Top;//顶边距 string line=null;//从RichTextBox中读取一段字符将存到line中 //每页最大行数=一页纸打印区域的高度/一行字符的高度 linesPerPage=e.MarginBounds.Height/printFont.GetHeight(e.Graphics); //若是当前页已打印行数小于每页最大行数并且读出数据不为null,继续打印 while(count<linesPerPage&&((line=streamToPrint.ReadLine())!=null)) { //yPos为要打印的当前行在垂直方向上的位置 yPos=topMargin+(count*printFont.GetHeight(e.Graphics)); e.Graphics.DrawString(line,printFont,Brushes.Black, leftMargin,yPos,new StringFormat());//打印,参见第五章 count++;//已打印行数加1 } if(line!=null)//是否须要打印下一页 e.HasMorePages=true;//须要打印下一页 else e.HasMorePages=false;//不须要打印下一页 } (46) 为printDocument1增长EndPrint事件处理函数以下: private void printDocument1_EndPrint (object sender, System.Drawing.Printing.PrintEventArgs e) { if(streamToPrint!=null) streamToPrint.Close();//释放不用的资源 } 4.8.2 打印设置对话框控件PageSetupDialog Windows窗体的PageSetupDialog控件是一个页面设置对话框,用于在Windows应用程序中设置打印页面的详细信息,对话框的外观如图4.8.2。 图4.8.2 用户使用此对话框可以设置纸张大小(类型)、纸张来源、纵向与横向打印、上下左右的页边距等。在打开对话框前,首先设置其属性Document为指定的PrintDocument类对象,用来把页面设置保存到PrintDocument类对象中。为文本编辑器增长页面设置功能的具体步骤以下: (47) 为文件顶级菜单项的弹出菜单增长菜单项:页面设置。 (48) 放PageSetupDialog控件到窗体,属性name为pageSetupDialog1。 (49) 为页面设置菜单项增长单击事件处理函数以下: private void menuItem5_Click(object sender,System.EventArgs e) { pageSetupDialog1.Document=printDocument1; pageSetupDialog1.ShowDialog(); } (50) 打开对话框pageSetupDialog1后,若是单击了肯定按钮,PageSetupDialog对话框中所作的的页面设置被保存到PrintDocument类对象printDocument1中,若是单击了取消按钮,不保存这些修改,维持原来的值。当调用PrintDocument.Print方法来实际打印文档时,引起PrintPage事件,该事件处理函数的第二个参数e提供了这些设置信息。 4.8.3 打印预览 用PrintPreviewDialog类能够在屏幕上显示PrintDocument的打印效果,既打印预览。实现打印预览的具体步骤以下: (51) 为文件顶级菜单项的弹出菜单增长菜单项:打印预览。 (52) 放PrintPreviewDialog控件到窗体,属性name为printPreviewDialog1。 (53) 为打印预览菜单项增长单击事件处理函数以下: private void menuItemPrintView_Click(object sender,System.EventArgs e) { printPreviewDialog1.Document=printDocument1; printPreviewDialog1.ShowDialog(); } (54) 编译,运行,输入若干字符,试验一下预览的效果,预览的效果如图4.8.3。 图4.8.3 4.8.4 用打印对话框PrintDialog实现打印 PrintDialog组件是类库中预先定义的对话框,用来设置对文档进行打印的打印机的参数,包括打印机名称、要打印的页(所有打印或指定页的范围)、打印的份数以及是否打印到文件等。在打开对话框前,首先设置其属性Document为指定的PrintDocument类对象,打开PrintDialog对话框后,修改的设置将保存到PrintDocument类的对象中。PrintDialog对话框的外观如图4.8.4。 图4.8.4 增长打印功能的具体步骤以下: (55) 放PrintDialog控件到窗体属性Name=printDialog1。 (56) 为文件顶级菜单项的弹出菜单增长菜单项:打印。 (57) 为打印菜单项增长单击事件处理函数以下:(不能打印?) private void menuItemPrint_Click(object sender, System.EventArgs e) { printDialog1.Document=printDocument1; if(printDialog1.ShowDialog(this)==DialogResult.OK) printDocument1.Print(); } (58) 编译,运行,输入若干字符,试验一下打印效果。 4.9 编写多文档界面应用程序 本节首先介绍如何创建相似Microsoft Word的文本编辑器,而后介绍如何创建相似Visualstudio.Net的编辑器那样的文本编辑器,有多个选项卡页。 4.9.1 创建相似Microsoft Word的编辑器 创建一个相似Microsoft Word的编辑器,能够有多页,每页处理一个文档。多文档界面(MDI)应用程序具备一个主窗体(父窗体),主窗体在其工做区内包含一组窗体(子窗体)。每一个子窗体都是一个限制为只能在该父窗体内出现的窗体。这些子窗体一般共享父窗体界面的菜单栏、工具栏以及其余部分。建立多文当编辑器的具体步骤以下: (1) 新建项目。修改主窗体属性IsMdiContainer=true,表示主窗体是一个子窗体容器。 (2) 放主菜单控件Mainmenu到主窗体。增长顶级菜单项:文件,属性Name=menuItemFile。为文件菜单增长菜单项:新建、打开、另存为、关闭当前窗口、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemSaveAs、menuItemCloseChild、menuItemExit。增长顶级菜单项:窗口,属性Name=menuItemWindow,属性MdiList=true,该属性将在窗口菜单下增长子窗口列表。为窗口菜单增长菜单项:水平平铺、层叠、垂直平铺,属性Name分别为menuItemTileH、menuItemCascade、menuItemTileV。 (3) 建立子窗体,选择菜单项:项目/添加Windows窗体,弹出对话框(见图4.5),选择Windows窗体,在名称栏输入窗体文件名称:FormChild.cs,单击打开按钮,能够见到一个新窗体。定义新窗体的类名也为FormChild。此窗体做为主窗体的子窗体。 (4) 放RichTextBox1控件到子窗体。修改属性Dock=Fill,Text="",Modifiers=public,使RichTextBox1为公有成员,在主窗体能够访问RichTextBox1。 (5) 为主窗体菜单项新文件增长单击事件处理函数以下: private void menuItemNew_Click(object sender,System.EventArgs e) { FormChild formChild=new FormChild(); formChild.MdiParent=this; formChild.Show(); } (6) 把OpenFileDialog控件放到窗体中。单击打开文件菜单项事件处理函数以下: private void menuItemOpen_Click(object sender, System.EventArgs e) { if(openFileDialog1.ShowDialog(this)==DialogResult.OK) { FormChild ChildForm=new FormChild(); ChildForm.MdiParent=this; ChildForm.richTextBox1.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType.PlainText); ChildForm.Show(); } } (7) 把SaveFileDialog控件放到子窗体中。另存为菜单项事件处理函数以下: private void menuItemChildSaveAs_Click(object sender, System.EventArgs e) { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK) { FormChild ChildForm=(FormChild)this.ActiveMdiChild; ChildForm.richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } (8) 为主窗体菜单项关闭当前窗口增长单击事件函数以下: private void menuItemCloseChild_Click(object sender, System.EventArgs e) { this.ActiveMdiChild.Close();} (9) 为主窗体菜单项退出增长单击事件函数以下: private void menuItemExit_Click(object sender, System.EventArgs e) { Close();} (10) 为主窗体菜单项水平平铺增长单击事件函数以下: private void menuItemTileH_Click(object sender, System.EventArgs e) { this.LayoutMdi(MdiLayout.TileHorizontal);} (11) 为主窗体菜单项层叠增长单击事件函数以下: private void menuItemCascade_Click_1(object sender, System.EventArgs e) { this.LayoutMdi(MdiLayout.Cascade);} (12) 为主窗体菜单项垂直平铺增长单击事件函数以下: private void menuItemTileV_Click(object sender, System.EventArgs e) { this.LayoutMdi(MdiLayout. TileVertical);} (13) 运行,运行效果以下,子窗体为层叠排列。 4.9.2 主窗口和子窗口的菜单的融合 在许多多文档编辑器应用程序中,在没有子窗体打开时,菜单比较简单,而有子窗体打开后,菜单增多。实现这种功能通常是在主窗体中建立一个简单菜单,子窗体没打开时,只显示这个简单菜单。在子窗体中也建立一个菜单,包含主窗体菜单中没有的菜单项。打开子窗体后,子窗体的菜单和主窗体菜单合并成为一个菜单,这个功能叫作主窗口和子窗口的菜单的融合。建立具备这种功能的多文档编辑器应用程序能够按下列步骤: (1) 新建项目。修改主窗口属性IsMdiContainer为true。 (2) 把Mainmenu控件放到主窗体中。增长顶级菜单项:文件。其属性MergeType=MergeItems,表示打开子窗体后,主窗体和子窗体中属性MergeOrder相同的顶级菜单项的弹出菜单中的菜单项合并为一个弹出菜单。属性MergeOrder=0。子窗体的顶级菜单项文件的属性MergeType也应为MergeItems,MergeOrder属性也应为0,这样打开子窗口后,才能合并为一个弹出菜单。属性Name=menuItemFile。为文件菜单增长菜单项:新建、打开、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemExit,属性MergeType都为Add,属性MergeOrder依次为一、二、6,目的是打开子窗口后,在新建和打开菜单项后加入子窗口菜单栏中的文件菜单的另存为菜单项。增长菜单:帮助,其属性MergeType=Add,属性MergeOrder=7,属性Name=menuItemHelp。为帮助菜单增长菜单项:关于…,属性Name=menuItemAbout。其他菜单在子窗口中实现。注意属性MergeOrder分别为0、7,打开子窗口后,子窗口中的菜单将按顺序插入到主窗口的菜单中,例如,子窗口有菜单:编辑,其属性MergeOrder=3,合并后,菜单排列顺序为:文件、编辑、帮助。 (3) 建立子窗体,选择菜单项:项目/添加Windows窗体,弹出对话框,选择Windows窗体,在名称栏输入窗体文件名称:formChild.cs,单击打开按钮,能够见到一个新窗体。定义新窗体的类名也为formChild。 (4) 为formChild窗体增长变量:Form1 MainForm1; (5) 修改formChild类构造函数以下(阴影部分是所作的修改): public formChild(Form1 form1)//增长参数 { //Windows窗体设计器支持所必需的 InitializeComponent(); //TODO:在InitializeComponent调用后添加任何构造函数代码 MainForm1=form1;//新增语句,这里Form1是主窗体的属性Name的值 }//有了Form1,能够在formChild窗体中调用主窗体的公有方法 (6) 把Mainmenu控件放到子窗体中。增长顶级菜单项:文件,其属性MergeType=MergeItems,属性MergeOrder=0。为文件顶级菜单项弹出菜单增长菜单项:另存为…,属性MergeType=Add,属性MergeOrder=3,菜单合并后,另存为…菜单项将出如今主窗口文件菜单的新建和打开菜单项以后。增长菜单项:关闭当前文件,属性MergeType=Add,属性MergeOrder=4。 (7) 增长顶级菜单项:编辑,其属性MergeType=Add,属性MergeOrder=3。注意属性MergeOrder=3,菜单合并后,编辑菜单将出现出现菜单文件以后。为编辑顶级菜单项弹出菜单增长菜单项:拷贝、剪贴、粘贴。 (8) 增长顶级菜单项:窗口,其属性MergeType=Add,属性MergeOrder=6,属性Name=menuItemWindow,属性MdiList=true,该属性将在窗口菜单下增长子窗口列表。为窗口菜单增长菜单项:层叠,属性Name为menuItemCascade。 (9) 放RichTextBox1控件到子窗体。修改属性Dock=Fill,Text=””,属性 Modifiers=public,使RichTextBox1为公有成员,在主窗体能够访问RichTextBox1。 (10) 为主窗体菜单项新文件增长单击事件函数以下: private void menuItemNew_Click(object sender, System.EventArgs e) { formChild ChildForm=new formChild(this); ChildForm.MdiParent=this; ChildForm.Show(); } (11) 把OpenFileDialog控件放到主窗体中。单击打开文件菜单项事件处理函数以下: private void menuItemOpen_Click(object sender, System.EventArgs e) { if(openFileDialog1.ShowDialog(this)==DialogResult.OK) { formChild ChildForm=new formChild(this); ChildForm.MdiParent=this; ChildForm.richTextBox1.LoadFile(openFileDialog1.FileName); ChildForm.Show(); } } (12) 为主窗体菜单项退出增长单击事件处理函数以下: private void menuItemExit_Click(object sender, System.EventArgs e) { Close();} (13) 为子窗体菜单项层叠增长单击事件处理函数以下: private void menuItemCascade_Click_1(object sender, System.EventArgs e) { MainForm1.LayoutMdi(MdiLayout.Cascade);} (14) 把SaveFileDialog控件放到子窗体中。为子窗体菜单项另存为增长单击事件函数以下: private void menuItemChildSaveAs_Click(object sender, System.EventArgs e) { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK) { richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } (15) 为子窗体菜单项关闭当前窗口增长单击事件函数以下: private void menuItemCloseChild_Click(object sender, System.EventArgs e) { Close();} (16) 为子窗体菜单项拷贝、剪贴和粘贴增长单击事件函数以下: 语句分别为:richTextBox1.Cut();richTextBox1.Copy();richTextBox1.Paste(); (17) 运行效果以下图。 4.9.3 创建相似Visualstudio.Net的编辑器 Visualstudio.Net的编辑器有多个选项卡页,能够编辑多个文件。创建选项卡页数固定,每选项卡页显示一行文本,相似Visualstudio.Net的编辑器的文本编辑器的具体实现步骤以下: (1) 新建项目。放TabControl控件到子窗体。修改属性Dock=Fill。 (2) 单击TabControl属性TabPages后按钮,打开TabPage集合编辑器,单击添加按钮,增长1个选项卡页。修改属性Text分别为:第一页,第二页。如图4.9.3。 (3) 选中第一页,能够在页中放置控件,例如放置Label控件,属性Text=”这是第一个选项卡页”。一样在第二页中也放置Label控件,属性Text=”这是第二个选项卡页”。若是放置RichTextBox控件,能够作成多文档编辑器。 (4) 运行,能够看到多页,单击每页的标题,能够转换选项卡页。运行效果如右图: 图4.9.3 如能够有多个选项卡页,每选项卡页处理一个文档,并能动态增长新选项卡页,关闭当前选项卡页。实现步骤以下: (1) 新建项目。放TabControl控件到子窗体。修改属性Dock=Fill。 (2) 把Mainmenu控件放到主窗体中。增长顶级菜单项:文件,为其弹出菜单增长4个菜单项:新页、关闭当前页、打开、另存为。属性Name分别为:menuItemFile、menuItemFileNew、menuItemFileClose、menuItemFileOpen、menuItemFileSaveAs。 (3) 增长一个新方法MakeNewTbpage()以下: private object MakeNewTbpage() { //增长选项卡页TabPage TabPage tabPage1=new TabPage(); tabControl1.Controls.Add(tabPage1);//将tabPage1放到tabControl1中 tabPage1.Location=new Point(4, 21); tabPage1.Size=new Size(284, 248); tabPage1.Text="第"+tabPage1.TabIndex.ToString()+"页"; //增长RichTextBox RichTextBox richTextBox1=new RichTextBox(); richTextBox1.Dock=DockStyle.Fill; richTextBox1.Size=new Size(284, 248); richTextBox1.Text=""; tabPage1.Controls.Add(richTextBox1);//将richTextBox1放到tabPage1中 return (object)richTextBox1; } (4) 为菜单项新页增长事件处理函数以下: private void menuItemFileNew_Click(object sender, System.EventArgs e) { MakeNewTbpage();} (5) 为菜单项关闭当前页增长事件处理函数以下: private void menuItemFileClose_Click(object sender, System.EventArgs e) { TabPage tabPage1=tabControl1.SelectedTab;//获得当前选定的选项卡页 tabControl1.Controls.Remove(tabPage1);//从tabControl1中移走该页 //获得当前选定的选项卡页中第0个控件,即RichTextBox控件 RichTextBox richTextBox1=(RichTextBox)tabPage1.Controls[0]; if(richTextBox1!=null) richTextBox1.Dispose();//删除当前选定选项卡页中RichTextBox控件对象 if(tabPage1!=null) tabPage1.Dispose();//删除当前选定的选项卡页 } (6) 把OpenFileDialog控件放到子窗体中。为菜单项打开增长事件处理函数以下: private void menuItemFileOpen_Click(object sender, System.EventArgs e) { if(openFileDialog1.ShowDialog()==DialogResult.OK) { RichTextBox richTextBox1=(RichTextBox)MakeNewTbpage(); richTextBox1.LoadFile(openFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } (7) 把SaveFileDialog控件放到子窗体中。为菜单项另存为增长事件处理函数以下: private void menuItemFileSaveAs_Click(object sender, System.EventArgs e) { if(saveFileDialog1.ShowDialog()==DialogResult.OK) { TabPage tabPage1=tabControl1.SelectedTab; RichTextBox richTextBox1=(RichTextBox)tabPage1.Controls[0]; richTextBox1.SaveFile(saveFileDialog1.FileName, RichTextBoxStreamType.PlainText); } } (8) 编译,运行,创建新文件,关闭当前选项卡页,打开新文件,存文件,开是否正常。 习题 (1) RichTextBox控件Lines属性记录控件中全部文本的字符串数组,每两个回车之间字符串是数组的一个元素。定义一个数组,将属性Lines 中的内容存到这个数组中。(提示:string[] s=new string [richTextBox1.Lines.Length];s= richTextBox1.Lines) (2) 为设计的单文档写字板增长工具栏,实现建新文件,打开文件,存文件等功能。 (3) 在工具栏中,增长2个下拉列表文本框,一个选择使用的字体,一个选择字体的字号。 (4) 在工具栏中,增长3个按钮,分别设定字符为黑体,斜体,增长下划线。 (5) 在工具栏中,增长1个按钮,用ColorDialog对话框选择字体的颜色。 (6) 如何实现全选菜单项。 (7) RichTextBox控件的属性Modified能够指示用户是否修改文本框控件的内容。请修改4.7节程序,使用属性Modified判断用户是否修改了RichTextBox控件中文本内容。 (8) 在查找对话框中,增长两个多选框,选择是否容许反向查和区分大小写,并实现反向查找和不区分大小写查找。 (9) 在实现打开文件和另存为功能时,使用属性InitialDirectory和属性DefaultExt。 (10) RichTextBox控件的属性SelectionAlignment表示段落的对齐方式,在工具栏中增长三个按钮,分别实现段落的左对齐(HorizontalAlignment.Left)、右对齐(HorizontalAlignment.Right)、中间对齐(HorizontalAlignment.Center)。(提示:3个按钮应是互斥的,用分隔符表示3个按钮是一组。工具条按钮属性Style设置为ToolBarButtonStyle.Separator,则按钮将显示为一个按钮分隔符,而不是按钮。) (11) RichTextBox控件的属性SelectionCharOffset能够设定选中的字符为上下标,能够为一个整数,为0表示正常字符,负数表示下标,正数表示上标。请在工具条中增长三个按钮,分别实现上下标功能。 (12) 请实现完整的单文档编辑器,具备前边介绍的单文档编辑器的功能。 (13) 请实现完整的多文档编辑器,具备前边介绍的单文档编辑器的功能。 (14) 请实现完整的选项卡式多文档文本编辑器,具备前边介绍的单文档编辑器的功能。 第五章 图形图像编程 本章的目的是介绍图形图像编程的方法,但愿在学了本章之后,能编制象Windows画图那样的程序。本章的重点是学习Graphics类中对象(象笔、刷子等)及各类方法的使用,以及Bitmap类的应用。 5.1 图形设备环境接口(GDI) 为了在Windows窗口输出数据(字符或图形),Windows操做系统提供了一些工具和函数,例如提供笔用来定义图形外轮廓线的颜色及粗细,提供刷子定义添充封闭图形内部的颜色和格式,提供不一样输出字体,提供函数用来输出字符或绘制图形等等。全部这些工具和函数被放在图形设备接口函数库中(GDI32.DLL),它负责CRT显示及打印。根据设备不一样,能够构造不一样的设备环境(GDI),使输出图形或字符与设备无关,既不管是在CRT显示仍是在打印机上打印同一个图形或字符,都用相同的函数。GDI所扮演的角色以下图所示: 用户应用程序根据是在CRT显示仍是在打印机打印,首先选择CRT显示设备环境或打印设备环境,而后调用GDI中的同名函数实如今CRT显示或在打印机上打印。而GDI设备环境根据选择的不一样设备,调用不一样的设备驱动程序,在CRT上显示或在打印机上打印。而这些驱动程序都是各个设备制造厂商提供的。这样作的最大好处是应用程序和设备无关,应用程序没必要为不一样的设备编制不一样的程序。为使用不一样的设备,不管是不一样的显卡,仍是不一样的打印机,只要安装该设备的驱动程序,应用程序就能够使用该设备,微软的Word程序能够使用不一样的打印机就是使用了这个原理。 .NET系统的基础类库(.Net FrameWork)对Windows操做系统的图形设备接口函数库(GDI32.DLL)进行了扩充,并用类进行了封装,通常叫作GDI+。使用GDI+绘图更加方便快捷。为了使用GDI+图形功能,必须引入如下命名空间:System.Drawing,System.Drawing.Priniting,System.Drawing.Imaging,System.Drawing.Drawing2D,System.Drawing.Design,System.Drawing.Text。 5.2 Graphics类 System.Drawing.Graphics类对GDI+进行了封装,Graphics类提供一些方法完成各类图形的绘制。Graphics类对象与特定的设备关联,为了在不一样的设备上用彻底相同的代码完成一样的图形,应根据不一样的设备创建不一样的Graphics类对象。Graphics类是密封类,不能有派生类。 5.2.1 使用Graphics类绘图的基本步骤 GDI+大部分功能被封装在Graphics类中,Graphics类提供了一些工具和函数,例如提供笔用来定义图形外轮廓线的颜色及粗细,提供刷子定义添充封闭图形内部的颜色和格式,提供不一样输出字体,提供函数用来输出字符或绘制图形等等。为了在窗体中或其它控件中使用这些工具和函数绘图,必须首先获得这些窗体或控件的使用的Graphics类对象。下面的例子,在窗体中增长了一个按钮,单击按钮将在窗体中画一个边界为红色,内部填充蓝色的圆。该程序段说明了使用Graphics类绘图的基本步骤。按钮的单击事件处理函数以下: private void button1_Click(object sender,System.EventArgs e) { Graphics g=this.CreateGraphics();//获得窗体使用的Graphics类对象 Pen pen1=new Pen(Color.Red);//建立红色笔对象 SolidBrush brush1=new SolidBrush(Color.Blue);//建立蓝色刷子对象 g.DrawEllipse(pen1,10,10,100,100);//用红色笔画圆的边界 g.FillEllipse(brush1,10,10,100,100);//用蓝色刷子填充圆的内部 } 运行后,单击按钮,出现边界为红色,内部填充为蓝色的圆。 5.2.2 窗体的Paint事件 运行上例,单击按钮,出现边界为红色,内部填充蓝色的圆。最小化后,再最大化,图形不见了。这是由于用户Form窗体用户区内容可能被破坏,例如窗体最小化后,再最大化,菜单被打开再关闭,打开对话框再关闭等,用户区内容被覆盖。Windows并不保存被破坏的用户区内容,而是由应用程序本身恢复被破坏的用户区的内容。当应用程序窗口用户区内容被破坏后需恢复时,Windows操做系统向应用程序发送Paint事件,应用程序应把在窗口用户区输出数据的语句放在Paint事件处理函数中,Windows发Paint事件时,能调用这些在窗口用户区输出数据的语句恢复被破坏的内容。Form窗体不能自动响应Paint事件,程序员必须生成Paint事件处理函数。修改上例,增长Form窗体的Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics;//获得窗体的使用的Graphics类对象 Pen pen1=new Pen(Color.Red); SolidBrush brush1=new SolidBrush(Color.Blue); g.DrawEllipse(pen1,10,10,100,100); g.FillEllipse(brush1,10,10,100,100); } 运行后,出现边界为红色,内部填充蓝色的圆。最小化后,再最大化,图形不消失。 5.3 GDI+中三种坐标系统: GDI+定义了三种坐标系统,并提供了三种坐标转换的方法Graphics.TransformPoints()。  全局坐标系统。  页面(Page)坐标系统:左上角为原点,横向x轴向右为正方向,纵向y轴向下为正方向。单位为像素。这是默认的坐标系统。  设备坐标系统:能够指定特定测量单位的页面(Page)坐标系统。若是单位为像素,和页面(Page)坐标系统相同。 5.4 GDI+中经常使用的结构 本节介绍GDI+中经常使用的结构,包括:Point、PointF、Size、SizeF、Rectangle、RectangleF、Color等。它们都在名字空间System.Drawing中定义的。 5.4.1 结构Point和PointF 点结构有两个成员:X,Y,表示点的x轴和y轴的坐标。其经常使用构造函数以下: Point p1=new Point(int X,int Y);//X,Y为整数 PointF p2=new PointF(float X,floa Y);//X,Y为浮点数 5.4.2 结构Size和SizeF Size和SizeF用来表示尺寸大小,有两个成员:Width和Height。经常使用构造函数以下: public Size(int width,int height); public SizeF(float width,float height); 5.4.3 结构Rectangle和RectangleF 结构Rectangle和RectangleF用来表示一个矩形,经常使用属性以下:  Top:Rectangle结构左上角的y坐标。  Left:Rectangle结构左上角的x坐标。  Bottom:Rectangle结构右下角的y坐标。  Right:Rectangle结构右下角的x坐标。  Width:获取或设置此Rectangle结构的宽度。  Height:获取或设置此Rectangle结构的高度。  Size:获取或设置此Rectangle的大小。  X:获取或设置此Rectangle结构左上角的x坐标。  Y:获取或设置此Rectangle结构左上角的y坐标。 其经常使用构造函数为: //参数为矩形左上角坐标的点结构location和表明矩形宽和高的Size结构size Rectangle(Point location,Size size);//参数也可为PointF和SizeF //参数为矩形左上角x和y坐标,宽,高 Rectangle(int X,int Y,int width,int height);//X和Y也可为float 5.4.4 结构Color Color结构表示颜色,结构中包含一个无符号32位数表明颜色。任何一种颜色能够用透明度(al),蓝色(bb),绿色(gg),红色(rr)合成,格式为0xalrrbbgg,其中al,bb,gg,rr为0到255间的二进制数。经常使用方法以下:  public static Color FromArgb(int alpha,int rr,int gg,int bb); 从四个份量(透明度、红色、绿色和蓝色)值建立Color结构。每一个份量的值仅限于8位(小于256)。alpha值表示透明度,=0为彻底透明,=255为彻底不透明  public static Color FromArgb(int rr,int gg,int bb); 从指定的8位颜色值(红色、绿色和蓝色)建立Color结构。透明度值默认为255(彻底不透明)。每一个份量的值仅限于8位(小于256)。红色为(255,0,0),绿色为(0,255,0),蓝色为(0,0,255)。  public static Color FromArgb(int alpha,Color color); 从指定的Color结构建立新Color结构,使用新指定的透明度值alpha。alpha值仅限于8位。透明度及颜色的使用方法的例子以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; SolidBrush RedBrush=new SolidBrush(Color.FromArgb(128,255,0,0));//半透明 SolidBrush GreenBrush=new SolidBrush(Color.FromArgb(128,0,255,0)); SolidBrush BlueBrush=new SolidBrush(Color.FromArgb(128,0,0,255)); g.FillRectangle(RedBrush,0,0,80,80); g.FillRectangle(GreenBrush,40,0,80,80); g.FillRectangle(BlueBrush,20,20,80,80); } 效果如右图,能够将透明度alpha值设为255,再运行一次,看看有何不一样。C#中还预约义了一些颜色常数,例如黑色为Color.Black,红色为Color.Red等等,可用帮助察看。 5.5 画笔 Pen类对象指定绘制的图形外轮廓线宽度和颜色。Pen类有4个构造函数,分别是:  public Pen(Color color);//创建颜色为color的笔,宽度默认为1  public Pen(Color color,float width);//创建颜色为color的笔,宽度为width  public Pen(Brush brush);//使用刷子为笔  public Pen(Brush,float width);//使用刷子为笔,宽度为width Pen类经常使用的属性:Color为笔的颜色,Width为笔的宽度,DashStyle为笔的样式,EndCap和StartCap为线段终点和起点的外观。下例显示各类笔的DashStyle、EndCap和StartCap不一样选项的样式(见下图)。主窗体Paint事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; Pen pen1=new Pen(Color.Red,6);//默认为实线笔 g.DrawLine(pen1,10,10,100,10);//画实线,图中左边第1条线 pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Dash;//虚线笔 g.DrawLine(pen1,10,20,100,20);//画虚线,图中左边第2条线 pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.DashDot;//点,短线风格的线 g.DrawLine(pen1,10,30,100,30);//图中左边第3条线 //双点,短线风格的线 pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.DashDotDot; g.DrawLine(pen1,10,40,100,40);//图中左边第4条线 pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Dot;//由点组成的线 g.DrawLine(pen1,10,50,100,50);//图中左边第5条线 pen1.DashStyle=System.Drawing.Drawing2D.DashStyle.Solid;//实线笔 pen1.EndCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;//后箭头 g.DrawLine(pen1,150,10,250,10);//图中右边第1条线 pen1.StartCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;//前箭头 g.DrawLine(pen1,150,22,250,22);//图中右边第2条线 pen1.EndCap=System.Drawing.Drawing2D.LineCap.RoundAnchor; g.DrawLine(pen1,150,34,250,34);//图中右边第3条线 pen1.EndCap=System.Drawing.Drawing2D.LineCap.SquareAnchor; g.DrawLine(pen1,150,46,250,46);//图中右边第4条线 pen1.EndCap=System.Drawing.Drawing2D.LineCap.Triangle; g.DrawLine(pen1,150,58,250,58);//图中右边第5条线 pen1.EndCap=System.Drawing.Drawing2D.LineCap.DiamondAnchor; //图中右边第6条线 g.DrawLine(pen1,150,70,250,70); } 运行效果如右图: 5.6 建立画刷 画刷类对象指定填充封闭图形内部的颜色和样式,封闭图形包括矩形、椭圆、扇形、多边形和任意封闭图形。GDI+系统提供了了几个预约义画刷类,包括:  SolidBrush:单色画刷。在名字空间System.Drawing中定义。  HatchBrush:阴影画刷。如下画刷在名字空间System.Drawing.Drawing2D中定义。  TextureBrush:纹理(图像)画刷。  LinearGradientBrush:颜色渐变画刷。  PathGradientBrush:使用路径及复杂的混合渐变画刷。 5.6.1 单色画刷SolidBrush 前边已使用过单色画刷。其构造函数只有1个,定义以下: SolidBrush brush1=new SolidBrush(Color color);//创建指定颜色的画刷 在使用中能够修改其属性Color来修改其颜色,例如:brush1.Color=Color.Green; 5.6.2 阴影画刷HatchBrush 用指定样式(例如,多条横线、多条竖线、多条斜线等)、指定线条的颜色和指定背景颜色定义的画刷,阴影画刷有两个构造函数: //指定样式和线条的颜色的构造函数,背景色被初始化为黑色。 HatchBrush brush1=new HatchBrush(HatchStyle h,Color c); //指定样式、线条的颜色和背景颜色的构造函数。 HatchBrush brush1=new HatchBrush(HatchStyle h,Color c1,Color c2); 有3个属性以下:  属性backgroundColor:画刷背景颜色。  属性foreColor:画刷线条的颜色。  属性HatchStyle:该属性是只读的,不能修改,表示画刷的不一样样式。 例子5.6.2:显示了阴影画刷属性HatchStyle为不一样值时画刷的不一样样式。在Form1.cs文件头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics;//获得窗体的使用的Graphics类对象 HatchBrush b1=new HatchBrush(HatchStyle.BackwardDiagonal,Color.Blue,Color.LightGray); g.FillRectangle(b1,10,10,50,50);//矩形被填充左斜线,第1图 HatchBrush b2=new HatchBrush(HatchStyle.Cross,Color.Blue,Color.LightGray); g.FillRectangle(b2,70,10,50,50);//矩形被填充方格,第2图 HatchBrush b3=new HatchBrush(HatchStyle.ForwardDiagonal,Color.Blue,Color.LightGray); g.FillRectangle(b3,130,10,50,50);//矩形被填充右斜线,第3图 HatchBrush b4=new HatchBrush(HatchStyle.DiagonalCross,Color.Blue,Color.LightGray); g.FillRectangle(b4,190,10,50,50);//矩形被填充菱形,第4图 HatchBrush b5=new HatchBrush(HatchStyle.Vertical,Color.Blue,Color.LightGray); g.FillRectangle(b5,250,10,50,50);//矩形被填充竖线,第5图 HatchBrush b6=new HatchBrush(HatchStyle.Horizontal,Color.Blue,Color.LightGray); g.FillRectangle(b6,310,10,50,50);//矩形被填充横线,第6图 } 运行效果如右图: 5.6.3 纹理(图像)画刷TextureBrush 纹理(图像)画刷使用图像来填充封闭曲线的内部,有8个构造函数,最简单的构造函数以下,其他请用帮助查看。 TextureBrush(Image bitmap);//使用位图类对象做为画刷构造函数的参数 下边的例子使用文件n2k.bmp创建位图类对象做为画刷的图案,在Form1文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics;//获得窗体的使用的Graphics类对象 Pen pen1=new Pen(Color.Red); //位图类对象做为画刷图案,使用文件n2k.bmp创建位图类对象见5.10节 TextureBrush b1= new TextureBrush(new Bitmap("C:\\WINNT\\system32\\n2k.bmp")); g.FillRectangle(b1,10,10,200,100); g.DrawRectangle(pen1,10,10,200,100); } 文件C:\WINNT\system32\n2k.bmp定义的图形的显示效果以下: 运行效果如右图。 5.6.4 颜色渐变画刷LinearGradientBrush 该类封装双色渐变和自定义多色渐变画刷。全部颜色渐变都是沿由矩形的宽度或两个点指定的直线定义的。默认状况下,双色渐变是沿指定直线从起始色到结束色的均匀水平线性变化。有8个构造函数,最简单的构造函数以下,其他请用帮助查看。 public LinearGradientBrush( Point point1,//point1做为线性渐变直线开始点,也能够为PointF Point point2,//point2做为线性渐变直线结束点,也能够为PointF Color color1,Color color2);//线性渐变开始颜色和结束颜色 下边的例子显示了不一样线性渐变直线开始点和结束点,使用颜色渐变画刷从黄渐变到蓝的效果。在Form1文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics;//获得窗体的使用的Graphics类对象 Pen pen1=new Pen(Color.Red); Point p1=new Point(10,10);//p1做为渐变直线开始点,也能够为PointF Point p2=new Point(50,10);//p2做为渐变直线结束点,也能够为PointF LinearGradientBrush brush1=//从黄渐变到蓝,见下图左图 new LinearGradientBrush(p1,p2,Color.Yellow,Color.Blue); g.FillRectangle(brush1,10,10,200,100); g.DrawRectangle(pen1,10,10,200,100); p1=new Point(220,10); p2=new Point(270,50); LinearGradientBrush brush2=//从黄渐变到蓝,见下图右图 new LinearGradientBrush(p1,p2,Color.Yellow,Color.Blue); g.FillRectangle(brush2,230,10,200,100); g.DrawRectangle(pen1,230,10,200,100); } 运行效果以下图: 5.6.5 画刷PathGradientBrush 画刷PathGradientBrush能够实现复杂的渐变颜色。有5个构造函数,这里只介绍其中的一个,其参数GraphicsPath类使用见例子及5.7.11节。其他构造函数请用帮助查看。 public PathGradientBrush(GraphicsPath path);//画刷PathGradientBrush构造函数 画刷属性SurroundColors:一个Color结构的数组,它表示与PathGradientBrush对象填充的路径中的各点相关联的颜色的数组。SurroundColors数组中的每一Color结构都对应于路径中的点。 下边的例子介绍画刷PathGradientBrush的使用方法,在Form1.cs文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下,运行效果以下图。 private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics;//获得窗体使用的Graphics类对象 Point[] p1=new Point[6];//点结构数组,有6个元素 p1[0]=new Point(30,0); p1[1]=new Point(160,30); p1[2]=new Point(90,60); p1[3]=new Point(100,90); p1[4]=new Point(30,60); p1[5]=new Point(0,30); GraphicsPath path=new GraphicsPath(//创建GraphicsPath类对象 p1,//由点数组p1定义绘制刷子的外轮廓线的路径 new Byte[]{//定义数组p1每一个点元素的关联类型,第1点是开始点 //贝塞尔曲线必须由4点组成,所以第一、二、三、4点组成一条贝塞尔曲线, (byte)PathPointType.Start,(byte)PathPointType.Bezier, (byte)PathPointType.Bezier,(byte)PathPointType.Bezier, //第五、6点是直线,从第4点到第5点和从第5点到第6点画直线 (byte)PathPointType.Line,(byte)PathPointType.Line,}); //为了造成闭合曲线,增长最后一条直线 PathGradientBrush brush1=new PathGradientBrush(path);//生成画刷 brush1.SurroundColors=new Color[]{Color.Green,Color.Yellow,Color.Red, Color.Blue,Color.Orange,Color.OliveDrab,};//设置属性SurroundColors的值 g.FillPath(brush1,path); } 5.7 基本图形的绘制和填充 Graphics类提供了一些绘图方法,用来绘制或填充各类图形。本节介绍这些方法。 5.7.1 绘制线段 两个绘制线段的函数和一个绘制多条线段的函数定义以下:  void DrawLine(Pen pen,int x1,int y1,int x2,int y2); 其中pen为画笔,(x1,y1)为画线起点坐标,(x2,y2)为画线终点坐标。  DrawLine(Pen pen,Point p1,Point p2); 其中pen为画笔,点p1为画线起点坐标,点p2为画线终点坐标。  public void DrawLines(Pen pen,Point[] points); 此方法绘制多条线段。从points[0]到points[1]画第1条线,从points[1]到points[2]画第2条线,依此类推。 例子e5_7_1A:使用DrawLine()的例子,为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; Pen pen1=new Pen(Color.Red); g.DrawLine(pen1,30,30,100,100);//用笔pen1从点(30,30)到(100,100)画直线 Point p1=new Point(30,40); Point p2=new Point(100,110); g.DrawLine(pen1,p1,p2);//用笔pen1从点(30,40)到(100,110)画直线 } 例子e5_7_1B:使用绘制线段函数画任意曲线(画正弦曲线,注意如何使用数学函数)。 private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics();//获得窗体的使用的Graphics类对象 Pen pen1=new Pen(Color.Red); float y=50,y1,x1,x2; for(int x=0;x<720;x++)//画正弦曲线 { x1=(float)x; x2=(float)(x+1); y1=(float)(50+50*Math.Sin((3.14159/180.0)*(x+1))); g.DrawLine(pen1,x1,y,x2,y1); y=y1; } } 运行,在窗体中能够看到一条红色正弦曲线以下图。 例子e5_7_1C:在画图程序中,能够用鼠标画任意曲线,现实现用拖动鼠标左键在主窗体中画曲线。每条曲线都是由若干很短的线段组成。鼠标左键按下状态下,移动鼠标,每移动很短距离,画出这段线段,全部这些线段组合起来,造成一条曲线。 (1) 新建项目。增长两个私有变量: private bool mark=false;//表示鼠标左键是否按下,如按下鼠标再移动将画曲线 private Point point;//记录画下一很短线段的起始点。 (2) 为Form窗体的事件OnMouseDown,OnMouseUp,OnMouseMove增长事件函数以下: private void Form11_MouseDown(object sender,//鼠标按下事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(e.Button==MouseButtons.Left)//若是鼠标左键按下 { point.X=e.X;//记录曲线的第一个点的坐标 point.Y=e.Y; mark=true;//表示鼠标左键已按下,鼠标若是再移动,将画曲线 } } private void Form1_MouseMove(object sender,//鼠标移动事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(mark)//若是鼠标左键按下 { Graphics g=this.CreateGraphics();//获得窗体的使用的Graphics类对象 Pen pen1=new Pen(Color.Black);//黑笔 g.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//画线 point.X=e.X;//记录画下一线段的起始点的坐标 point.Y=e.Y; } } private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { mark=false;}//中止画线 (3) 运行,在Form窗体拖动鼠标左键能够画线。但最小化后再最大化后,图形消失。修改上例,使其能克服这个缺点。实现的思路是记录每一条曲线的每一条很短线段的坐标。使用ArrayList类对象记录曲线以及曲线中的点,请注意ArrayList类使用方法。 (4) 为定义主窗体的Form1类中增长私有变量: private ArrayList Point_List;//用来记录1条曲线的全部点。 private ArrayList Line_List;//用来记录每条曲线,既Point_List对象。 在Form1类构造函数中增长语句:Line_List=new ArrayList(); (5) 修改主窗体事件OnMouseDown,OnMouseUp,OnMouseMove事件处理函数以下: private void Form1_MouseDown(object sender,//阴影部分为修改的内容 System.Windows.Forms.MouseEventArgs e) { if(e.Button==MouseButtons.Left) { Point_List=new ArrayList();//创建数组,记录1条曲线的全部点 point.X=e.X; point.Y=e.Y; mark=true; Point_List.Add(point);//曲线起点的坐标 } } private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { if(mark) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Black); g.DrawLine(pen1,point.X,point.Y,e.X,e.Y); point.X=e.X; point.Y=e.Y; Point_List.Add(point);//记录曲线中其它点的坐标 } } private void Form1_MouseUp(object sender,System.Windows.Forms.MouseEventArgs e) { mark=false; Line_List.Add(Point_List);//记录此条线,注意参数是Point_List } (6) 增长Form窗体的Paint事件函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; Pen pen1=new Pen(Color.Black); Point p1,p2; foreach(ArrayList l in Line_List)//取出每条线 { for(int k=0;k<(l.Count-1);k++)//重画每条线的点 { p1=(Point)l[k]; p2=(Point)l[k+1]; g.DrawLine(pen1,p1,p2); } } } (7) 运行,在Form窗体拖动鼠标能够画线。最小化后再最大化后,图形不消失。 5.7.2 ArrayList类 ArrayList类是容量能够动态增长的数组,其元素类型能够是任意类型。和其它数组同样,ArrayList类能够使用对象名[索引号]引用其元素,索引号也从零开始。前边已屡次使用此类,例如:控件ListBox和ComboBox的属性Items,以及5.7.1节中的例子。其经常使用的属性及方法以下:  属性Count:ArrayList中实际包含的元素数。  方法Add:将参数指定的对象添加到ArrayList对象的结尾处。  方法Clear:从ArrayList中移除全部元素。  方法Contains:bool类型,肯定参数指定的元素是否在ArrayList中。  方法IndexOf:int类型,顺序查找和参数指定对象相同的第一个元素的索引。  方法Insert:插入数据,第1个参数为插入的位置(索引号),第2个参数为插入的对象。  方法LastIndexOf:顺序查找和参数指定对象相同的最后一个元素的索引。  方法RemoveAt:移除指定索引处的元素。  方法Sort:对整个ArrayList中的元素进行排序。 5.7.3 画椭圆(圆)及键盘消息的使用 两个画椭圆的函数的功能是画指定矩形的内切椭圆,如为正方形则画圆,两个函数以下:  void DrawEllipse(Pen pen,int x,int y,int width,int height); 其中pen为画笔,画外轮廓线,(x1,y1)为指定矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。  void DrawEllipse(Pen pen,Rectangle rect); 其中pen为画笔,画外轮廓线,rect为指定矩形结构对象。 例子5_7_3A:画椭圆。为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); g.DrawEllipse(pen1,10,10,200,100); Rectangle rect=new Rectangle(20,20,100,100); g.DrawEllipse(pen1,rect); } 例子5_7_3B:用四个箭头键移动窗体中的圆球。移动圆球,实际是先把前边画的圆擦掉,在新的位置从新画圆。如要擦掉圆,能够用窗体背景色做为笔和刷子的颜色,在圆的原先位置重画和填充圆。注意键盘事件处理函数的使用。具体实现步骤以下: (1) 新建项目。在Form1类中增长变量:int x,y,记录定义圆位置的矩形左上角的坐标。 (2) 在Form1类中增长一个方法,该方法按照参数指定颜色画圆,方法定义以下: void DrawCir(Color color)//参数是画圆的笔和刷子的颜色 { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(color); SolidBrush brush1=new SolidBrush(color); g.DrawEllipse(pen1,x,y,100,100); g.FillEllipse(brush1,x,y,100,100); } (3) 为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { DrawCir(Color.Red);} (4) 为主窗体KeyDown事件增长事件函数以下:(注意不要使用KeyPress事件,其事件处理函数的第2个参数e的e.KeyChar是按下键的ASCII值,但不少键无ASCII值。) private void Form1_KeyDown(object sender,System.Windows.Forms.KeyEventArgs e) { switch (e.KeyCode)//e.KeyCode是键盘每一个键的编号 { case Keys.Left://左箭头键编号 DrawCir(this.BackColor);//用Form窗体的背静色画圆,即擦除圆 x=x-10;//圆左移 DrawCir(Color.Red);//在新的位置用红色画圆,效果是圆左移 break; case Keys.Right://圆右移 DrawCir(this.BackColor); x+=10; DrawCir(Color.Red); break; case Keys.Down://圆下移 DrawCir(this.BackColor); y+=10; DrawCir(Color.Red); break; case Keys.Up://圆上移 DrawCir(this.BackColor); y=y-10; DrawCir(Color.Red); break; } } (5) 运行,能够用4个箭头键移动红色圆。 使用KeyDown事件,事件处理函数的第2个参数e的e.KeyCode是键盘每一个键的编号,其它经常使用键的编号以下:数字键0-9编号为Keys.D0-Keys.D9;字母键A-Z为Keys.A-Keys.Z;F0-F12键表示为Keys.F0-Keys.F12等。 如使用KeyPress事件,事件处理函数的第2个参数e的e.KeyChar表示按键的ACSII值,例如可用以下语句if(e.KeyChar==(char)13)判断是否按了回车键。 5.7.4 画矩形 两个绘制1个矩形(正方形)的函数和一个绘制多个矩形(正方形)的函数定义以下:  void DrawRectangle(Pen pen,int x,int y,int width,int height); 其中pen为画笔,画外轮廓线,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。  void DrawRectangle(Pen pen,Rectangle rect); 其中pen为画笔,画外轮廓线,rect为矩形结构对象。  public void DrawRectangles(Pen pen,Rectangle[] rects); 绘制一系列由Rectangle结构指定的矩形。 例子5_7_3:画矩形。为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); g.DrawRectangle(pen1,10,10,200,100); Rectangle rect=new Rectangle(20,20,100,100); g.DrawRectangle(pen1,rect); } 5.7.5 绘制圆弧 DrawArc方法绘制指定矩形的内切椭圆(圆)中的一段圆弧,方法定义以下: void DrawArc(Pen pen,int x,int y,int width,int height,int StartAngle,int EndAngle); 其中pen为画笔,画外轮廓线,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。StartAngle为圆弧的起始角度,EndAngle为圆弧的结束角度,单位为度。指定矩形的中心点作矩形宽和高的的垂线做为x,y轴,中心点为圆点。圆点右侧x轴为0度,顺时针旋转为正角度,逆时针旋转为负角度。 例子5_7_4:画圆弧。为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); g.DrawArc(pen1,10,10,200,100,0,30); } 5.7.6 DrawPie方法 DrawPie方法方法绘制指定矩形的内切椭圆(圆)中的一段圆弧,而且用指定矩形的中心点链接开始点和结束点,这个图形叫作饼图,方法定义以下: void DrawPie(Pen pen,int x,int y,int width,int height,int StartAngle,int EndAngle); 方法参数和DrawArc方法参数相同。 例子5_7_5:画饼图。为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); g.DrawPie(pen1,10,10,200,100,0,30); } 5.7.7 Bezier曲线 能够使用DrawBezier方法画一条Bezier曲线。它的两个画线函数定义以下:  Void DrawBezier(Pen pen,float x1,float y1, float x2,float y2,float x3,float y3,float x4,float y4); 其中pen是画笔对象,画轮廓线,(x1,y1)是起始点,(x2,y2)是第一控制点,(x3,y3)是第二控制点,(x4,y4)是结束点。  Void DrawBezier(Pen pen,Point p1,Point p2,Point p3,Point P4); 其中pen是画笔对象,画轮廓线,p1是起始点,p2是第一控制点,p3是第二控制点,p4是结束点。 例子5_7_6:画Bezier曲线。为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); g.DrawBezier(pen1,10,10,200,100,50,60,100,200); } 5.7.8 DrawPolygon方法 该方法画一个多边形,使用点结构数组定义多边形的顶点。两个画线函数定义以下:  void DrawPolygon(Pen pen,Point[] points);  void DrawPolygon(Pen pen,PointF[] points);//点坐标能够是小数 例子5_7_7:画一个多边形以下图,为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); Point[] p1=new Point[] { new Point(10,10), new Point(60,40), new Point(100,80), new Point(60,100) }; g.DrawPolygon(pen1,p1); } 5.7.9 DrawClosedCurve方法 DrawClosedCurve方法用来绘制通过Point结构数组中每一个点的闭合基数样条。基数样条是一连串单独的曲线,这些曲线链接起来造成一条较大的曲线。样条由点的数组指定,并经过该数组中的每个点。基数样条平滑地经过数组中的每个点,请比较一下本节的图形和上节图形的区别。若是最后一个点不匹配第一个点,则在最后一个点和第一个点之间添加一条附加曲线段以使该图闭合,点Point结构数组必须至少包含四个元素,此方法使用默认张力0.5。有4个画线函数,经常使用的2个画线函数定义以下:  void DrawClosedCurve(Pen pen,Point[] points);  void DrawClosedCurve(Pen pen,PointF[] points);//点坐标能够是小数 例子e5_7_9:使用DrawClosedCurve方法,绘制有4个元素的Point结构数组定义的闭合基数样条闭合曲线以下图,为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red); Point[] p1=new Point[] { new Point(10,10), new Point(60,40), new Point(100,80), new Point(60,100) }; g.DrawClosedCurve(pen1,p1); } 5.7.10 DrawCurve方法 用DrawCurve方法和DrawClosedCurve方法同样,用来绘制通过Point结构数组中每一个点的闭合基数样条,但最后两个点之间不连线。经常使用的两个画线函数定义以下:  void DrawPolygon(Pen pen,Point[] points);  void DrawPolygon(Pen pen,PointF[] points); 例子5_7_9:使用DrawCurve方法,绘制有4个元素的Point结构数组定义的闭合基数样条闭合曲线以下图,为主窗体Paint事件增长事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); Pen pen1=new Pen(Color.Red,3); Point[] p1=new Point[] { new Point(10,10), new Point(60,40), new Point(100,80), new Point(60,100) }; g.DrawCurve(pen1,p1); } 5.7.11 DrawPath方法和GraphicsPath类 用DrawPath方法能够绘制多个曲线,方法参数GraphicsPath类对象path定义每一个曲线类型。DrawPath方法定义以下: void DrawPath(Pen pen,GraphicsPath path); 例子5_7_10A:用DrawPath方法画一个矩形和其内切椭圆以下图,在Form1.cs文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Rectangle myEllipse=new Rectangle(20,20,100,50); GraphicsPath myPath=new GraphicsPath();//创建GraphicsPath()类对象 myPath.AddEllipse(myEllipse);//追加一椭圆 myPath.Add Rectangle(myEllipse);//追加一矩形 Pen myPen=new Pen(Color.Black,2); //画这条曲线,即椭圆和矩形。 e.Graphics.DrawPath(myPen,myPath); } GraphicsPath类是表示一系列相互链接的直线和曲线的路径,应用程序使用此路径来绘制曲线的轮廓、填充形状内部和建立剪辑区域。由属性PathPoints(点数组)定义绘制直线和曲线的路径。路径可由任意数目的图形(子路径)组成,每一图形都是由一系列相互链接的直线和曲线或几何形状基元构成的。由属性PathTypes(字节数组)定义属性PathPoints(点数组)中每一个点元素的关联图形或曲线类型,图形的起始点是相互链接的一系列直线和曲线中的第一点。终结点是该序列中的最后一点。GraphicsPath在System.Drawing.Drawing2D名字空间。经常使用属性、事件和方法定义以下:  构造函数GraphicsPath();//创建空对象  构造函数public GraphicsPath(Point[] pts,byte[] types); 参数pts为Point结构数组,数组元素表示构造路径所使用的点。参数types指定路径中相应点的关联图形或曲线类型数组。参见5.6.5节。  属性PointCount:获取 PathPoints 或 PathTypes 数组中的元素数。  方法IsVisible:指定点是否包含在此 GraphicsPath 对象内。  方法:void AddArc(Rectangle rect,float startAngle,float sweepAngle); 在表明要描绘图形的GraphicsPath类对象中追加一段椭圆弧。  方法:void AddEllipse(Rectangle rect),追加一椭圆(圆)  方法:void AddLine(Point pt1,Point pt2),追加一线段  方法:void AddRectangle(Rectangle rect),追加一矩形 还有其它增长曲线的方法,例如:AddBezier 方法、AddBeziers 方法、AddClosedCurve 方法、AddCurve 方法、AddLines 方法、AddPath 方法、AddPie 方法、AddPolygon 方法、AddRectangles 方法、AddString 方法等,请用帮助查看。 5.7.12 DrawString方法 DrawString方法在指定位置而且用指定的Brush和Font对象绘制指定的文本字符串。有6个重载方法,经常使用的一个是: public void DrawString(string s,//s是为要显示的字符串 Font font,//显示的字符串使用的字体 Brush brush,//用刷子写字符串 PointF point);//显示的字符串左上角的坐标 最后一个参数也能够是RectangleF对象,仍表示显示的字符串位置。还能够再增长一个参数,即第5个参数,StringFormat对象,它指定应用于所绘制文本的格式化属性(如行距和对齐方式)。在打印和打印预览一节已使用了这个方法。 例子5_7_12:用DrawString方法显示字符串,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { String drawString="Sample Text";//要显示的字符串 Font drawFont=new Font("Arial",16);//显示的字符串使用的字体 SolidBrush drawBrush=new SolidBrush(Color.Black);//写字符串用的刷子 PointF drawPoint=new PointF(20.0F,20.0F);//显示的字符串左上角的坐标 e.Graphics.DrawString(drawString,drawFont,drawBrush,drawPoint); } 5.7.13 DrawImage和DrawIcon方法 用来在指定的位置绘制指定的Image对象和图标。Graphics类中有多个DrawImage重载方法,最简单的是如下方法:  public void DrawImage(Image image,Point point); 在指定的位置使用原始物理大小绘制指定的Image对象。参数1为要绘制的Image对象,参数2表示所绘制图像的左上角在窗体中的位置。  public void DrawImage(Image image,Point[] destPoints); 在指定位置而且按指定形状和大小绘制指定的Image对象。参数1为要绘制的Image对象,参数2表示有3个元素的Point结构数组,三个点定义一个平行四边形。缩放和剪切image参数表示的图像,以在此平行四边形内显示。参数2也能够是一个矩形结构。  public void DrawImage(Image image,//要绘制的Image对象 Rectangle destRect,//指定所绘制图像的位置和大小,图像进行缩放以适合该矩形 Rectangle srcRect,//指定image对象中要绘制的部分 GraphicsUnit srcUnit);//枚举的成员,指定srcRect参数所用的度量单位 例子5_7_1A:在指定位置绘制Image对象指定部分。 private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Image newImage=Image.FromFile("d:\\CSARP\\1.jpg");//创建要绘制的Image图像 Rectangle destRect=new Rectangle(10,10,150,150);//显示图像的位置 Rectangle srcRect=new Rectangle(50,50,150,150);//显示图像那一部分 GraphicsUnit units=GraphicsUnit.Pixel;//源矩形的度量单位设置为像素 e.Graphics.DrawImage(newImage,destRect,srcRect,units);//显示 }//若是把显示图像的位置变宽,看一下效果,为何?其它重载方法可用帮助查看。  public void DrawIcon(Icon icon,Rectangle targetRect); 在Rectangle结构指定的区域内绘制指定的Icon对象表示的图标。 例子5_7_1B:在指定位置画图标。 private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Icon newIcon=new Icon("d:\\CSARP\\TASKS.ICO"); Rectangle rect=new Rectangle(100,100,200,200); e.Graphics.DrawIcon(newIcon,rect); } 5.7.14 FillEllipse方法 该方法用指定画刷来填充指定矩形的内切椭圆(圆)。两个填充函数的定义以下:  void FillEllipse(Brush brush,int x,int y,int width,int height); 其中brush为指定画刷,(x1,y1)为指定矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。  void DrawEllipse(Pen pen,Rectangle rect); 其中brush为指定画刷,rect为指定矩形结构对象。 例子5_7_13:用指定画刷来填充指定矩形的内切椭圆。 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); SolidBrush brush=new SolidBrush(Color.Blue); g.FillEllipse(brush,10,10,200,100); Rectangle rect=new Rectangle(120,120,100,100); g.FillEllipse(brush,rect); } 5.7.15 FillRectangle方法 FillRectangle方法用指定画刷来填充指定矩形。两个填充函数定义以下:  void FillRectangle(Brush brush,int x,int y,int width,int height); 其中brush为指定画刷,(x1,y1)为矩形的左上角坐标,width为指定矩形的宽,height为指定矩形的高。  void FillRectangle(Brush brush,Rectangle rect); 其中brush为指定画刷,rect为矩形结构对象。 例子5_7_14:用指定画刷来填充指定矩形。 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); SolidBrush brush=new SolidBrush(Color.Blue); g.FillRectangle(brush,10,10,200,100); Rectangle rect=new Rectangle(120,120,100,100); g.FillRectangle(brush,rect); } 5.7.16 FillPie方法 FillPie方法用指定画刷来填充指定饼图。函数定义以下: void FillPie(Brush brush,int x,int y,int width, int height,int StartAngle,int EndAngle); 其中brush为指定画刷,方法其它参数和DrawArc方法参数相同。 例子5_7_15:用指定画刷来填充指定饼图。 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g=this.CreateGraphics(); SolidBrush brush=new SolidBrush(Color.Blue); g.FillPie(brush,10,10,200,100,0,30); } 5.7.17 FillRegion方法和Region类 FillRegion方法用刷子填充区域Region类对象内部。Region类对象由矩形和路径构成。若是区域不闭合,则在最后一个点和第一个点之间添加一条额外的线段来将其闭合。方法定义以下: public void FillRegion(Brush brush,Region region); 第1个参数是填充使用的刷子,第2个参数是指定的区域。 例子5.7.16A:用纯蓝色刷子,使用FillRegion方法填充一个矩形区域。 private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { SolidBrush blueBrush=new SolidBrush(Color.Blue); Rectangle fillRect=new Rectangle(10,10,100,100); Region fillRegion=new Region(fillRect); e.Graphics.FillRegion(blueBrush,fillRegion); } 区域是输出设备显示区域的一部分。区域能够是简单的(单个矩形)或复杂的(多边形和闭合曲线的组合)。下图中的左数第1图显示了两个区域:一个利用矩形构造,另外一个利用路径构造。能够经过合并现有的区域来建立复杂区域。Region类提供了如下合并区域的方法:Intersect、Union、Xor、Exclude和Complement。两个区域的交集是同时属于两个区域的全部点的集合,方法Intersect能够获得两个Region类对象的交集。并集是多个区域的全部点的集合,方法Union能够获得两个Region类对象的并集。方法Xor能够获得两个Region类对象的并集减去这二者的交集,即下图中的左数第4图显示的蓝色区域。方法Exclude和Complement能够获得1个Region类对象和参数指定的Region类对象的不相交的部分,即下图中的左数第5图显示区域。 Region类经常使用的方法以下:  构造函数Region:能够没有参数,即建立一个空区域。也能够有一个参数,能够是GraphicsPath、Rectangle、RectangleF和RegionData类型,由今生成一个区域。  方法Exclude和Complement:获得1个Region类对象和参数指定的Region类对象的不相交的部分。参数能够是GraphicsPath、Rectangle、RectangleF和Region类型。  方法Equals:比较2个区域是否相等。参数1是要比较的区域,参数2是要绘制表面的Graphics对象。  方法Intersect:能够获得两个Region类对象的交集。参数能够是GraphicsPath、Rectangle、RectangleF和Region类型。  方法IsEmpty:测试是否为空区域。  方法IsVisible:测试参数指定的点或矩形是否在区域中。  方法Union:能够获得两个Region类对象的并集。  方法Xor:能够获得两个Region类对象的并集减去这二者的交集。 例子5_7_16B:创建2个矩形,有部分相交,将相交部分填充为蓝色。在Form1.cs文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { Graphics g=e.Graphics; Rectangle regionRect=new Rectangle(10,10,50,50); Pen pen1=new Pen(Color.Black); g.DrawRectangle(pen1,regionRect);//绘制第1个矩形 RectangleF unionRect=new RectangleF(25,25,50,50);//第2个矩形 pen1.Color=Color.Red; g.DrawEllipse(pen1,unionRect);//画椭圆 GraphicsPath myPath=new GraphicsPath();//创建GraphicsPath()类对象 myPath.AddEllipse(unionRect);//追加一椭圆 Region myRegion=new Region(regionRect);//创建表示第1个矩形的区域 myRegion.Intersect(myPath);//获得两个区域的交集 SolidBrush myBrush=new SolidBrush(Color.Blue); e.Graphics.FillRegion(myBrush,myRegion);//填充区域 } 运行效果如右图。除了以上介绍的填充方法,还有以下方法:FillClosedCurve方法、FillPath方法、FillPolygon方法等,请用帮助查看。 5.8 Matrix类和图形的平移、变形、旋转 本节介绍使用Matrix类实现图形的平移、变形、旋转。 5.8.1 Matrix类 Matrix类封装了表示几何变形的3行3列仿射矩阵,能够记录图形的平移、变形、旋转等操做。主要包括以下方法:  构造函数Matrix():建立一个空Matrix类对象。  方法Rotate:在Matrix类对象中增长相对于原点顺时针旋转指定角度的操做。参数指定旋转角度。  方法RotateAt:在Matrix类对象中增长相对于指定点顺时针旋转指定角度的操做。参数1指定旋转角度。参数2指定相应的点。  方法Scale:在X轴或Y轴方向对图形放大或缩小。参数1指定在X轴方向缩放的值,参数2指定在Y轴方向缩放的值。  方法Translate:使图形在X轴或Y轴方向移动。参数1指定在X轴方向移动的值,参数2指定在Y轴方向移动的值。 例子5_8_1:下面的示例建立了复合变形(先旋转30度,再在y方向上缩放2倍,而后在x方向平移5个单位)的Matrix类对象。注意变形得顺序很是重要。通常说来,先旋转、再缩放、而后平移,与先缩放、再旋转、而后平移是不一样的。 Matrix myMatrix=new Matrix(); myMatrix.Rotate(30); myMatrix.Scale(1,2,MatrixOrder.Append); myMatrix.Translate(5,0,MatrixOrder.Append); 5.8.2 图形的平移、变形、旋转 GraphicsPath类的Transform方法能够缩放、转换、旋转或扭曲GraphicsPath对象,参数Matrix对象表示须要的变形。 例子5_8_2A:下面的示例代码执行下列操做:建立一个路径并向该路径添加一个椭圆。将路径绘制到主窗体上。建立一个Matrix类对象,在对象中增长在X轴方向上将路径移动100个单位操做。将该已变形的路径绘制到屏幕。观察一下变换前和变换后的不一样,注意,初始椭圆是以黑色绘制的,而变形后的椭圆是以红色绘制的。在Form1.cs文件的头部增长语句:using System.Drawing.Drawing2D,主窗体Paint事件处理函数以下: private void Form1_Paint(object sender,System.Windows.Forms.PaintEventArgs e) { GraphicsPath myPath=new GraphicsPath();//建立一个路径 myPath.AddEllipse(0,0,50,70);//向路径添加一个椭圆 e.Graphics.DrawPath(Pens.Black,myPath);//用黑笔画出这个椭圆 Matrix translateMatrix=new Matrix();//建立一个Matrix类对象 translateMatrix.Translate(25,0);//在X轴方向上移动25个单位 //根据Matrix类对象修改路径myPath myPath.Transform(translateMatrix); //用红笔按新路径画这个椭圆 e.Graphics.DrawPath(new Pen(Color.Red,2),myPath); } 运行效果如右图。请读者实现变形、旋转。 5.8.3 仿射矩阵 m×n矩阵是以m行和n列排列的一组数字,例如一个3×3矩阵记为以下图形式,也可简记为:[a33]。 两个行、列分别相同的矩阵能够相加,例如:[a33]+[b33]=[c33],矩阵相加运算的规则是:ci j=ai j+bi j,i和j为常量,即相对应位置的项相加。若是有矩阵[am n]和[bn k],[am n]矩阵的列数等于[bn k]矩阵的行数,两个矩阵能够相乘,记为:[am n]*[bn k]=[cm k],矩阵相乘的运算的规则是:ci j=∑(ai t+bt j),其中,i和j为常量,t为变量,初始值为1,最大值为n。 若是将平面中的点视为1×2矩阵,则可经过将该点乘以2×2变换矩阵来变形该点。下图是点(2,1)在X轴按比例3放大,Y轴不变。 下图表示点(2,1)旋转了90度。 下图表示点(2,1)以x轴为对称轴的新点。 假定要从点(2,1)开始,将其旋转90度,在x方向将其平移3个单位,在y方向将其平移4个单位。可经过先使用矩阵乘法再使用矩阵加法来完成此操做。 若是用矩阵[2 1 1]表明点(2,1),使用一个3×3变换矩阵,能够用一个矩阵乘法代替以上的两个矩阵运算,见下图: 注意运运结果的矩阵[2 6 1]表明点(2,6),即点(2,1)映射到了点(2,6)。这个3×3矩阵叫做仿射矩阵,Matrix类中用这个仿射矩阵记录增长的各类变换操做。它和前边的两个2×2矩阵的关系以下图,其中第三列固定为0、0、1。 Matrix类增长了一些方法处理这个仿射矩阵,主要包括:逆转方法Invert、相乘方法Multiply、重置为单位矩阵方法Reset等。 5.9 图形文件格式 在磁盘中存储图形和图像的文件格式有多种。GDI+支持如下图形文件格式。  位图文件(.bmp): 位图文件是Windows使用的一种标准格式,用于存储设备无关和应用程序无关的图像。BMP文件一般不压缩,所以不太适合Internet传输。  可交换图像文件格式(.gif): GIF是一种用于在Web页中显示图像的通用格式。GIF文件是压缩的,可是在压缩过程当中没有信息丢失,解压缩的图像与原始图像彻底同样。GIF文件中的一种颜色能够被指定为透明,这样,图像将具备显示它的任何Web页的背景色。在单个文件中存储一系列GIF图像能够造成一个动画GIF。GIF文件每一个像素颜色最多用8位表示,因此它们只限于使用256种颜色。  JPG文件(.jpg): JPEG是联合摄影专家组提出的一种适应于天然景观(如扫描的照片)的压缩方案。一些信息会在压缩过程当中丢失,可是这些丢失人眼是察觉不到的。JPEG文件每像素颜色用24位表示,所以可以显示超过16,000,000种颜色。JPEG文件不支持透明或动画。JPEG图像中的压缩级别是能够控制的,可是较高的压缩级别(较小的文件)会致使丢失更多的信息。对于一幅以20:1压缩比生成的图像,人眼难以把它和原始图像区别开来。JPEG是一种压缩方案,不是一种文件格式。“JPEG文件交换格式(JFIF)”是一种文件格式,经常使用于存储和传输根据JPEG方案压缩的图像。Web浏览器显示的JFIF文件使用.jpg扩展名。  可移植网络图形(.PNG) PNG格式不但保留了许多GIF格式的优势,还提供了超出GIF的功能。像GIF文件同样,PNG文件在压缩时也不损失信息。PNG文件能以每像素八、24或48位来存储颜色,并以每像素一、二、四、8或16位来存储灰度。相比之下,GIF文件只能使用每像素一、二、4或8位。PNG文件还可为每一个像素存储一个透明度alpha值,该值指定了该像素颜色与背景颜色混合的程度。PNG优于GIF之处在于它可以逐渐显示一幅图像,也就是说,当图像经过网络链接到达时显示将愈来愈近似。PNG文件可包含伽玛校订和颜色校订信息,以便图像可在各类各样的显示设备上精确地呈现。  图元文件(.emf): GDI+提供Metafile类,以便可以记录和显示图元文件。图元文件,也称为矢量图像,是一种存储为一系列绘图命令和设置的图像。Metafile对象记录的命令和设置能够存储在内存中或保存到文件或流。下面示例在主窗体显示了一个图元文件的图形。 Graphics g=this.CreateGraphics(); Metafile myMetafile=new Metafile("SampleMetafile.emf"); myGraphics.DrawImage(myMetafile,10,10);//图形左上角的位置是(10,10)  支持的文件格式还有:图标文件(.ico)、.EXIF、.TIFF、.ICON、.WMF等 5.10 图形框PictureBox控件 PictureBox控件经常使用于图形设计和图像处理程序,又称为图形框,该控件可显示和处理的图像文件格式有:位图文件(.bmp)、图标文件(.ico)、GIF文件(.gif)和JPG文件(.jpg)。其经常使用的属性、事件和方法以下:  属性Image:指定要显示的图像,通常为Bitmap类对象。  属性SizeMode:指定如何显示图像,枚举类型,默认为Normal,图形框和要显示的图像左上角重合,只显示图形框相同大小部分,其他不显示;为CentreImage,将图像放在图形框中间,四周多余部分不显示;为StretchImage,调整图像大小使之适合图片框。  方法CreateGraphics():创建Graphics对象。  方法Invalidate():要求控件对参数指定区域重画,如无参数,为整个区域。  方法Update():方法Invalidate()并不能使控件当即重画指定区域,只有使用Update()方法才能当即重画指定区域。使用见5.10.4节中的鼠标移动事件处理函数。 例子e5_10:使用PictureBox控件显示图像 (1) 新建项目。放PictureBox控件到窗体。属性Name=pictureBox1。 (2) 放Button控件到窗体。属性Name=button1。 (3) 放OpenFileDialog控件到窗体。属性Name=openFileDialog1。 (4) 能够在设计阶段修改属性Image为指定图形文件,设定初始显示的图像。 (5) button1控件事件处理函数以下: private void button1_Click(object sender, System.EventArgs e) { if(openFileDialog1.ShowDialog()==DialogResult.OK) { Bitmap p1=new Bitmap(openFileDialog1.FileName);//Bitmap类见下节 pictureBox1.Image=p1; } } 5.11 Bitmap类 System.Drawing命名空间有一个类Image,用来处理图像。Image类的派生类Bitmap类封装了GDI+中的位图,能够处理由像素数据定义的图像。Image类的派生类metafile处理元文件,此类文件用记录绘图命令的方法存储图像。 5.11.1 Bitmap类支持的图像类型 使用Bitmap类能够显示和处理多种图像文件,可处理的文件类型及文件扩展名以下:扩展名为.bmp的位图文件、扩展名为.ico的图标文件、扩展名为.gif的GIF文件、扩展名为.jpg的JPG文件。当使用构造函数Bitmap(string FileName)创建Bitmap类对象时,若是文件是以上类型,将自动转换为位图格式存到Bitmap类对象中。可以使用Bitmap类方法Save(string FileName,ImageFormat imageFormat)把Bitmap类对象中的位图存到文件中,其中第1个参数是选定的文件名,第2个参数是指定文件存为那种类型,能够是以下类型:System.Drawing.Imaging.ImageFormat.bmp(或.ico、.gif、.jpg)。 5.11.2 Bitmap类的方法  方法SetPixel():画点方法,前2个参数是指定点的位置,第3个参数是颜色值。  方法GetPixle():获得指定点的颜色,2个参数是指定点的位置,返回颜色值。  有多个构造函数例如:new Bitmap(”图像文件名”),new Bitmap(宽,高)等。  方法Save():第1个参数是文件名,第2个参数是指定文件存为那种类型,能够是以下类型:System.Drawing.Imaging.ImageFormat.bmp(或.ico、.gif、.jpg)。  方法Dispose():释放位图对象 5.11.3 画点 例子e5_11_3:用SetPixel画点,GetPixle获得指定点的颜色。放Button和PictureBox控件到主窗体。增长Button控件单击事件函数以下: private void button1_Click(object sender,System.EventArgs e) { pictureBox1.Width=720;//设定pictureBox1的宽和高 pictureBox1.Height=110; Bitmap bits=new Bitmap(720,110);//创建位图对象,宽=720,高=110 int x,y; for(x=0;x<720;x++)//画正弦曲线 { y=(int)(50+50*Math.Sin((3.14159/180.0)*x)); bits.SetPixel(x,y,Color.Red); } pictureBox1.Image=bits;//位图对象在pictureBox1中显示 Color c1=bits.GetPixel(20,20); string s="R="+c1.R+",G="+c1.B+",G+"+c1.G; MessageBox.Show(s); } 5.11.4 在PictureBox中画任意曲线 例子e5_11_4:例子5_7_1C实现了画任意曲线程序,用ArrayList类对象记录绘制的曲线。在该程序中增长橡皮功能、图像的拷贝、图像的剪贴、图像的粘贴比较困难,也不能和画图程序交换文件。为了实现这些功能,用图形框(PictureBox控件)显示绘制图形。绘制图形必须存在图形框属性Image引用的位图对象中,图形框显示的图像被破坏,图形框响应Paint事件,将用其属性Image引用的位图对象恢复所绘制的图形。仅将图形绘制在图形框表面,图形框响应Paint事件,绘制的图形将不能被恢复,也就是说,绘制在图形框表面的图形丢失了。具体实现步骤以下: (1) 新建项目。增长4个私有变量:private bool mark=false; private Point point; private Bitmap bits; private Graphics bitG; (2) 放PictureBox控件到窗体,修改属性Dock=Fill。 (3) 在构造函数中增长语句: //创建位图对象,宽和高为指定值 bits=new Bitmap(pictureBox1.Width,pictureBox1.Height); bitG=Graphics.FromImage(bits);//获得位图对象的Graphics类的对象 bitG.Clear(Color.White);//用白色清除位图对象中的图像 pictureBox1.Image=bits;//位图对象在pictureBox1中显示 (4) 为控件PictureBox事件OnMouseDown,OnMouseUp,OnMouseMove增长事件处理函数: private void pictureBox1_MouseDown(object sender,//鼠标按下事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(e.Button==MouseButtons.Left)//是不是鼠标左键按下 { point.X=e.X; point.Y=e.Y;//画线段开始点 mark=true;//鼠标左键按下标识 } } private void pictureBox1_MouseMove(object sender,//鼠标移动事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(mark)//若是鼠标左键按下 { Graphics g=pictureBox1.CreateGraphics(); Pen pen1=new Pen(Color.Black); g.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//图形画在PictureBox表面 bitG.DrawLine(pen1,point.X,point.Y,e.X,e.Y);//图形画在位图对象bits中 EndPoint.X=e.X; EndPoint.Y=e.Y;//下次绘制画线段开始点 } } private void pictureBox1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { mark=false; pictureBox1.Image=bits;//保存了所画的图形 } (5) 运行,在PictureBox控件拖动鼠标能够画线。最小化后再最大化后,图形不消失。 5.11.5 存取位图文件 例子e5_11_5:为上例增长存取位图文件功能。 (6) 把Mainmenu控件放到主窗体中。增长菜单:文件,属性Name=menuItemFile。为文件菜单增长菜单项:新建、打开、另存为、退出,属性Name分别为menuItemNew、menuItemOpen、menuItemSaveAs、menuItemExit。 (7) 为主窗体菜单项新文件增长单击事件函数以下: private void menuItemNew_Click(object sender, System.EventArgs e) { bitG.Clear(Color.White);//用白色清空位图对象bitG pictureBox1.Image=bits;//pictureBox1显示用白色清空位图对象bitG } (8) 把OpenFileDialog控件放到窗体中。为主窗体菜单项打开文件增长单击事件函数以下: private void menuItemOpen_Click(object sender,System.EventArgs e) { if(openFileDialog1.ShowDialog(this)==DialogResult.OK) { bits.Dispose();//撤销bitG所引用的对象 bits=new Bitmap(openFileDialog1.FileName);//创建指定文件的新位图对象 bitG=Graphics.FromImage(bits);//获得位图对象使用的Graphics类对象 pictureBox1.Image=bits; } } (9) 把SaveFileDialog控件放到子窗体中。为主窗体菜单项另存为增长单击事件函数以下: private void menuItemSaveAs_Click(object sender,System.EventArgs e) { if(saveFileDialog1.ShowDialog(this)==DialogResult.OK) { string s=saveFileDialog1.FileName+".bmp"; bits.Save(s,System.Drawing.Imaging.ImageFormat.Bmp); } }//也能够存为其它格式,例如:Jpg,Gif等。请读者试一下。 (10) 为主窗体菜单项退出增长单击事件函数以下: private void menuItemExit_Click(object sender, System.EventArgs e) { Close();} (11) 运行,在PictureBox控件拖动鼠标能够画线。存所画的图形到文件,再从新读出该文件,看是否正常运行。 5.11.6 用拖动鼠标方法画椭圆或圆 例5_11_6:画笔程序中,拖动鼠标方法画椭圆或圆,拖动鼠标时显示椭圆或圆的轮廓,鼠标抬起时,按指定条件画椭圆或圆。若是图形仅画在图形框(PictureBox控件)上,而不保存到其属性Image引用的位图对象中,当调用图形框的Invalidate()方法,图形框响应Paint事件,用图形框属性Image引用的位图对象恢复图像,将擦除仅画在图形框上的图形。拖动鼠标方法画椭圆或圆时,仅将椭圆或圆画在PictureBox上,在鼠标拖动到下一个位置,用图形框的Invalidate()方法将前一位置所画的图形擦除。实现步骤以下: (1) 新建项目。增长5个私有变量:private bool mark=false;private Point StartPoint; private Bitmap bits;private Graphics bitG;private Point EndPoint; (2) 放PictureBox控件到子窗体。修改属性Dock=Fill。 (3) 在构造函数中增长语句: //bits用来保存pictureBox1中位图图像 bits=new Bitmap(pictureBox1.Width,pictureBox1.Height); bitG=Graphics.FromImage(bits); bitG.Clear(Color.White); pictureBox1.Image=bits; (4) 在Form1类中增长MakeRectangle方法返回由参数指定的两个点定义的矩形。方法以下: private Rectangle MakeRectangle(Point p1,Point p2) { int top,left,bottom,right; top=p1.Y<=p2.Y? p1.Y:p2.Y;//计算矩形左上角点的y坐标 left=p1.X<=p2.X? p1.X:p2.X;//计算矩形左上角点的x坐标 bottom=p1.Y>p2.Y? p1.Y:p2.Y;//计算矩形右下角点的y坐标 right=p1.X>p2.X? p1.X:p2.X;//计算矩形右下角点的x坐标 return(new Rectangle(left,top,right-left,bottom-top));//返回矩形 } (5) 为PictureBox事件OnMouseDown、OnMouseUp、OnMouseMove增长事件处理函数以下: private void pictureBox1_MouseDown(object sender,//鼠标按下事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(e.Button==MouseButtons.Left) { StartPoint.X=e.X; StartPoint.Y=e.Y; EndPoint.X=e.X; EndPoint.Y=e.Y; mark=true; } } private void pictureBox1_MouseMove(object sender,//鼠标移动事件处理函数 System.Windows.Forms.MouseEventArgs e) { if(mark) { Rectangle r1=MakeRectangle(StartPoint,EndPoint);//计算重画区域 r1.Height+=2; r1.Width+=2;//区域增大些 pictureBox1.Invalidate(r1);//擦除上次鼠标移动时画的图形,r1为擦除区域 pictureBox1.Update();//当即重画,即擦除 Graphics g=pictureBox1.CreateGraphics(); Pen pen1=new Pen(Color.Black); EndPoint.X=e.X; EndPoint.Y=e.Y; r1=MakeRectangle(StartPoint,EndPoint);//计算椭圆新位置 g.DrawEllipse(pen1,r1);//在新位置画椭圆 } } private void pictureBox1_MouseUp(object sender,//鼠标抬起事件处理函数 System.Windows.Forms.MouseEventArgs e) { Pen pen1=new Pen(Color.Black); EndPoint.X=e.X; EndPoint.Y=e.Y; Rectangle r1=MakeRectangle(StartPoint,EndPoint); bitG.DrawEllipse(pen1,r1); mark=false; pictureBox1.Image=bits; } (6) 运行,在PictureBox控件中拖动鼠标能够画圆或椭圆。 5.12 图像剪贴板功能 Windows中的许多程序都支持剪贴板功能。经过剪贴板能够完成显示数据的剪贴(Cut),复制(Copy),粘贴(Paste)等功能。剪贴板能够理解为一块存储数据的公共区域,用户能够用菜单项复制(Copy)或剪贴(Cut)把数据放入到剪贴板中,当本任务或其它任务要用剪贴板中的数据时,能够用菜单项粘贴(Paste)从剪贴板中把数据取出。存入剪贴板中的数据,能够是字符,位图,或者其它格式数据。在图形模式下使用剪贴板包括以下动做:选定剪贴区域、剪贴(Cut)、复制(Copy)、粘贴(Paste)等。使过画图程序的读者都知道,在使用剪贴和复制前,必须首先选定剪贴或复制区域,首先按一个按钮,通知程序要选定剪贴或复制区域,而后在要选定区域的左上角按下鼠标左键,拖动鼠标画出一个矩形,抬起鼠标后显示一个矩形既为要选定剪贴或复制区域。剪贴或复制后,矩形自动消失。下面详细介绍实现以上功能的方法。 5.12.1 剪贴区域选定 剪贴区域选定的方法和前边章节中拖动鼠标方法绘制椭圆或圆的方法基本同样,只是在这里绘制的是矩形,并且在鼠标抬起时,不把矩形存入PictureBox控件属性Image引用的位图对象中,仅仅记录矩形的位置。请读者本身实现此功能。 5.12.2 剪贴板复制功能的实现 假定已选定剪贴区域,例如为区域Rectangle(10,10,50,50),把此区域的图形或图像放到剪贴板中。具体实现步骤以下: (1) 新建项目。放PictureBox控件到窗体,修改属性Dock=Fill。属性Name=pictureBox1,修改属性Image,使其显示一幅图。 (2) 把Mainmenu控件放到主窗体中。增长顶级菜单项:编辑,属性Name=menuItemEdit。为编辑弹出菜单增长菜单项:复制、剪贴、粘贴。属性Name分别为menuItemCopy、menuItemCut、menuItemPaste。 (3) 为窗体菜单项复制增长单击事件函数以下: private void menuItemCopy_Click(object sender, System.EventArgs e) { Bitmap myBitmap=new Bitmap(pictureBox1.Image); Rectangle cloneRect=new Rectangle(10,10,50,50); System.Drawing.Imaging.PixelFormat format=myBitmap.PixelFormat; Bitmap cloneBitmap=myBitmap.Clone(cloneRect,format); Clipboard.SetDataObject(cloneBitmap); } (4) 运行,选中复制菜单项,复制图形到剪贴板。打开画图程序,选中画图程序粘贴菜单项,能够看到被复制的图形能正确粘贴到画图程序中。 5.12.3 剪贴板剪贴功能的实现 (5) 剪贴是先复制,再把选中区域图形清除,菜单项剪贴单击事件处理函数以下: private void menuItemCut_Click(object sender,System.EventArgs e) { menuItemCopy_Click(sender,e);//调用复制菜单项单击事件处理函数 Bitmap bits=new Bitmap(50,50);//创建位图对象,宽和高为选中区域大小 Graphics g=Graphics.FromImage(bits);//获得位图对象的Graphics类的对象 g.Clear(Color.White);//用白色清除位图对象中的图像 Bitmap myBitmap=new Bitmap(pictureBox1.Image); g=Graphics.FromImage(myBitmap); g.DrawImage(bits,10,10,50,50); pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示,即清除 } (6) 运行,选中剪贴菜单项,拷贝图形到剪贴板,原位置图形被清空为白色,最小化后再最大化,图形不变。打开画图程序,选中画图程序粘贴菜单项,能够看到被拷贝的图形能正确粘贴到画图程序中。 5.12.4 剪贴板粘贴功能的实现 (7) 为窗体菜单项粘贴增长单击事件函数以下: private void menuItemPaste_Click(object sender, System.EventArgs e) { IDataObject iData=Clipboard.GetDataObject();//获得剪贴板对象 if(iData.GetDataPresent(DataFormats.Bitmap))//判断剪贴板有无位图对象 { Bitmap bits=(Bitmap)iData.GetData(DataFormats.Bitmap);//获得剪贴板位图 Bitmap myBitmap=new Bitmap(pictureBox1.Image); Graphics g=Graphics.FromImage(myBitmap); g.DrawImage(bits,30,30); pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示 } } (8) 运行画图程序,选中拷贝菜单项,拷贝图形到剪贴板。运行本身编制的程序,选中粘贴菜单项,能够看到画图程序中被拷贝的图形能正确粘贴到本身编制的程序中。 (9) 画图程序粘贴后,能用鼠标移动粘贴的图形,现实现此功能。放PictureBox控件到窗体,属性Name=pictureBox2,属性Visable=false。这里把粘贴后的图形放到PictureBox2中,使其能够移动。为Form1类增长变量:bool mark=false;int x=0,y=0;为pictureBox2控件的事件OnMouseDown,OnMouseUp,OnMouseMove增长事件函数以下: private void pictureBox2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { mark=true; x=e.X; y=e.Y; } private void pictureBox2_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { if(mark) { int x1,y1; x1=e.X-x; y1=e.Y-y; pictureBox1.Invalidate();//擦除上次鼠标移动时画的图形 pictureBox1.Update();//当即重画,即擦除 pictureBox2.Left+=x1; pictureBox2.Top+=y1; x=e.X;//原来没有此2句 y=e.Y; } } private void pictureBox2_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { mark=false;} (10) 修改窗体菜单项粘贴单击事件函数以下: private void menuItemPaste_Click(object sender,System.EventArgs e) { IDataObject iData=Clipboard.GetDataObject(); if(iData.GetDataPresent(DataFormats.Bitmap)) { Bitmap bit=(Bitmap)iData.GetData(DataFormats.Bitmap); pictureBox2.Width=bit.Width;//阴影为修改部分 pictureBox2.Height=bit.Height; pictureBox2.Image=bit; pictureBox2.Top=pictureBox1.Top; pictureBox2.Left=pictureBox1.Left; pictureBox2.Parent=pictureBox1; pictureBox2.Visible=true; } } (11) 在pictureBox1控件任意位置单击鼠标,表示已将粘贴图像拖到指定位置,需将粘贴图像粘贴到pictureBox1控件。为pictureBox1控件的事件OnMouseDown增长事件函数以下: private void pictureBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { if(pictureBox2.Image!=null&&pictureBox2.Visible) { Bitmap bits=new Bitmap(pictureBox2.Image); Bitmap myBitmap = new Bitmap(pictureBox1.Image); Graphics g=Graphics.FromImage(myBitmap); g.DrawImage(bits,pictureBox2.Left,pictureBox2.Top); pictureBox1.Image=myBitmap;//位图对象在pictureBox1中显示 pictureBox2.Visible=false; } } (12) 运行画图程序,选中拷贝菜单项,拷贝图形到剪贴板。运行本身编制的程序,选中粘贴菜单项,能够看到画图程序中被拷贝的图形能正确粘贴到本身编制的程序中。拖动被拷贝的图形,使其运动到指定位置,在pictureBox2外,单击鼠标右键,图形固定到指定位置。 5.13 图像的处理 本节介绍图像的处理的最基础知识,要想深刻了解这方面的知识,还要读这方面的专著。 5.13.1 图像的分辨力 例子e5.13.1:将原图形的分辨率下降16倍,其方法是将原图形分红4*4的图形块,这16个点的颜色都置成这16个点中某点的颜色,例如4*4的图形块左上角的颜色。 (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一幅图。 (2) 放Button控件到窗体,为其增长事件处理函数以下: private void button1_Click(object sender,System.EventArgs e) { Color c; int i,j,size,k1,k2,xres,yres; xres=pictureBox1.Image.Width;//pictureBox1显示的图像的宽 yres=pictureBox1.Image.Height;//pictureBox1显示的图像的高 size=4; pictureBox2.Width=xres;//令pictureBox2和pictureBox1同宽,同高。 pictureBox2.Height=yres; Bitmap box1=new Bitmap(pictureBox1.Image); Bitmap box2=new Bitmap(xres,yres); for(i=0;i<=xres-1;i+=size) { for(j=0;j<=yres-1;j+=size) { c=box1.GetPixel(i,j); for(k1=0;k1<=size-1;k1++) { for(k2=0;k2<=size-1;k2++) box2.SetPixel(i+k1,j+k2,c); } } } pictureBox2.Image=box2; } (3) 运行,单击按钮,在PictureBox2中能够看到分别率低的图形。 5.13.2 彩色图像变换为灰度图像 例子e5.13.2:本例把彩色图像变换为灰度图像。其方法是将原彩色图形每个点的颜色取出,求出红色、绿色、蓝色份量的平均值,即(红色+绿色+蓝色)/3,做为这个点的红色、绿色、蓝色份量,这样就把彩色图像变成了灰度图像。具体步骤以下: (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一幅图。 (2) 放Button控件到窗体,为其增长事件函数以下: private void button1_Click(object sender, System.EventArgs e) { Color c; int i,j,xres,yres,r,g,b; xres=pictureBox1.Image.Width; yres=pictureBox1.Image.Height; pictureBox2.Width=xres; pictureBox2.Height=yres; Bitmap box1=new Bitmap(pictureBox1.Image); Bitmap box2=new Bitmap(xres,yres); for(i=0;i<xres;i++) { for(j=0;j<yres;j++) { c=box1.GetPixel(i,j); r=c.R; g=c.G; b=c.B; r=(r+g+b)/3; c=Color.FromArgb(r,r,r); box2.SetPixel(i,j,c); } } pictureBox2.Image=box2; } (3) 运行,单击按钮,在PictureBox2中能够看到黑白图形。 5.13.3 灰度图像处理 例子e5.13.3:将一幅灰度图像变换为另外一幅灰度图像,两幅灰度图形的灰度知足以下关系:设图1和图2的灰度分别为d1和d2,如d1<85,d2=0;如85<=d1<=170,d2=(d1-85)*3;如d1>170,d2=255。变换的效果是加强了对比度。具体步骤以下: (1) 新建项目。放两个PictureBox控件到窗体,属性Name分别为pictureBox1,pictureBox2,修改pictureBox1属性Image,使其显示一黑白幅图。 (2) 放Button控件到窗体,为其增长事件函数以下: private void button1_Click(object sender, System.EventArgs e) { Color c; int i,j,xres,yres,m; xres=pictureBox1.Image.Width; yres=pictureBox1.Image.Height; pictureBox2.Width=xres; pictureBox2.Height=yres; Bitmap box1=new Bitmap(pictureBox1.Image); Bitmap box2=new Bitmap(xres,yres); int[] lut=new int[256]; for(i=0;i<85;i++) lut[i]=0; for(i=85;i<=170;i++) lut[i]=(i-85)*3; for(i=171;i<256;i++) lut[i]=255; for(i=0;i<xres;i++) { for(j=0;j<yres;j++) { c=box1.GetPixel(i,j); m=lut[c.R]; c=Color.FromArgb(m,m,m); box2.SetPixel(i,j,c); } } pictureBox2.Image=box2; } (3) 运行,单击按钮,在PictureBox2中能够看到对比度加强的黑白图形。 5.13.4 动画 学习了以上知识,制做一些简单动画是比较容易的。例如,若是一段动画,要求一个动画小人从窗体左侧走到右侧,如何实现呢?首先,为了看到人在走动,应该有3个动做:右脚在前,左脚在后;两脚并排;左脚在前,右脚在后。所以应制做3幅图画,表示这三个动做。每当转换一幅图画,图画应在X轴方向右移一步的距离。将3幅图画放到3个PictureBox控件中,用定时器产生中断,中断处理程序负责使其中一幅图画显示,其他两幅不显示,同时,修改PictureBox控件属性Left,使其在正确的位置上。这样就能够看到人的走动了。请读者本身完成这段动画。 习题 (1) 使用PictureBox控件显示图像,修改属性SizeMode为不一样值,例如pictureBox1.SizeMode=PictureBoxSizeMode.StretchImage看一下效果。 (2) 实现画图程序的橡皮功能。 (3) 有时为了很快找到一幅图像,把不少图像都压缩后在窗体中并排显示,如但愿更仔细的查看某幅图像,单击这幅压缩图像,放大这幅图像。请实现此功能。 (4) 实现设定剪贴板剪贴区域为矩形的功能。 (5) 实现设定剪贴板剪贴区域为任意封闭曲线的功能。(提示:使用GraphicsPath类) (6) 如何将PictureBox控件显示图像存为其它格式文件,例如:Jpg,Gif等。 (7) 完成5.11.4所要求的动画,并能走到窗体右边界后,从右向左走回来,到左边界后,再向右走回去。若是有背景,如何处理。 (8) 制做本身的画图程序,看一看能完成那些功能。 (9) 有些时候,为突出图形的分界,例如医院的X片时黑白的,为了使医生能更清楚的看到肿瘤,将黑白图形变为彩色的图形,在分界两侧用不一样颜色表示,这种方法叫伪彩色。实现的原理,就是在黑白图像灰度变化很大处,认为是边界。试一下,可否实现黑白图像的伪彩色。 第六章 文件和流 编程语言在如何处理输入/输出问题方面已经通过了不少变革。早期语言,例如Basic语言,使用I/O语句。后来的语言,例如C语言,使用标准的I/O库(stdio.h)。在C++和Java语言中,引入了抽象的概念:流。流的概念不只可用于文件系统,也可用于网络。但在C++和Java语言中流的概念比较复杂。C#语言也采用了流的概念,可是使用起来要简单的多。本章介绍C#语言中,如何处理目录和文件夹,如何处理文件,如何使用流的概念读写文件。 6.1 用流读写文件 C#把每一个文件都当作是顺序的字节流,用抽象类Stream表明一个流,能够从Stream类派生出许多派生类,例如FileStream类,负责字节的读写,BinaryRead类和BinaryWrite类负责读写基本数据类型,如bool、String、int1六、int等等,TextReader类和TextWriter类负责文本的读写。本节介绍这些类的用法。 6.1.1 用FileStream类读写字节 写字节代码段以下: byte[] data=new byte[10]; For(int i=0;i<10;i++) data[i]=(byte)i; System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); fs.Write(data,0,10); 读字节代码段以下: byte[] data=new byte[10]; System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); fs.Seek(-5,SeekOrigin.End); int n=fs.Read(data,0,10);//n为所读文件字节数 6.1.2 用BinaryReader和BinaryWriter类读写基本数据类型 C#中除了字节类型之外,还有许多其它基本数据类型,例如,int、bool、float等等,读写这些基本数据类型须要使用BinaryReader和BinaryWriter类。写int类型数据代码段以下: System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); System.IO.BinaryWrite w=new System.IO. BinaryWrite(fs); For(int i=0;i<10;i++) w.Write(i); w.Close(); 读int类型数据代码段以下: int[] data=new int[10]; System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); System.IO.BinaryReader r=new System.IO. BinaryReader(fs); For(int i=0;i<10;i++) data[i]=r.ReadInt(); r.Close(); 6.1.3 用StreamReader和StreamWriter类读写字符串 读写字符串能够用StreamReader和StreamWriter类。写字符串类型数据代码段以下: System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); System.IO.StreamWrite w=new System.IO.StreamWrite(fs); w.Write(100); w.Write("100个"); w.Write("End of file"); w.Close(); 读字符串代码段以下: String[] data=new String[3]; System.IO.FileStream fs=new System.IO.FileStream("g1",FileMode.OpenOrCreate); System.IO.StreamReader r=new System.IO.StreamReader(fs); For(int i=0;i<3;i++) data[i]=r.ReadLine(); r.Close(); 6.2 File类和FileInfo类 C#语言中经过File和FileInfo类来建立、复制、删除、移动和打开文件。在File类中提供了一些静态方法,使用这些方法能够完成以上功能,但File类不能创建对象。FileInfo类使用方法和File类基本相同,但FileInfo类能创建对象。在使用这两个类时须要引用System.IO命名空间。这里重点介绍File类的使用方法。 6.2.1 File类经常使用的方法  AppendText:返回StreamWrite,向指定文件添加数据;如文件不存在,就建立该文件。  Copy:复制指定文件到新文件夹。  Create:按指定路径创建新文件  Delete:删除指定文件。  Exists:检查指定路径的文件是否存在,存在,返回true。  GetAttributes:获取指定文件的属性。  GetCreationTime:返回指定文件或文件夹的建立日期和时间。  GetLastAccessTime:返回上次访问指定文件或文件夹的建立日期和时间。  GetLastWriteTime:返回上次写入指定文件或文件夹的建立日期和时间。  Move:移动指定文件到新文件夹。  Open:返回指定文件相关的FileStream,并提供指定的读/写许可。  OpenRead:返回指定文件相关的只读FileStream。  OpenWrite:返回指定文件相关的读/写FileStream。  SetAttributes:设置指定文件的属性。  SetCretionTime:设置指定文件的建立日期和时间。  SetLastAccessTime:设置上次访问指定文件的日期和时间。  SetLastWriteTime:设置上次写入指定文件的日期和时间。 下面经过程序实例来介绍其主要方法: 6.2.2 文件打开方法:File.Open 该方法的声明以下:public static FileStream Open(string path, FileMode mode)。下面的代码打开存放在c:\Example目录下名称为e1.txt文件,并在该文件中写入hello。 FileStream TextFile=File.Open(@"c:\ Example\e1.txt",FileMode.Append); byte [] Info={(byte)'h',(byte)'e',(byte)'l',(byte)'l',(byte)'o'}; TextFile.Write(Info,0,Info.Length); TextFile.Close(); 6.2.3 文件建立方法:File.Create 该方法的声明以下:public static FileStream Create(string path)。下面的代码演示如何在c:\Example下建立名为e1.txt的文件。 FileStream NewText=File.Create(@"c:\Example\e1.txt"); NewText.Close(); 6.2.4 文件删除方法:File.Delete 该方法声明以下:public static void Delete(string path)。下面的代码演示如何删除c:\Example目录下的e1.txt文件。 File.Delete(@"c:\Example\e1.txt"); 6.2.5 文件复制方法:File.Copy 该方法声明以下: public static void Copy(string sourceFileName,string destFileName,bool overwrite); 下面的代码将c:\Example\e1.txt复制到c:\Example\e2.txt。因为Cope方法的OverWrite参数设为true,因此若是e2.txt文件已存在的话,将会被复制过去的文件所覆盖。 File.Copy(@"c:\Example\e1.txt",@"c:\Example\e2.txt",true); 6.2.6 文件移动方法:File.Move 该方法声明以下:public static void Move(string sourceFileName,string destFileName);下面的代码能够将c:\Example下的e1.txt文件移动到c盘根目录下。注意:只能在同一个逻辑盘下进行文件转移。若是试图将c盘下的文件转移到d盘,将发生错误。 File.Move(@"c:\Example\BackUp.txt",@"c:\BackUp.txt"); 6.2.7 设置文件属性方法:File.SetAttributes 方法声明以下:public static void SetAttributes(string path,FileAttributes fileAttributes);下面的代码能够设置文件c:\Example\e1.txt的属性为只读、隐藏。 File.SetAttributes(@"c:\Example\e1.txt", FileAttributes.ReadOnly|FileAttributes.Hidden); 文件除了经常使用的只读和隐藏属性外,还有Archive(文件存档状态),System(系统文件),Temporary(临时文件)等。关于文件属性的详细状况请参看MSDN中FileAttributes的描述。 6.2.8 判断文件是否存在的方法:File.Exist 该方法声明以下:public static bool Exists(string path);下面的代码判断是否存在c:\Example\e1.txt文件。 if(File.Exists(@"c:\Example\e1.txt"))//判断文件是否存在 {…}//处理代码 6.2.9 获得文件的属性 用下面的代码能够获得文件的属性,例如文件建立时间、最近访问时间、最近修改时间等等。 FileInfo fileInfo=new FileInfo(“file1.txt”); string s=fileInfo.FullName+”文件长度=”+fileInfo.Length+”,创建时间=”+ fileInfo.CreationTime+”; 也可用以下代码: string s=”创建时间=”+File.File.GetCreationTime(“file1.txt”)+”最后修改时间=”+ File.GetLastWriteTime(“file1.txt”)+”访问时间=”+File.GetLastAccessTime(“file1.txt”); 6.3 Directory类和DirectoryInfo类 C#语言中经过Directory类来建立、复制、删除、移动文件夹。在Directory类中提供了一些静态方法,使用这些方法能够完成以上功能。但Directory类不能创建对象。DirectoryInfo类使用方法和Directory类基本相同,但DirectoryInfo类能创建对象。在使用这两个类时须要引用System.IO命名空间。这里重点介绍Directory类的使用方法。 6.3.1 Directory类经常使用的方法以下:  CreateDirectory:按指定路径建立全部文件夹和子文件夹。  Delete:删除指定文件夹。  Exists:检查指定路径的文件夹是否存在,存在,返回true。  GetCreationTime:返回指定文件或文件夹的建立日期和时间。  GetCurrentDirectory:获取应用程序的当前工做文件夹。  GetDirectories:获取指定文件夹中子文件夹的名称。  GetDirectoryRoot:返回指定路径的卷信息、根信息或二者同时返回。  GetFiles:返回指定文件夹中子文件的名称。  GetFileSystemEntries:返回指定文件夹中全部文件和子文件的名称。  GetLastAccessTime:返回上次访问指定文件或文件夹的建立日期和时间。  GetLastWriteTime:返回上次写入指定文件或文件夹的建立日期和时间。  GetLogicalDrives:检索计算机中的全部驱动器,例如A:、C:等等。  GetParent:获取指定路径的父文件夹,包括绝对路径和相对路径。  Move:将指定文件或文件夹及其内容移动到新位置。  SetCreationTime:设置指定文件或文件夹的建立日期和时间。  SetCurrentDirectory:将应用程序的当前工做文件夹设置指定文件夹。  SetLastAccessTime:设置上次访问指定文件或文件夹的日期和时间。  SetLastWriteTime:设置上次写入指定文件夹的日期和时间。 6.3.2 目录建立方法:Directory.CreateDirectory 方法声明以下:public static DirectoryInfo CreateDirectory(string path);下面的代码演示在c:\Dir1文件夹下建立名为Dir2子文件夹。 Directory.CreateDirectory(@"c:\Dir1\Dir2"); 6.3.3 目录属性设置方法:DirectoryInfo.Atttributes 下面的代码设置c:\Dir1\Dir2目录为只读、隐藏。与文件属性相同,目录属性也是使用FileAttributes来进行设置的。 DirectoryInfo DirInfo=new DirectoryInfo(@"c:\Dir1\Dir2"); DirInfo.Atttributes=FileAttributes.ReadOnly|FileAttributes.Hidden; 6.3.4 目录删除方法:Directory.Delete 该方法声明以下:public static void Delete(string path,bool recursive);下面的代码能够将c:\Dir1\Dir2目录删除。Delete方法的第二个参数为bool类型,它能够决定是否删除非空目录。若是该参数值为true,将删除整个目录,即便该目录下有文件或子目录;若为false,则仅当目录为空时才可删除。 Directory.Delete(@"c:\Dir1\Dir2",true); 6.3.5 目录移动方法:Directory.Move 该方法声明以下:public static void Move(string sourceDirName,string destDirName);下面的代码将目录c:\Dir1\Dir2移动到c:\Dir3\Dir4。 File.Move(@"c:\Dir1\Dir2",@"c:\Dir3\Dir4");} 6.3.6 获取当前目录下全部子目录:Directory.GetDirectories 该方法声明以下:public static string[] GetDirectories(string path;);下面的代码读出c:\Dir1\目录下的全部子目录,并将其存储到字符串数组中。 string [] Directorys; Directorys = Directory. GetDirectories (@"c:\Dir1"); 得到全部逻辑盘符: string[] AllDrivers=Directory.GetLogicalDrives(); 6.3.7 获取当前目录下的全部文件方法:Directory.GetFiles 该方法声明以下:public static string[] GetFiles(string path;);下面的代码读出c:\Dir1\目录下的全部文件,并将其存储到字符串数组中。 string [] Files; Files = Directory. GetFiles (@"c:\Dir1",); 6.3.8 判断目录是否存在方法:Directory.Exist 该方法声明以下:public static bool Exists(string path;);下面的代码判断是否存在c:\Dir1\Dir2目录。 if(File.Exists(@"c:\Dir1\Dir2"))//判断目录是否存在 {…}//处理语句 注意:路径有3种方式,当前目录下的相对路径、当前工做盘的相对路径、绝对路径。以C:\dir1\dir2为例(假定当前工做目录为C:\Tmp)。“dir2”,“\dir1\dir2”,“C:\dir1\dir2”都表示C: \dir1\dir2。另外,在C#中 “\”是特殊字符,要表示它的话须要使用“\\”。因为这种写法不方便,C#语言提供了@对其简化。只要在字符串前加上@便可直接使用“\”。因此上面的路径在C#中应该表示为”dir2”,@”\dir1\dir2”,@”C:\dir1\dir2”。 6.4 例子:查找文件 6.4.1 Panel和ListView控件 6.4.2 在指定文件夹中查找文件 Windows操做系统提供了一个查找文件的程序,能够查找指定文件夹中的指定文件,本例也实现了一样的功能。具体实现步骤以下: (1) 新建项目。 (2) 放Panel控件到窗体,属性Dock=Left。Panel控件能够把窗体分割为多个部分,这里将窗体分割为左右两部分。 (3) 在Panel控件中增长两个Label控件,属性Text分别为”要搜索的文件或文件夹”和”搜索范围”。 (4) 在Panel控件中增长一个TextBox控件,属性Name=textBox1,属性Text为空,用来输入要搜索的文件或文件夹。 (5) 在Panel控件中增长一个TextBox控件,属性Name=textBox2,属性Text为空,用来输入搜索范围。在其后增长一个Button控件,属性Name=Broswer,属性Text=”浏览”。 (6) 为”浏览”按钮增长事件函数以下: private void Broswer_Click(object sender, System.EventArgs e) { OpenFileDialog dlg=new OpenFileDialog(); if(dlg.ShowDialog()==DialogResult.OK) { textBox2.Text=dlg.FileName; } } (7) 在Panel控件中增长一个Button控件,属性Name分别为Start和Stop,属性Text分别为”开始搜索”和”中止搜索”。 (8) 放分割器控件Splitter到窗体,属性Dock=Left。 (9) 在分割器控件右侧放置视图控件ListView,属性Dock=Right,属性SmallImgeList =”imageList”,属性View=”Detail”。点击属性Column右侧标题为…的按钮,在弹出的ColumnHeader编辑对话框中添加4个列头,属性Name分别为:FileName、FileDirectory、FileSize和LastWriteTime,属性Text分别为:名称、所在文件夹、大小和修改时间。 (10) 为窗体增长一个方法:FindFiles(DirectoryInfo dir,string FileName),该方法是在第一个参数指定的文件夹中查找第二个参数指定的全部文件。在一个文夹中可能还有子文件夹,子文件夹中可能还有子文件夹,所以要在第一个参数指定的文件夹中和其子文件夹中查找第二个参数指定的全部文件。为了实现可以查找全部文件夹中的同名文件,采用递归调用方法,若是在一个文件夹中存在子文件夹,在一次调用函数本身,查找子文件夹中的文件。具体实现代码以下: void FindFiles(DirectoryInfo dir,string FileName) { FileInfo[] files=dir.GetFiles(FileName);//查找全部文件并在ListView中显示 If(files.Length!=0) { foreach(FileInfo aFile in files) { ListViewItem lvi; lvi=new ListViewItem(aFile.Name,aFile.Directory.FullName,aFile.Length.ToString, aFile.LastWriteTime.ToShortDateString()); lvi.ImageIndex=0; listView1.Items.Add(lvi); } } DirectoryInfo[] dirs=dir.GetDirectories();//查找子文件夹中的匹配文件 If(dirs.Length!=0) { foreach(DirectoryInfo aDir in dirs) { FindFiles(aDir,FileName); } } } (11) 为”开始搜索”按钮增长事件函数以下: private void Start_Click(object sender, System.EventArgs e) { DirectoryInfo aDir=CreateDirectorie(comboBox1.Text); FindFiles(aDir,textBox1.Text); } (12) 为”中止搜索”按钮增长事件函数以下: private void Stop_Click(object sender, System.EventArgs e) { } (13) 编译、运行, 6.5 例子:拆分和合并文件 在将一个文件做为电子邮件的附件传送时,因为附件的大小有限制,能够将较大的文件分割为较小的多个文件,传送后再合并为一个文件,下边两个方法实现文件的拆分和合并。首先是拆分方法,参数1时要拆分的文件名,参数2是拆分后的文件名,文件名后边由拆分方法自动增长序号,参数3是被拆分后的文件大小。拆分方法定义以下: void SplitFile(string f1,string f2,int f2Size) { FileStream inFile=new FileStream(f1,FileMode.OpenOrCreate,FileAccess.Read); bool mark=true; int i=0; int n=0; byte[] buffer=new byte[f2Size]; while(mark) { FileStream OutFile=new FileStream(f2+i.ToString+”.fsm”, FileMode.OpenOrCreate,FileAccess.Read); if((n=inFile.Read(buffer,0,f2Size))>0) { OutFile.Write(buffer,0,n); i++; OutFile.Close(); } else { mark=false; } } inFile.Close(); } 合并文件方法,参数1时要合并的文件名,参数2是被拆分的文件名,文件名后边有序号,要将这些文件合并到一块儿,参数3是要合并的文件数。合并方法定义以下: void MergeFile(string f1,string f2,int f2Num) { FileStream OutFile=new FileStream(f1,FileMode.OpenOrCreate,FileAccess.Write); int n,l; for(int i=0;i<f2Num;i++) { FileStream InFile=new FileStream(f2+i.ToString+”.fsm”,FileMode.OpenOrCreate,FileAccess.Read); l=InFile.Length; byte[] buffer=new byte[l]; n=inFile.Read(buffer,0,l); OutFile.Write(buffer,0,n); InFile.Close(); } OutFile.Close(); } 习题: (1) 实现拆分和合并文件的完整程序。 第七章 多线程程序设计 若是在一个程序中,有多个工做要同时作,能够采用多线程。在Windows操做系统中能够运行多个程序,把一个运行的程序叫作一个进程。一个进程又能够有多个线程,每一个线程轮流占用CPU的运算时间,Windows操做系统将时间分为许多个时间片,一个线程使用一个时间片后,操做系统将此线程挂起,将另外一个线程唤醒,使其使用下一个时间片,操做系统不断的把线程挂起,唤醒,再挂起,再唤醒,如此反复,因为如今CPU的速度比较快,给人的感受象是多个线程同时执行。Windows操做系统中有不少这样的例子,例如复制文件时,一方面在进行磁盘的读写操做,同时一张纸不停的从一个文件夹飘到另外一个文件夹,这个飘的动做其实是一段动画,两个动做是在不一样线程中完成的,也就是说两个动做是同时完成的。又如Word程序中的拼写检查也是在另外一个线程中完成的。每一个进程最少有一个线程,叫主线程,是进程自动建立的,每进程能够建立多个线程。 不一样语言和操做系统对线程提供了不一样支持,编写多线程应用程序的方法也不尽相同。例如,VB6没有提供对线程的支持,程序员不能处理本身的线程。VC++6.0开发人员必须充分理解Windows线程和处理模型的复杂性,同时拥有这种线程模型的强大功能。C++程序员能够建立出多线程程序,但必须学习掌握不少复杂的技巧,以确保线程在本身的控制之下。 .NET Framework提供了一个完整而功能强大的线程模型,该模型容许编程人员精确控制线程中运行的内容,线程什么时候退出,以及它将访问多少数据等。因此,在.NET中,既提供了C++的强大功能,又具备VB6的简单性。 7.1 线程类(Thread)的属性和方法 线程类在命名空间System..Threading中定义的,所以若是要建立多线程,必须引入命名空间System..Threading。Thread类的经常使用方法以下:  属性ThreadPriority:设置线程优先级,有5种优先级类别:(AboveNormal)稍高、(BelowNormal)稍低、Normal(中等,默认值)、Highest(最高)和Lowest(最低)。  构造函数:new Thread(new ThreadStart(线程首先执行的方法名)),构造方法参数中指定的方法须要程序员本身定义,在这个方法中完成线程要完成的任务,退出该方法,线程结束。该方法必须为公有void类型的方法,不能有参数。  方法Start():创建线程类对象后,线程并不能自动运行,用这个方法启动线程。  方法IsAlive():判断线程对象是否存在,=true,存在。  方法Abort():撤销线程对象。不能撤销一个已不存在的线程对象,所以在撤销一个线程对象前,必须用方法IsAlive()判断线程对象是否存在。  方法Sleep():参数为毫秒,线程暂时中止参数指定的时间,容许其它线程运行。  方法Suspend():线程挂起。如只是暂时中止线程的运行,可用此函数将线程挂起。必须用Resume()方法唤醒线程。  方法Resume():恢复挂起线程。如但愿继续运行挂起线程,可用此方法唤醒线程。须要注意的是,若是线程屡次被挂起,调用一次Resume()方法就能够把线程唤醒。 7.2 线程的建立 例子:多线程程序设计,该程序包括一个子线程,在标签控件中显示子线程运行的时间。增长4个按钮,分别单击按钮,能够创建、挂起、恢复和中止线程。 (1) 新建项目。在窗体中放置4个按钮和一个标签控件,属性Name分别为button一、button二、button三、button4和label1,按钮属性Text分别为新线程、挂起、恢复和撤销。button1属性Enabled=true,其他按钮的属性Enabled=false。 (2) 在Form1.cs头部增长语句:using System.Threading。 (3) 为Form1类定义一个线程类变量:private Thread thread; (4) 为新线程按钮(button1)增长单击事件函数以下: thread= new Thread(new ThreadStart(fun);//生成线程类对象 label1.Text=”0”; thread.Start(); button1.Enabled=false; button2.Enabled=true; button3.Enabled=false; button4.Enabled=true; (5) 为挂起按钮(button2)增长单击事件函数以下: thread. Suspend(); button1.Enabled=false; button2.Enabled=false; button3.Enabled=true; button4.Enabled=false; (6) 为恢复按钮(button3)增长单击事件函数以下: thread. Resume(); button1.Enabled=false; button2.Enabled=true; button3.Enabled=false; button4.Enabled=true; (7) 为撤销按钮(button4)增长单击事件函数以下: if(thread.IsAlive()) { thread.Abort();//撤销线程对象 button1.Enabled=true; button2.Enabled=false; button3.Enabled=false; button4.Enabled=false; } (8) C#线程模型因为容许将任何一个原型为void类型的公有类成员方法(静态或非静态)做为线程方法,所以它实际上容许在任何一个类(不要求这个类是某个类的子类)中实现线程方法,并且同一个类中能够实现多个线程方法。为Form1类定义一个方法以下: public void fun()//在线程中执行的方法,必须为公有void类型方法,不能有参数。 { while(true)//死循环,线程将一直运行 { int x=Convert.ToInt(label1.Text); x++; label1.Text=Convert.ToString(x); thread.Sleep(1000);//休眠1秒钟,休眠一次,线程运行了1秒钟 } } (9) 编译,运行,按新线程(Button1)按钮,新线程开始,计数器从0开始计数。按挂起(Button2)按钮,线程暂停,计数器也暂停。按恢复(Button3)按钮,线程从新启动,计数器也从新计数。按撤销(Button4)按钮,线程对象被撤销,线程对象不存在,计数器中止计数。 7.3 创建线程类 有时须要创建多个线程,每一个线程要实现的功能基本相同,但有个别参数不一样,例如,每一个线程完成一样的任务,但控制的对象不一样。线程构造函数参数指定的方法须要本身定义,在这个方法中完成一些任务,但该方法不能有参数,所以不能经过方法的参数传递不一样设置。为解决这个问题,能够定义一个线程类。具体实现方法件下例。 例子:创建两个线程,分别控制进度条(ProgressBar)控件,每一个进度条的速度不同。首先介绍进度条(ProgressBar)控件。 7.3.1 进度条(ProgressBar)控件 进度条(ProgressBar)控件常常用来显示一个任务的进度。有时,要在后台完成一个长时间的任务,例如一个软件的安装,若是没有任何提示,使用者可能分不清任务是在进行中,仍是死机了,为了让用户知道安装正在进行,能够使用进度条控件显示一个安装进度。进度条控件经常使用的属性以下:  属性Maximum:进度条所表明的整数最大值,等于此值,任务完成。默认值100。  属性Minimum:进度条所表明的整数最小值,等于此值,任务开始。默认值0。  属性Step:变化的步长,默认值为10。  属性Value:进度条当前位置表明的值。修改该值,达到一个Step,进度增长一格。 7.3.2 用线程控制进度条 例子实现的具体实现步骤以下: (1) 新建项目。在Form1.cs头部增长语句:using System.Threading。 (2) 在窗体中放置2个进度条(ProgressBar)控件和一个标签控件,属性Name分别为progressBar一、progressBar2和label1。Label1的属性Text=””。 (3) 在文件Form1.cs的最后创建线程类以下: public class myThread { private int SleepTime; private ProgressBar progressBar; private Thread thread1; public myThread(int Time,ProgressBar p1) { SleepTime=Time; progressBar=p1; thread1=new Thread(new ThreadStart(Fun)); Thread1.Start(); } public void fun() { while(progressBar.Value!=100) { progressBar.Value+=1; thread1.Sleep(SleepTime); } if(label.Text==””) label1.Text=”第一个线程结束”; else label.Text+=”,第二个线程结束”; } } (4) 为Form1类增长变量:myThread myThread1,myThread2。 (5) 为Form1类构造函数增长语句以下: myThread1=new myThread(100,progressBar1); myThread2=new myThread(200,progressBar2); (6) 编译,运行,能够看到两个进度条以不一样的速度前进,当进度条被添满,线程中止。 7.4 线程的优先级 当一个程序被调入内存准备运行时,操做系统自动建立一个进程和一个主线程,并为进程指定基本优先级,进程基本优先级分为如下四种:  IDLE_PROCESS_CLASS 系统空闲时才执行  NORMAL_PROCESS_CLASS 系统默认进程优先级  BELOW_NORMAL_PROCESS_CLASS 比系统默认进程优先级低一级  ABOVE_NORMAL_PROCESS_CLASS 比系统默认进程优先级高一级  HIGH_PROCESS_CLASS 高进程优先级  REALTIME_PROCESS_CLASS 进程最高(实时)优先级 操做系统通常分配用户应用程序进程为NORMAL_PROCESS_CLASS系统默认进程优先级。对于进程中的各个子线程,能够修改属性ThreadPriority来调整其的优先级,属性ThreadPriority能够取以下值:  THREAD_PRIORITY_IDLE 系统空闲时才执行  THREAD_PRIORITY_LOWEST 比NORMAL低2级  THREAD_PRIORITY_LOWER 比NORMAL低1级  THREAD_PRIORITY_NORMAL 系统默认线程优先级  THREAD_PRIORITY_HIGHT 比NORMAL高1级  THREAD_PRIORITY_HIGHTEST 比NORMAL高2级  THREAD_PRIORITY_TIME_CRITICAL 线程最高(实时)优先级 一个线程的优先权并非越高越好,应考虑到整个进程中全部线程以及其余进程的状况作出最优选择。优先级相同的线程按照时间片轮流运行。优先级高的线程先运行,只有优先级高的线程中止、休眠或暂停时,低优先级线程才能运行。 7.5 多个线程互斥 多个线程同时修改同一个共享数据可能发生错误,例如,两个线程记录不一样入口进入的人数,用一个变量实时显示总人数。每一个线程都要对这个总人数变量执行加1操做,这个加1操做是一个高级语言语句,可能包含若干机器语言语句,例如,可能先从内存取数,加1,再存回内存。假如,当前人数为100,第一个线程运行,从内存取出总人数100,时间片时间到,第2个线程启动,执行加1操做,总人数变为101,第2个线程退出运行。第一个线程恢复运行,执行加1操做,存回内存,总数本应为102,实际为101,少计算了一个。为了防止此类错误,在一个线程操做这个总人数变量时,不容许其它线程对它进行操做,这叫线程的互斥。 7.5.1 多个线程同时修改共享数据可能发生错误 例子e7_5_1:下边的例子模拟多个线程同时修改同一个共享数据发生的错误。 (1) 新建项目。在Form1.cs头部增长语句:using System.Threading。 (2) 在窗体中放置一个标签控件,属性Name=label1。 (3) 为Form1类定义2个线程类变量:Thread thread1,thread2。定义1个整形变量:int num=0。 (4) 为Form1类构造函数增长语句以下: thread1= new Thread(new ThreadStart(Fun1); thread2= new Thread(new ThreadStart(Fun2); (5) 为Form1类定义Fun1()和Fun2()方法以下: public void Fun1() { int k,n; for(k=0;k<4;k++) { n=num; n++; thread1.Sleep(100); num=n; } label1.Text=Convert.ToString(num); } public void Fun2() { int k,n; for(k=0;k<4;k++) { n=num; n++; thread2.Sleep(200); num=n; } label1.Text=Convert.ToString(num); } (6) 编译,运行,标签控件应显示8,实际运行屡次,显示的数要小于8。 7.5.2 用LOCK语句实现互斥 7.5.3 用Mutex类实现互斥 7.5.4 用Monitor类实现互斥 7.6 Monitor类 7.7 线程的同步:生产者和消费者关系 在生产者和消费者关系中,生产者线程产生数据,并把数据存到公共数据区,消费者线程使用数据,从公共数据区取出数据,并进行分析。很显然,若是公共数据区只能存一个数据,那么在消费者线程取出数据前,生产者线程不能放新数据到公共数据区,不然消费者线程将丢失数据。一样,只有在生产者线程把数据已经放到公共数据区,消费者线程才能取出数据,若是新数据未放到公共数据区,消费者线程不能取数据。这些就是所谓的生产者和消费者关系,这要求生产者线程和消费者线程同步。 7.7.1 生产者线程和消费者线程不一样步可能发生错误 例子e7_7_1:下边的例子模拟生产者线程和消费者线程不一样步可能发生错误。有一个公共变量,要求生产者线程顺序放1到4到这个公共变量中,每放一个变量,消费者线程取出这个数求和,最后把和显示出来,显然和应为10。如不采起同步措施,和的结果不正确。 (1) 新建项目。在Form1.cs头部增长语句:using System.Threading。 (2) 在窗体中放置一个标签控件,属性Name=label1。 (3) 为Form1类定义2个线程类变量:Thread thread1,thread2。 (4) 为Form1类定义2个整形变量:int sum=0,x=-1。 (5) 为Form1类构造函数增长语句以下: thread1= new Thread(new ThreadStart(Fun1); thread2= new Thread(new ThreadStart(Fun2); (6) 为Form1类定义Fun1()和Fun2()方法以下: public void Fun1()//生产数据 { int k,n; for(k=1;k<5;k++) { x=i; thread1.Sleep(200); } } public void Fun2()//消费数据 { int k,n; for(k=0;k<4;k++) { sum+=x; thread2.Sleep(100); } label1.Text=Convert.ToString(sum); } (7) 编译,运行,标签控件应显示10,实际运行屡次,显示的数要小于10。 7.7.2 生产者线程和消费者线程同步的实现 修改上例,为Form1类定义1个布尔变量:bool mark=false。其值为false,表示数据还未放到公共数据区(即x)中,生产者线程能够放数据到公共数据区中,因为没有数据,消费线程不能取数据,必须等待。mark=true,表示数据已未放到公共数据区(即x)中,消费线程还未取数据,生产者线程不能再放数据到公共数据区中,必须等待。因为有了数据,消费线程能够取数据。修改Fun1()以下: public void Fun1()//生产数据 { int k,n; for(k=1;k<5;k++) { Monitor.Enter(this); If(mark) Monitor.Wait(this);//若是消费者数据未取走,生产者等待 !mark; x=i; Monitor.Pulse(this);//激活消费者线程 Monitor.Exit(this); } } 修改Fun2()以下: public void Fun2()//消费数据 { int k,n; for(k=0;k<4;k++) { Monitor.Enter(this); If(!mark) Monitor.Wait(this);//若是生产者未放数据,消费者等待 !mark; sum+=x; Monitor.Pulse(this); Monitor.Exit(this); } label1.Text=Convert.ToString(sum); } 编译,运行,标签控件应显示10。 习题: (1) 修改例子e7_5,用线程类实现。 第八章 ADO.NET与数据操做 8.1 数据库基本概念 数据库系统提供了一种将信息集合在一块儿的方法。数据库主要由三部分组成:数据库管理系统(DBMS),是针对全部应用的,例如ACCESS。数据库自己,按必定的结构组织在一块儿的相关数据。数据库应用程序,它是针对某一具体数据库应用编制的程序,用来获取,显示和更新数据库存储的数据,方便用户使用。这里讲的就是如何编写数据库应用程序。 常见的数据库系统有:FoxPro,Access,Oracle,SQLserver,Sybase等。数据库管理系统主要有四种类型:文件管理,层次数据库,网状数据库和关系数据库。目前最流行,应用最普遍的是关系数据库,以上所列举的数据库系统都是关系数据库。关系数据库以行和列的形式来组织信息,一个关系数据库由若干表组成,一个表就是一组相关的数据按行排列,例如一个通信录就是这样一个表,表中的每一列叫作一个字段,例如通信录中的姓名,地址,电话都是字段。字段包括字段名及具体的数据,每一个字段都有相应的描述信息,例如数据类型,数据宽度等。表中每一行称为一条记录。 数据库可分为本地数据库和远程数据库,本地数据库通常不能经过网络访问,本地数据库每每和数据库应用程序在同一系统中,本地数据库也称为单层数据库。远程数据库一般位于远程计算机上,用户经过网络来访问远程数据库中的数据。远程数据库能够采用两层,三层和四层结构,两层结构通常采用C/S模式,即客户端和服务器模式。三层模式通常采用B/S模式,用户用浏览器访问WEB服务器,WEB服务器用CGI,ASP,PHP,JSP等技术访问数据库服务器,生成动态网页返回给用户。四层模式是在WEB服务器和数据库服务器中增长一个应用服务器。利用ADO.NET能够开发数据库应用程序。 因为ADO.Net的使用,设计单层数据库或多层数据库应用程序使用的方法基本一致,极大地方便了程序设计,所以,这里讨论的内容也适用于后边的Web应用程序设计。 8.2 设计链接和不链接数据库应用程序的基本步骤: 设计一个数据库应用程序能够采用链接和不链接方式。所谓链接方式,是数据库应用程序运行期间,一直保持和数据库链接,数据库应用程序经过SQL语句直接对数据库操做,例如,查找记录、删除记录、修改记录。所谓不链接方式,是数据库应用程序把数据库中感兴趣的数据读入创建一个副本,数据库应用程序对副本进行操做,必要时将修改的副本存回数据库。设计一个不链接方式数据库应用程序通常包括如下基本步骤: (1) 创建数据库,包括若干个表,在表中添入数据。 (2) 创建和数据库的链接。 (3) 从数据库中取出感兴趣的数据存入数据集DataSet对象,包括指定表和表中知足条件的记录,DataSet对象被创建在内存中,能够包含若干表,能够认为是数据库在内存中的一个子集。而后断开和数据库的联接。 (4) 用数据绑定的方法显示这个子集的数据,供用户浏览、查询、修改。 (5) 把修改的数据存回源数据库。 设计一个链接方式数据库应用程序通常包括如下基本步骤: (1) 创建数据库,包括若干个表,在表中添入数据。 (2) 创建和数据库的链接。 (3) 使用查询、修改、删除、更新等Command对象直接对数据库操做。 如下章节将按以上步骤说明数据库应用程序的具体设计方法。 8.3 用ACCESS建立数据库 本例建立一个学生信息管理系统,包括两个表,第一个表记录学生的基本状况,包括如下字段:学号、姓名、性别等。第二个表记录学生的学习成绩,包括如下字段:记录编号、课程名称、分数、拥有该课程成绩学生的学号。注意,把学习成绩表字段定义为:学号、语文成绩、数学成绩、物理成绩等字段是不合适的,这样作,增长一门课程,就要增长一个字段,字段要动态增长,这显然不合理。用Access2000程序建立数据库具体步骤以下: (1) 运行microsoft Access2000程序,出现《microsoft Access》对话框以下图,选择空Access数据库,单击肯定按钮,打开标题为《文件新建数据库》对话框。 (2) 在标题为《文件新建数据库》对话框中,添入数据库文件名:StudentI,选择保存位置和保存类型以下图。单击建立按钮,出现《StudentI:数据库》对话框。 (3) 在《StudentI:数据库》对话框中,双击《使用设计器建立数据表》,出现《表1:表》对话框。在表中能够建立数据库表的字段。 (4) 在《表1:表》对话框中,建立字段StudentNum,数字类型的整形,必填字段,默认值为0,标题为学生编号。字段StudentName,文本,字段大小8,必填字段,默认值为空,标题为学生姓名。字段StudentSex,文本,字段大小2,标题为性别。右击字段StudentNum,在弹出菜单中选择主键菜单项,设置字段StudentNum为主关键字。以下图。 (5) 选择《文件》弹出菜单的《保存》菜单项,出现《另存为》对话框,在对话框中的《表名称(N)》编辑框中输入表名:Student,单击肯定按钮。关闭《表1:表》对话框。 (6) 在《StudentI:数据库》对话框左侧,选择对象为:表,在右测出现Student表,双击Student表,出现《Student:表》对话框。 (7) 在《Student:表》对话框中为各个字段输入数据:例如:1,张三,男;2,李四,女;3,王五,男;4,鲁六,女。而后存盘。以下图。 (8) 一样方法创建表Score,记录全部学生学习成绩。包括字段ScoreID(记录编号),自动编号;ClassName(课程名称),文本,字段大小26,必填字段,默认值为空;Score(分数),字节类型,必填字段,默认值为0;StudentNum(拥有该课程成绩学生的学号),数字类型的整形,必填字段,默认值为空。设置字段ScoreID为主关键字。增长若干数据。 (9) 退出microsoft Access2000程序。 8.4 结构化查询语言SQL 用户经过SQL(Structed Query Language,结构化查询语言)来访问数据库中的数据,使用SQL语句能够对数据库中的数据进行查询、增长、删除记录,修改记录中的数据。几乎全部的数据库都支持SQL语言,编写数据库应用程序必须学习SQL语言。 8.4.1 Select语句 Select语句是最经常使用的语句,能够从数据库的表中得到知足一些条件的数据集。常见的Select语句以下:  select * from student 从表Student表中选择全部字段的全部记录  select StudentNum,StudentName from student 从表Student表中选择字段StudentNum和字段StudentName的全部记录  select * from score where StudentNum=1 从表score表中查找学号StudentNum=1同窗的全部课程的成绩。 8.4.2 Insert语句 用于向数据库表中插入一个新记录。例如,向表student中插入一个新纪录的语句以下: Insert student (StudentNum,StudentName,StudentSex)Value(5,"田七","男") 8.4.3 Delete语句 用于删除数据库表中的一个记录。例如,删除student表中学号为1的学生,语句以下: Delete From student where StudentNum=1 8.4.4 Update语句 更新数据库的Student表中学号为1的学生名字为"陈七": Update Student Set StudentName="陈七" Where StudentNum=1。 8.5 用Connection对象链接数据库。 Connection类对象用来链接数据库。ADO.NET中有两类Connection对象,一类用于微软的Sql Server数据库,该对象链接微软SQL数据库时效率较高,另外一类用于其它支持ODBC的数据库。链接Sql Server数据库序应引用以下命名空间: Using System.Data; using System.Data.SqlClient; 链接其它数据库序应引用以下命名空间: Using System.Data; using System.Data.OleDb; 使用SqlConnection的例子: string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; SqlConnection conn=new SqlConnection(txtConn);//创建链接 其中,DATABASE为数据库名称,这里为Northwind,是微软Sql Server数据库自带的数据库例子,必须安装此数据库才能使用。UID为用户名,PWD为密码,Northwind数据库安装后的用户名为sa,密码为空。SERVER为所使用的数据库服务器,这里数据库服务器和数据库应用程序在同一台计算机中,所以为localhost,中文意义是本地主机。 使用OleDbConnection的例子: string txtConn= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb"; OleDbConnection conn = new OleDbConnection(txtConn);//创建链接 Provider为所使用的数据库驱动程序。DataSource为数据库的位置,有时还增长参数ConnectionTimeOut,为连接超时时间,默认为15秒。 也能够使用Visual Stutdio.Net创建链接,例子见8.10B。 Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\myFolder\*.accdb;Persist Security Info=False; http://zm10.sm.cn/?src=http%3A%2F%2Fwww.codefans.net%2Farticles%2F321.shtml&uid=56c023f99f0b87c81990565cfbf7702e&hid=3f68bec9381b228930fa08d2f35779b4&pos=8&cid=9&time=1431233777690&from=click&restype=1&pagetype=0300004000000402&bu=web&uc_param_str=pidi 8.6 Command对象 创建链接后,ADO.Net经过Command对象用SQL语句来访问数据库中的数据,对数据库中的数据进行查询,增长、删除记录,修改记录中的数据。具体用法以下: string txtCommand="SELECT * FROM student"; 使用OleDbCommand OleDbCommand Command1=new OleDbCommand(txtCommand,conn); 使用SQLCommand SQLCommand Command1=new SQLCommand(txtCommand,conn); 例子8.6:链接方式数据库应用程序。有时,数据库应用程序使用链接方式可能更方便一些,例如,用户的注册信息应该当即存到数据库中。下边的例子模拟用户注册,首先请用户输入我的信息,单击注册按钮,用SQL语句把数据存到StudentI数据库的Student表中。具体步骤以下: (1) 新建项目。放三个Label控件到窗体,修改属性Text分别为:学号、姓名、性别。 (2) 放三个TextBox控件到窗体,修改属性Text都为空。TextBox1输入学号,TextBox2输入学生姓名,TextBox3输入学生性别。 (3) 引入命名空间using System.Data.OleDb; (4) 增长变量:OleDbConnection conn; OleDbCommand da; (5) 增长一个按钮,属性Text="增长记录"。界面以下图。为其增长单击事件函数以下: private void button1_Click(object sender, System.EventArgs e) { if(textBox1.Text==""&&textBox2.Text==""&&textBox3.Text=="") { MessageBox.Show("全部项都必须填写!"); return; }//注意,下边语句必须保证数据库文件studentI.mdb在文件夹D:\\vc#中 string txt1= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\vc#\\studentI.mdb"; string txt2= "Insert Into Student(StudentNum,StudentName,StudentSex) Values('"; txt2+=textBox1.Text + "' , '"; txt2+=textBox2.Text + "' , '"; txt2+=textBox3.Text+"')";//最后结果要保证在TextBox中输入的内容被单引号括住 conn = new OleDbConnection(txt1); conn.Open(); da=new OleDbCommand(); da.CommandText=txt2; da.Connection=conn; da.ExecuteNonQuery(); textBox1.Text=""; textBox2.Text=""; textBox3.Text=""; conn.Close(); } (6) 运行,输入学号,姓名,性别,单击《增长记录》按钮,察看数据库,能够看到增长了一个记录。请读者修改成删除一个记录,修改一个记录。 8.7 DataAdapter对象 DataAdapter对象包含4个Command对象:SelectCommand对象,InsertCommand对象,UpdateCommand对象,DeleteCommand对象,完成对数据库中的数据的选择,插入,更新,删除等功能。DataAdapter对象隐藏了Connection对象和Command对象沟通的细节,方便使用,在许多数据库应用中,都使用DataAdapter对象。使用DataAdapter对象例子以下: string txtConn= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb"; OleDbConnection conn=new OleDbConnection(txtConn);//创建链接 string txtCommand="SELECT * FROM student"; OleDbDataAdapter da=new OleDbDataAdapter(txtCommand,conn); 8.8 DataSet对象 DataSet对象从数据库中取出感兴趣的数据,包括指定表和表中知足条件的记录,DataSet对象被创建在内存中,能够包含若干表,能够认为是数据库在内存中的一个子集。DataSet对象只在获取或更新数据时保持和数据库链接,其它时间都是断开的。 8.8.1 使用DataSet的优势 在传统的数据库应用程序中,必须创建与数据库的链接,并在数据库应用程序运行过程当中保持链接状态。出于各类缘由,该方法在许多数据库应用程序中是不实用的。  通常状况下,数据库只能够维持少许的并发链接。维持过多并发链接将下降数据库的整体性能,增长数据库应用程序的访问时间。保持四个用户链接的执行也许还能够接受,但链接100个用户时可能就不行了。同时访问Web数据库的访问者可能很是多,例如大型网上商店,全部访问都保持链接是不现实的。  在Web应用程序中,浏览器从服务器请求网页,服务器发送该页后,服务器就再也不与浏览器有任何链接,直到下一次请求为止。在这种状况下,维持打开的数据库链接是不可行的,由于没有办法知道数据使用者(客户端)是否还将对数据库访问。  若是数据库应用程序的多个控件需对数据库数据操做,则多个控件都必须和数据库创建链接,或者为这些控件设计一种方式以相互传递数据。这使问题变得复杂。 出于以上这些缘由,ADO.NET数据库访问被设计为以不链接的数据模型为基础,应用程序只在获取或更新数据时保持链接,其它时间都是断开的。因为数据库并未被大部分时间空闲的链接占用,因此它能够为更多用户服务。 8.8.2 数据集DataSet概念 在不链接的数据模型中,每次数据库应用程序须要处理下一条记录时都链接回数据库是不可行的,这样作会大大消除使用不链接数据的优越性。解决方案是临时存储从数据库检索的记录,而后使用该临时集。这即是数据集的概念。数据集DataSet是从数据库检索的记录的缓存。数据集DataSet中包含一个或多个表(这些表基于源数据库中的表),而且还能够包含有关这些表之间的关系,以及对表包含数据的约束信息。数据集DataSet的数据一般是源数据库内容的子集,能够用与操做实际数据库十分相似的方式操做数据集DataSet,但操做时,将保持与源数据库的不链接状态,使数据库能够自由执行其余任务。 由于数据集DataSet是数据库数据的私有子集,因此它不必定反映源数据库的当前状态,所以,须要常常更新数据集DataSet中的数据。能够修改数据集DataSet中的数据,而后把这些修改写回到源数据库。为了从源数据库获取数据和将修改写回源数据库,请使用数据适配器DataAdapter对象。数据适配器DataAdapter对象包含更新数据集DataSet和将修改写回源数据库的方法。DataAdapter.Fill()方法执行更新数据集DataSet操做。DataAdapter.Update()方法执行将修改写回源数据库操做。 尽管数据集是做为从数据库获取的数据的缓存,但数据集与数据库之间没有任何实际关系。数据集是容器,它用数据适配器的SQL命令或存储过程填充。 8.8.3 使用DataSet对象 使用DataSet对象例子以下: string txtConn= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\VC#\\studentI.mdb"; OleDbConnection conn=new OleDbConnection(txtConn);//创建链接 string txtCommand="SELECT * FROM student";//将表student全部记录从数据库取出 OleDbDataAdapter da=new OleDbDataAdapter(txtCommand,conn); DataSet MyDataSet=new DataSet();//创建DataSet对象MyDataSet da.Fill(MyDataSet,"MyTable");//将数据填充到数据集MyDataSet中,新表名为:MyTable 下例说明了DataSet的使用方法:  添加记录 DataRow dr=MyDataSet.Tables["Student"].NewRow(); dr["StudentNum"]=4; dr["StudentName"]="鲁豫"; dr["StudentSex"]="女"  修改Student表中第0个记录的"StudentName"字段的值为"田歌" MyDataSet.Tables["Student"].Rows.[0]["StudentName"]="田歌";  删除Student表中第0个记录 MyDataSet.Tables["Student"].Rows.[0].Delete();  恢复数据 if(MyDataSet.HasErros) MyDataSet.RejectChanges();  检查DataSet是否有改动 if(MyDataSet.HasChanges()) da.Update(MyDataSet);//更新数据库 8.8.4 为DataSet对象中的表指定主键、创建关系 为DataSet对象中的表指定主键、创建关系,能够保证数据的完整性,例如,主键取值不能重复,不能删除主表中的数据(例如某个学生),而不删除另外一个表中和其有关的数据(例如另外一个表中的学生成绩)等等。  设置表的主键: DataColumn[] pKey=new DataColumn[1]; pKey[0]=MyDataSet.Tables["Student"].Columns["StudentNum"]; MyDataSet.Tables["Student"].PrimaryKey=pKey;  创建两个表的联系: MyDataSet.Relations.Add("StudentNum", MyDataSet.Tables["Student"].Columns["StudentNum"], MyDataSet.Tables["Score"].Columns["StudentNum"]); 8.9 用DataGraid控件显示数据和数据绑定 DataGraid控件用来按行和列格式显示数据表中的数据。DataGraid控件属性DataSource用来指定数据表所在的数据集DataSet对象。DataGraid控件属性DataMember用来指定在数据集DataSet对象中要显示的数据表的名字。当这两个属性被正确设定,DataGraid控件将以网格形式正确显示指定数据表。DataGraid控件中的数据被修改后,DataSet对象中相应数据表中的数据也被修改。这叫作数据绑定。数据绑定有两个要点:第一,数据绑定控件能按绑定的数据源正确显示数据源的数据,第二,在数据绑定控件中被修改的数据能被正确写回数据源,这里的数据源通常是数据集DataSet对象中的一个表或者是表中的一个字段。许多控件均可以数据绑定。 8.10 不链接数据库应用程序的完整的例子 例8.10A: (1) 新建项目。增长语句using System.Data.OleDb; (2) 添加控件DataGraid,属性Name=dataGrid1 (3) 为Form1类增长变量:OleDbConnection conn; OleDbDataAdapter da; DataSet ds; (4) 为Form1类增长一个方法: private void OnLoadData() { string txtConn= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\vc#\\studentI.mdb"; conn=new OleDbConnection(txtConn); string txtCommand="SELECT StudentName, StudentNum, StudentSex FROM Student"; da=new OleDbDataAdapter(txtCommand,conn); ds=new DataSet("ds"); da.Fill(ds,"Student"); dataGrid1.DataSource=ds; dataGrid1.DataMember="Student"; } (5) 在构造函数中增长语句 OnLoadData(); (6) 运行,能够看到表Student中的信息。运行效果如上图。 例8.10B:使用Visual Stutdio.Net链接数据库StudentI并显示Student表,具体步骤以下: (1) 新建项目。在窗体中放置控件OleDbConnection,其属性Name=OleDbConnection1。单击控件oleDbConnection属性ConnectionString的下拉列表的箭头,在列表中选择新建链接,打开《数据连接属性》对话框,选择提供程序选项卡页,选择OLE DB提供程序为Microsoft.Jet.OLEDB.4.0后,单击下一步按钮,在1.选择或输入数据库名称下的编辑框中,单击其后按钮,选择数据库StudentI。在2.输入登陆服务器信息下,选中使用指定的用户名称和密码单选按钮,在用户名称中输入Admin,选中空白密码多选按钮。单击测试链接按钮,应出现测试链接成功对话框。按肯定按钮退出。 (2) 在窗体中放置控件DataGrid,其属性Name=dataGrid1。 (3) 选择菜单项视图/服务器资源管理器,打开服务器资源管理器窗口,能够看到新创建的链接,单击前边的加号,展开此链接树后,再展开表(Table)树,能够看到数据库中的全部表名。拖表Student到窗体控件DataGrid中,将自动增长控件oleDbDataAdapter1到窗体。 (4) 单击oleDbDataAdapter1选中它,单击菜单项数据/生成数据集…,打开生成数据集对话框,他选择默认值。按肯定按钮退出。自动增长数据集DataSet对象dataSet11。 (5) 修改DataGrid1属性DataSource= dataSet11,属性Datamember=Student,Student为表名。此时在DataGrid1中可看到表的表头。 (6) 在构造函数中增长语句以下: oleDbDataAdapter1.Fill(dataSet11); (7) 运行,能够在控件DataGrid1中看到表Student的内容。 (8) 如下将标题改成中文。选中控件DataGrid1,单击其属性TableStyles后的标题为…的按钮,打开DataGridTableStyle集合编辑器对话框,单击添加按钮,增长一个dataGridTableStyle对象。从属性MapingName的下拉列表中选择Student表。单击其属性GridColumnStyles后的标题为…的按钮,打开DataGridTextBoxColumn集合编辑器对话框,单击添加按钮,增长一个dataGridTextBoxColumn对象。修改HeadText属性为:学生姓名,从属性MapingName的下拉列表中选择Student表的StudentName字段。按此办法,再增长两个dataGridTextBoxColumn对象,修改HeadText属性分别为:学生学号、学生性别,属性MapingName分别为:StudentNum、StudentSex。 (9) 运行,能够在控件DataGrid1中看到表头为中文。 8.11 修改数据并保存修改的数据到源数据库 在控件DataGrid1中能够修改数据,能够增长记录。现增长一个按钮,标题为:删除,单击此按钮后,将删除控件DataGrid1中选定的纪录。为其增长单击事件函数以下: private void button1_Click(object sender, System.EventArgs e) { int x; x=dataGrid1.CurrentRowIndex; dataSet11.Tables["Student"].Rows[x].Delete(); } 运行,选中一个控件DataGrid中某行(一条记录),单击按钮能够删除此行。因为排序等缘由,控件DataGrid中数据记录的顺序可能和dataSet11中表的记录的顺序不一致,所以此方法删除数据可能出出错。修改按钮单击事件函数以下: private void button1_Click(object sender, System.EventArgs e) { int x; object z; x=dataGrid1.CurrentRowIndex; z=dataGrid1[x,1]; DataRow foundRow; object[] findTheseVals = new object[1]; findTheseVals[0] = z; foundRow = dataSet11.Tables["Student"].Rows.Find(findTheseVals); if(foundRow != null) foundRow.Delete(); } 为了保存修改的数据到源数据库,现增长一个按钮,标题为:保存数据,单击此按钮后,将保存修改的数据到源数据库。为其增长单击事件函数以下: private void button2_Click(object sender, System.EventArgs e) { if(dataSet11.HasChanges())//在退出程序时,也应用此函数检查,提醒用户是否存盘 oleDbDataAdapter1.Update(dataSet11); } 运行,增长一个记录,单击第二个按钮,关闭程序,再打开,能够看到新增的记录被显示,说明新增记录已被存到源数据库。 8.12 其它数据绑定控件 本例用TextBox控件显示Student表,具体步骤以下: (1) 新建项目。 (2) 从工具箱中,将3个Label控件放到窗体上,属性Text分别为:学号、姓名、性别。 (3) 从工具箱中,将 3个TextBox控件放到窗体上,属性Text都为空。 (4) 从“工具箱”的“数据”选项卡中,将 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”启动,它将帮助您建立链接和适配器。 (5) 在该向导中,执行下列操做:  在第二个窗格中,建立或选择一个指向数据库StudentI的链接。  在第三个窗格中,指定您要使用 SQL 语句来访问数据库。  在第四个窗格中建立如下 SQL 语句:SELECT * FROM Student  单击“完成”完成该向导。 (6) 从“数据”菜单中选择“生成数据集”。 若是看不到“数据”菜单,请在窗体中单击;该窗体必须具备焦点,该菜单才会出现。 (7) 选择“新建”选项,将该数据集命名为 DataSet1。在“选择要添加到数据集中的表”下面的列表中,确保选择了“Student”。 选中“将此数据集添加到设计器”,而后单击“肯定”。 从“文件”菜单中选择“所有保存”,存全部文件。 (8) 选中textBox1控件的属性DataBindings,单击前边+号展开,选中Text属性,从下拉列表中选择dataSet11 - Student.StudentNum。一样办法textBox2为dataSet11 - Student.StudentName,textBox3为dataSet11 - Student.StudentSex。 (9) 增长主窗体Form1的Load事件函数以下: private void Form1_Load(object sender, System.EventArgs e) { dataSet11.Clear(); oleDbDataAdapter1.Fill(dataSet11); } (10) 运行,能够看到第一个学生状况。现增长移动记录功能,以显示不一样学生状况。 (11) 增长变量:BindingManagerBase Navigator; (12) 修改主窗体Form1的Load事件函数以下: private void Form1_Load(object sender, System.EventArgs e) { dataSet11.Clear(); oleDbDataAdapter1.Fill(dataSet11); Navigator=this.BindingContext[dataSet11,"Student"]; } (13) 从工具箱中,将 4个Button控件放到窗体上,属性Text分别为:第一记录、下一记录、前一记录、最后记录。各个按钮的单击事件函数以下: private void button1_Click(object sender, System.EventArgs e) { Navigator.Position=0;//第一记录 } private void button2_Click(object sender, System.EventArgs e) { if(Navigator.Position!=Navigator.Count-1) Navigator.Position+=1;//下一记录 } private void button3_Click(object sender, System.EventArgs e) { if(Navigator.Position!=0) Navigator.Position-=1;//前一记录 } private void button4_Click(object sender, System.EventArgs e) { Navigator.Position=Navigator.Count-1;//最后记录 } (14) 运行,能够看到第一个学生状况。单击4个按钮能够移动记录。 8.13 创建主从关系表 在数据库StudentI中,在显示表Student和Score时,但愿选中某个学生时,表Score只显示此学生的成绩,两个表的这种关系叫作主从关系。如今实现这两个表的主从关系。具体步骤以下: (1) 新建项目。 (2) 从“工具箱”的“数据”选项卡中,将 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”启动,它将帮助您建立链接和适配器。 (3) 在该向导中,执行下列操做:  在第二个窗格中,建立或选择一个指向数据库StudentI的链接。  在第三个窗格中,指定您要使用 SQL 语句来访问数据库。  在第四个窗格中建立如下 SQL 语句:SELECT * FROM Student  单击“完成”完成该向导。 (4) 将第二个 OleDbDataAdapter 对象拖到窗体上。“数据适配器配置向导”再次启动。 (5) 重复第(3)步,其中有如下差别:  在第二个窗格中,选择上次所使用或建立的同一链接。  建立下列 SQL 语句来访问Score表:SELECT Score.* FROM Score (6) 从“数据”菜单中选择“生成数据集”。 若是看不到“数据”菜单,请在窗体中单击;该窗体必须具备焦点,该菜单才会出现。 (7) 选择“新建”选项,将该数据集命名为 DataSet1。在“选择要添加到数据集中的表”下面的列表中,确保选择了“Student”和“Score”。 选中“将此数据集添加到设计器”,而后单击“肯定”。从“文件”菜单中选择“所有保存”,存全部文件。 (8) Visual Studio 生成某类型化数据集类 (DataSet1) 和定义该数据集的架构。将在“解决方案资源管理器”中看到新架构 (DataSet1.xsd)。 (9) 在“解决方案资源管理器”中,双击刚建立的数据集的架构(名为 DataSet1.xsd)。“XML 设计器”在“架构”视图中打开,显示数据集内的两个表。 (10) 从“工具箱”的“XML 架构”选项卡中,将 Relation 对象拖到Score表(子表)上。“编辑关系”对话框打开,其中带有从这两个表中派生的默认值。父元素为Student表,子元素为Score表,键字段和外键字段都为StudentNum。其它不修改选默认值。单击“肯定”按钮,关闭“编辑关系”对话框。在“XML 设计器”中,这两个表之间显示一个关系图标。若是须要更改关系设置,则能够右击相应的关系,选择“编辑关系”。 (11) 保存该架构并关闭XML设计器。此刻,为执行从数据库获取信息并转移到数据集的操做所需的所有设置均已完成。能够向窗体添加显示数据的控件了。 (12) 返回到建立该项目时已打开的默认窗体 (Form1)。从“工具箱”的“Windows 窗体”选项卡中,将 DataGrid 控件拖到窗体上,Name=dataGrid1。RowHeadersVisable=false。在“属性”窗口中,将 DataSource 属性设置为 dataSet11,将 DataMember属性设置为Student。 (13) 从“工具箱”的“Windows 窗体”选项卡中,将 DataGrid 控件拖到窗体上,Name=dataGrid2。在“属性”窗口中,将 DataSource 属性设置为 dataSet11,将 DataMember属性设置为Student.StudentScore,设置这两个属性将网格绑定到关系对象,以便网格只包含dataGrid1表中选择的学生成绩。 (14) 增长主窗体Form1的Load事件函数以下: private void Form1_Load(object sender, System.EventArgs e) { dataSet11.Clear(); oleDbDataAdapter1.Fill(dataSet11); oleDbDataAdapter2.Fill(dataSet11); } (15) 运行,能够看到两个DataGrid 控件,dataGrid1显示学生状况表,dataGrid2显示dataGrid1表中选中的学生的成绩。在dataGrid1表中选择不一样学生,dataGrid2显示相应学生的成绩。 第九章 ASP.Net编程基础知识 本章首先介绍用ASP.Net技术编制服务器端动态网页所需的网络和HTML标记语言方面有关的知识。而后介绍ASP.Net技术基础。 9.1 网络基础 用ASP.Net技术编制服务器端动态网页,必然要和网络打交道,具有一些网络方面的知识是必要的。这里假设读者已经学过计算机基础课程,在此基础上,进一步介绍用ASP.Net技术编制服务器端动态网页所需的必备网络基础知识。 9.2.1 局域网、广域网和INTERNET 把分布在不一样地理区域的计算机以及专门的外部设备利用通讯线路互连,使各个计算机之间可以相互通信,实现信息和资源共享,就组成了计算机网络。在一个较小区域,例如在单位内部组成的计算机网络,称为局域网。一个较大区域的计算机网络,称为广域网。为了使各个局域网之间互相通信,能够把各个局域网连起来,组成广域网。如将全世界范围的计算机网络采用TCP/IP网络传输协议联到一块儿,则组成INTERNET。 INTERNET提供了许多服务,例如:远程登陆服务Telnet、文件传送服务FTP、电子邮件服务E-mail、电子公告板系统BBS、万维网WWW(Web)、电子商务、IP电话等等。本课程主要介绍万维网WWW中服务器端动态网页的设计方法。 9.2.2 网络传输协议 网络的目的是为了通信,共享资源。通信即传输数据,为传输数据应遵照必定规则,这个规则叫网络传输协议。不一样的网络操做系统采用不一样的网络传输协议。而在INTERNET中,为了传输数据,你们都必须采用相同的传输协议,即TCP/IP协议。 9.2.3 IP地址 INTERNET中有成千上万台计算机,它容许任何两台计算机之间进行通信,为了区分不一样的计算机,必须给每一台计算机一个惟一的编号,这个编号叫计算机的IP地址,它是一个32位二进制数,用四个十进制数表示,中间用点隔开,每一个十进制数容许值为0-255(一个字节),例如,202.112.10.105,这种记录方法叫点数记法。一个IP地址通常由两部分组成,网络标志号及此网络中的计算机号,例如,202.112.10.105若是是一个C类地址,其网络标志号为202.112.10.0,网络中的计算机号为105。一个局域网络中,全部计算机中都应有相同的网络标志号,每一个计算机有不一样的计算机号,两个不一样局域网络,其网络标志号必须不一样,但不一样网络中,主机号能够相同。每一个计算机要和INTERNET联接,必须有本身的IP地址。 32位IP地址被分为5类,即A、B、C、D和E类地址。A类地址的第一字节为网络标志号,其他3字节为计算机号。B类地址的前两个字节为网络标志号,其他2字节为计算机号。C类地址的前三个字节为网络标志号,最后一字节为计算机号。D为特殊地址,E为私有地址。设置TCP/IP时,有一项叫子网掩码,用子网掩码表示IP地址是哪类地址,A类地址的子网掩码是:255.0.0.0,B类地址的子网掩码是:255.255.0.0,C类地址的子网掩码是:255.255.255.0。我国大部分单位的IP地址为C类地址。 9.2.4 域名 用点数法表示的IP地址,虽然简单,但很难记忆,为了解决此问题,能够为INTERNET网中的每台计算机起一个名字,在INTERNET中叫域名,并使此计算机的名字和IP地址对应起来,使咱们能够使用名字访问计算机,就象咱们使用IP地址同样。例如微软的域名www.microsoft.com,清华大学的域名www.tsinghua.deu.cn。 在INTERNET中访问其它计算机必须使用IP地址,所以域名必须转换为IP地址。实现域名(和WINDOWS/2000/98中计算机名有区别)和IP地址转换的软件叫DNS(域名服务器)。在网内有一台计算机运行DNS服务器软件,这台计算机叫DNS服务器。它负责必定区域内的计算机域名和IP地址的转换,这个区域通常是一个网的内部的全部计算机。当网内的计算机用域名和其它计算机通信时,则首先呼叫DNS服务器,DNS服务器送出此域名对应的IP地址,网内的计算机收到IP地址后,再利用IP地址和其它计算机通信。若是,本DNS不能转换相应的域名,则向上一级DNS申请转换。计算机要和INTERNET联接,设置TCP/IP时,必须设置DNS服务器IP地址。 9.2.5 URL 咱们在用浏览器浏览网页时,其实是用URL来定位一个网页的。URL是Uniform Resource Location(统一资源定位器)的简称。他的通常格式是:传输协议://域名:端口号/全路径文件名。http:为www专用超文本传输协议。域名,即上边讲到,例如www.microsoft.com微软域名,此处能够是IP地址,其格式为http://IP地址:端口号/全路径文件名。全路径文件名:它指示访问文件的全路径,只写出宿主目录如下的全路径文件名。若是在访问主页时不指定此项,则访问其默认主页,例如当咱们在浏览器的URL(地址)处键入http://www.sun.com/时,将访问sun公司默认主页。当光标移到某关键词,光标变为手型,单击此关键词则显示和此关键词相联系的URL所指定的网页。此网页可能在Internet中某网站的计算机中。在www系统中,每个网页都有本身的URL,由它决定此网页在www网中的具体位置。它很象计算机文件系统中的文件全路径名。 9.2.6 端口号 一台计算机上可能运行多个服务器软件,如www服务器软件(可能不仅一个)、ftp服务器软件等,它们的IP地址是相同的。为了区分不一样的服务器,为每一个服务器编一个号,叫端口号。此项不是必须的,若是一台计算机仅运行一个www服务器软件,则通常使用默认端口号80,运行一个ftp服务器软件,其端号为采用默认值20或21等,如采用默认端口号,端口号能够不写。但若有多个相同服务器运行,则应指定不一样端口,其中不是采用默认端口号值的服务器软件在使用URL定位时,则应指明使用的端口号。 9.2.7 HTML,HTTP和网页 网页使用HTML标记语言写成。HTML标记是用特殊的ASCII字符来定义网页中的格式、字体、颜色等内容。因为各类系统中,例如Windows、Linux、Unix和苹果系统,都支持ASCII字符标准,不一样系统中的浏览器均可以解释这些ASCII标记,将其所表示的网页在屏幕中显示。这样,不一样的系统均可以使用统一的ASCII标记,访问其它系统中的网页。网页是WWW系统最小传输单位,它是用HTML语言写的具备必定格式的数据集合,可供浏览器显示。HTTP是超文本传输协议,用在WWW服务器和浏览器之间传输网页。本质上是TCP/IP协议,全部的WWW服务器和浏览器都应遵照HTML和HTTP协议,才能使同一网页在任何计算机中,使用任何浏览器都能显示同一画面,但实际上各公司浏览器软件是有差别的,最明显的是IE和Netscape之间在显示同一网页时,是有差异的。作好网页后,应用不一样浏览器测试一下,看是都能经过 9.2.8 Web服务器和浏览器工做方式 Web是基于客户机/服务器模式,运行Web服务器软件的计算机叫Web服务器,运行浏览器的计算机叫客户机。服务器24小时开机,在指定的文件夹(宿主目录)上存贮大量的网页,这些网页用URL定位,Web服务器软件老是在监视是否有浏览器访问本身。客户机用浏览器访问Web服务器,在浏览器上键入要访问的网页的URL,例如:http://www.sun.com/,用DNS(域名系统)转换www.sun.com域名为IP地址,经过IP地址和sun公司的Web服务器通信,sun公司的Web服务器接到信息后,因为未指定文件名,将默认主页送出。浏览器接到主页数据,将其显示。主页中列出各项主题,每当鼠标通过这些主题时,鼠标变为手形,双击此主题,将和主题有关的网页调入并显示。这种方法叫超连接。超连接的本质是:单击该题目,转换为所联系网页的URL,在此URL中,在文件路径名处,指出了网页在Web服务器中路径及文件名,当把这些信息送给Web服务器后,Web服务器就送出指定的网页。 9.2.9 宿主目录、默认主页及网站 默认文档(主页)就是当用户用不带文件名的URL访问www服务器时送出去的网页。它所在的目录,叫宿主目录。宿主目录下的文件对访问者都是可见的。宿主目录和默认文档名在不一样的Web服务器中是不一样的。Windows2000的IIS服务器宿主目录为…\InetPub\wwwroot,默认文件名(主页名)为Default.htm。大部分www服务器容许修改宿主目录。当用户使用不带文件名的URL访问www服务器时,而且宿主目录中不存在默认文档,并且www服务器容许列出宿主目录下的全部目录列表,将返回宿主目录下的全部目录列表。 由此,能够看出,建立一个网站,就是建立一个文件夹,在文件夹中存入预先设计好的网页。而后,把此文件夹设定为宿主目录,例如Windows2000的IIS服务器默认的宿主目录为…\InetPub\wwwroot。而后运行WEB服务器软件,例如微软的IIS服务器软件。网络上的其它计算机就能够经过URL访问该计算机中宿主目录中的网页。 9.2.10 静态网页 建立一个网站,必须编制若干网页,网站才算建成。静态网页是预先作好的网页,在被访问时不被修改。相对讲静态网页制做比较容易,即便不懂超文本语言,使用FrontPage,Dreamerware就能够完成,而动态网页制做则比较困难。 9.2.11 客户端动态网页 例如,网页根据上下午,晚上问客人早上好,下午好和晚上好,又如,网页对于用户输入的内容进行检查,根据用户的选择完成不一样的功能。有时,但愿在浏览器内实现动画,放映影片等,这些都是客户端动态网页的例子。这能够在HTML语言加入Javascript或VBscript脚本语言来实现。也能够在HTML语言嵌入Java小程序(Applet)来实现。所以应比较系统的学习一下HTML语言,还应该学习Javascript或VBscript脚本语言,进一步还能够学习Java语言。另外使用微软的AxtiveX控件也是实现客户端动态网页的一种方法。 9.2.12 服务器端动态网页 举一个客户登记的例子,当客人在客户机端用浏览器填好表格后,将表格数据返回Web服务器,应把这些信息存入运行Web服务器的计算机内的数据库。Web服务器并不能完成此工做,它调用运行Web服务器的计算机上的其它程序完成,这个程序叫Web服务器应用程序。又如,创建一个中学校园网,能够创建一个学生状况数据库,可供校长、教导处、老师查询,可能要根据一些条件进行查询,如某班的学生,得到三好生的学生等,浏览器把所查找的条件送给Web服务器后,Web服务器必须调用Web服务器应用程序完成此项工做,此程序查到有关数据后,要变成用HTML语言的网页,送给Web服务器,再送给请求此项内容的客户机。编制服务器端的Web服务器应用程序的方法主要有以下几种:CGI,ISAPI,NSAPI,ASP,ASP.NET,JSP,PHP等。ASP.NET技术是微软最新提出的编制动态网页技术。如今Web数据库比较流行的方法是三层数据库,以下图: 访问数据库的方法 这里的3层是指浏览器、Web服务器和数据库。一些文献中提到4层数据库系统是在Web服务器和数据库之间增长一个应用服务器层。本章既是讲述设计Web服务器应用程序的方法。 9.2.13 修改宿主目录及创建虚拟目录 建立一个网站,必须有以下条件:第一,有固定的IP地址。第二,安装并运行Web服务器软件,例如Windows操做系统下的IIS,Unix或Liunx下的Aparc。第三,在宿主目录下有可供游览的网页。Windows2000的IIS服务器的宿主目录为\InetPub\wwwroot。能够修改宿主目录为其它任意目录,修改Windows2000的IIS服务器的宿主目录方法以下: (1) 在D盘建文件夹:ASP (2) 打开控制面板,双击管理工具图标。 (3) 双击Internet 服务管理器图标。 (4) 右击第一个+号后的计算机名,出现弹出菜单,选择菜单项新建/WEB站点,按向导步骤选择D:/asp为新站点。单击第一个+,打开文件树,右击默认WEB站点,将其中止。右击管理WEB站点,将其中止。 请读者做以下试验:首先用记事本建立以下网页: <html> <body> 这是个人第一个网页 </body> </html> 以文件名Test.htm存到d:/asp文件夹中,查找本机IP地址,假如为:202.204.206.98。在另外一台计算机中打开浏览器,输入地址:http://202.204.206.98/Test.htm,在浏览器中应能看到文字:这是个人第一个网页。由此可知,必须把建立的网页拷贝到宿主目录下。从本机访问宿主目录下的网页时,能够在浏览器的URL(地址)处键入:http://localhost/网页以宿主目录为根目录的全路经。其余人访问时,能够在浏览器的URL(地址)处键入:http://IP地址或域名/网页以宿主目录为根目录的全路经。 也能够用建虚拟目录的方法,存放网页,具体步骤以下: (1) 打开控制面板,双击管理工具图标。 (2) 双击Internet 服务管理器图标。 (3) 单击计算机名前的+号。 (4) 右击默认Web站点,出现弹出菜单,选择菜单项新建/虚拟目录,按向导步骤选择D:/asp为新虚拟目录。此时,在默认Web站点下将会出现设定的虚拟目录,此目录容许其余人访问。 请读者想想,如把文件Test.htm拷贝到新虚拟目录,在另外一台计算机中如何访问此网页。 9.2 HTML标记语言 网页使用HTML标记语言写成。HTML标记是用特殊的ASCII字符来定义网页中的格式,字体等等特色。因为各类系统中,都支持ASCII字符标准,不一样系统中的浏览器均可以解释这些ASCII标记,将其所表示的网页在屏幕中显示。这样,不一样的系统均可以使用统一ASCII标记,访问其它系统中的网页。 若是有WEB服务器时,用IE浏览器显示网页前,必须把网页拷贝到宿主目录下,例如Windows2000的IIS服务器为\InetPub\wwwroot。访问此网页时,在浏览器的URL(地址)处键入此网页的URL,回车便可。 为了在没有WEB服务器时,能用IE浏览器显示静态网页,首先将IE的默认网页设置为about:blank。而后运行IE,在地址栏中输入网页文件的路径,回车便可。修改IE的默认网页为about:blank及IE的默认编辑器为写字板的具体办法是:单击IE菜单工具/Internet选项,打开Internet选项对话框。在常规页中的主页选择使用空白页,程序页中,HTML编辑器选择Windows Notepad。 9.2.1 HTML标记 HTML标记是用特殊的ASCII字符来定义网页中的格式、字体、字符对齐方式等特色。其格式为:<标记名称>被控制的文字</标记名称>。其中,<标记名称>为开始标记,</标记名称>为结束标记,通常用来定义被控制的文字的格式或字体等。例如,下列标记使被控制的文字ASP.NET中间对齐:<center>ASP.NET</center> 9.2.2 HTML文件结构 一个网页文件的最基本HTML标记结构以下: <html> <head> <title> 显示在浏览器标题栏中的文字 </title> </head> <body> 这里是网页的内容 </body> </html> <html>表示网页文件的开始,</html>表示网页文件的的结束,网页的全部内容都应在这两个标记之间。<head>…</head>之间能够设定网页的一些信息,<title> </title>之间的文字显示在IE浏览器的标题栏中。<body>…</body>之间时网页的主题内容。由此能够看出,在HTML语法中,HTML标记能够嵌套,一个HTML标记能够包含另外一个HTML标记,但标记的嵌套不能是交错的。下边是一个实际例子: <html> <head> <title> 这是个人第一个网页 </title> </head> <body> <center> 这是个人第一个网页。<BR> 其中&ltBR&gt标记表示换行, 注意,仅键入回车时不能在网页中换行的。<BR> 注意空格的用法, 键入四个空格,网页中只有一个空格。<BR> 增长4个空格,&nbsp&nbsp&nbsp&nbsp是4个空格<BR> 网页中不区分大小写,不一样浏览器显示的效果可能不一样<BR> <!--这是注释,浏览时不显示--> 注意,特殊符号的显示:&lt,&gt,&quot,&amp,&copy,&reg </center> </body> </html> 网页中能够增长一些标记,例如: <html> <head> <title>,<base>,<link>,<isindex>,<meta> </head> <body> HTML文件的正文写在这里… </body> </html> 这些标记的用法见如下各节。 9.2.3 语言字符集的信息 语言字符集的信息用法见下边的网页。主要是选用网页使用的字符集,gb2312是中文字符集。 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>meta的使用</title> </head> <body> meta的使用。 </body> </html> 9.2.4 背景色彩和文字色彩 设置背景和文字色的语法以下:<body bgcolor=#,text=#,link=#,alink=#,vlink=#>其中,bgcolor,text,link,alink,vlink的意义见书,#表示颜色,可用rrggbb表示,rr,gg,bb分别是表示红色,绿色,蓝色的16进制数,例如,红色为:ff0000。下例设置背景色为红色。 <html> <head> <title>设置背景色为红色</title> </head> <body bgcolor=ff0000> 设置背景色为红色。 </body> </html> 9.2.5 页面空白 能够设置页面的上下左右边距,单位为像素。例如设置左边距为20个像素格式以下: <body leftnargin=20> 9.2.6 显示一幅图 下例在网页中显示一幅图。注意<IMG src="file:///D:/耿肇英/ASP.NET教案/baobao048.jpg" width="320" height="240">的写法。其中,file://是文件协议,用来选择本地文件。通常网页中用http://,即超文本协议,此时要求运行WEB服务器。 <html> <head> <title> 增长一幅图形! </title> </head> <body> <IMG src="file:///D:/耿肇英/ASP.NET教案/baobao048.jpg" width="320" height="240"> </body> </html> 9.2.7 超级连接 浏览网页时,当鼠标变为手形时,单击,能够打开另外一个网页,下边的例子在当前窗口打开另外一个网页。 <html> <head> <title> 连接的例子 </title> </head> <body> 这是一个 <a href="C214.htm">连接的例子</a> 点一下带下划线的文字! </body> </html> 9.2.8 超级连接在新窗口打开另外一网页 以下例子: <html> <head> <title> 开一个新窗口例子 </title> </head> <body> <a href="window.htm"target="window_name"> 开一个新窗口! </a> </body> </html> 将光标移到”开一个新窗口”,光标变为手型,单击”开一个新窗口”,将打开另外一个网页窗口,即window.htm网页。window.htm网页以下: <html> <head> <title> New Window Form HyperLink Dir </title> </head> <body> <H1aligh=center>这是新开的窗口!</H1> <center><p>返回原先窗口<a href="C226.htm#window">开新一个(浏览器)窗口</a>"一处 </p></center> </body> </html> 9.2.9 标尺线 标尺线标记格式为:<hr align=left color="red" noshade size=10 width=50>。其中,align为标尺线对齐方式,能够取left,right,center。Color为标尺线颜色。Noshade设定标尺线为没有三维效果的实心黑线。Size为标尺线的厚度。width为标尺线的宽度,能够是像素值,也能够相对于窗口的百分比。实际使用见下例: <html> <head> <title> 标尺线 </title> </head> <body> <hr size=10> <hr width=#> <hr width=50> <hr width=50%> <hr align=left> <hr align=right> <hr width=50% align=left> <hr width=50% align=right> <hr noshade> <hr color="red"> </body> </html> 9.2.10 网页中标题的字体 网页中一共有6种标题字体,见下例: <html> <head> <title> 标题字体 </title> </head> <body> <h1>这是1级标题!</h1> <h2>这是2级标题!</h2> <h3>这是3级标题!</h3> <h4>这是4级标题!</h4> <h5>这是5级标题!</h5> <h6>这是6级标题!</h6> </body> </html> 其中,标题字体是黑体,每一个标题自动插入一个空行。 9.2.11 网页中正文字体 网页中正文字体只有7种大小,能够用以下方法设置正文字体: <html> <head> <title> 正文字体 </title> </head> <body> <font size=7>字号为7,最大字体</font> <font size=6>字号为6</font> <font size=5>字号为5</font> <font size=4>字号为4</font> <font size=3>字号为4</font> <font size=2>字号为2</font> <font size=1>字号为1,最小字体</font> </body> </html> 注意字号只能是1到7。 9.2.12 斜体、粗体字符及为字体增长下划线,删除线 能够使字体为斜体、粗体,为字体增长下划线,删除线。见下例: <html> <head> </head> <body> <b>标记内的字为黑体</b> <br> <i>标记内的字为斜体</i> <br> <u>标记内的字有下划线</u> <br> <tt>标记内的字等宽,例如w和i等宽</tt> <br> H<sub>2</sub> ,2为下标<br> A<sup>2</sup> ,2为上标<br> <s>标记内的字加删除线</s> <br> <strike>标记内的字加删除线</strike> <br> </body> </html> 9.2.13 字体标记的组合使用 见下例: <html> <head> <title> 字体标记的组合使用 </title> </head> <body> <i><font size=5> <b>今天</b> 天气<font size=6>真好!</font> </font></i> </body> </html> 9.2.14 字体的颜色 见下例: <html> <head> </head> <body> <font size=5 color=000000>Black</font>& <font size=5 color=red>Red</font> </body> </html> 9.2.15 客户端字体 每一个操做系统都提供若干字体,网页能够使用操做系统提供的字体,下边的网页显示如何使用这些字体。 <html> <head> </head> <body> <h1>客户端字体事例</h1> <font face="Arial">Arial...</font><br> <font face="Comic Sans MS">Comic Sans MS...</font><br> <font face="Courier">Courier...</font><br> <font face="Courier New">Courier New...</font><br> <font face="Modern">Modern...</font><br> <font face="MS Serif">MS Serif...</font><br> <font face="MS-DOS CP 932">MS-DOS CP 932...</font><br> <font face="Roman">Roman...</font><br> <font face="Script">Script...</font><br> <font face="Small Fonts">Small Fonts...</font><br> <font face="Symbol">Symbol...</font><br> <font face="Times Roman">Times Roman...</font><br> <font face="Times New Roman">Times New Roman...</font><br> <font face="WingDings">WingDings...</font><br> </body> </html> 9.2.16 网页中控件的概念 WEB服务器为了和用户进行交互,必须解决以下问题:首先,用户应能输入一些数据,例如,要查询的条件,用户登陆的信息等等。第二,这些数据用什么方法传到WEB服务器。第三,WEB服务器用那个程序响应用户。为了实现以上功能,必须使用窗体控件,也叫表单控件form,Visual Studio.net中叫WebForm。同时还须要一些其它控件,例如,编辑框控件,列表框控件,下拉列表框控件和按钮等。能够用HTML标记语言定义控件。IE浏览器看到这些标记,就把它显示为相应的控件。控件有许多属性,也能够用HTML标记语言表示,每一个属性用空格分开,用属性名称=属性值格式定义。 9.2.17 窗体控件和其它控件的使用 窗体控件是其它控件的容器,全部其它控件都要放到窗体控件中。Form控件的基本语法以下: <form action=URL method="POST"> <!--在此处增长交互控件,例如编辑框、列表框--> <input type="submit" value="提交" name="B1"> <input type="reset" value="所有重写" name="B2"> </form> 其中,<form action=URL method="POST">…</form>定义Form控件,action是WEB服务器用响应用户程序的URL,method="POST"是数据用POST方法传到WEB服务器,也能够是get方法。用户用交互控件输入信息。<input type="submit" value="提交" name="B1">是一个按钮控件,其类型为submit,按钮标题为:提交,表明此控件的name为B1。当用户单击此按钮,form控件将把控件内的全部交互控件中的数据用POST方法,传递给action指定WEB服务器的程序处理。<input type="reset" value="所有重写" name="B2">也是一个按钮,用户单击此按钮,将清空form控件内的全部交互控件。 form控件内能够增长交互控件,交互控件的使用<input>标记,其语法以下:<input align=top|middle|bottom [check] maxlength=? Name=? Size=? Src=? Type=? Value=?>各个属性意义以下: align为对齐方式,能够取top、middle和bottom。 Type为控件类型,能够是:type="submit"为提交按钮。type="reset"为所有重写按钮。type="text"为编辑框。type="password"为口令编辑框。type="checkbox"为复选框。type="radio"为单选框。type="image"为图形。type="hidden"用户不能看到,能够用来传递网页的一些默认的数据。 [check]只有在type="checkbox"或"radio"时使用,表示缺省被选中。 Maxlength属性在type="text"编辑框时表示最大字符长度。 Name属性表明此控件的名字。 Size属性在type="text"编辑框时表示编辑框最大宽度。 Value如为按钮,则为标题,为编辑框,则为缺省内容。此值将按Name/Value对的形式传递给WEB服务器。 9.2.18 例子:文字输入和密码输入 <html> <head> <title>Form的例子</title> </head> <body> <form method="POST" action="--WEBBOT-SELF--"> 您的姓名:<input type="text" name="T1" size="20"><br> 您的主页的网址:<input type="text" name="T2" size="20"><br> 密码:<input type="password" name="T3" size="20"><br> <input type="submit" value="提交"><input type="reset" value="所有重写"><br> </form> </body> </html> 9.2.19 用FontPage作网页的例子,使用复选框和单选按钮 用FontPage或Dreamerware要比使用记事本编辑网页方便的多。下边用FontPage作网页的例子。下边是具体步骤: (1) 运行FontPage。 (2) 单击菜单文件/新建/网页。出现新建对话框,选择常规页的普通网页,单击肯定按钮,建立一个新网页。 (3) 单击菜单插入/表单/表单,增长一个新表单(form)。在表单中已有一个提交按钮和一个所有重写按钮。将光标移到提交按钮先后回车,增长form的尺寸。 (4) 右击提交按钮,在弹出菜单中选中菜单项表单域属性,出现按钮属性对话框,可修更名称(Name属性)属性,值/标签(value属性)=提交查询内容,按钮类型为提交。按肯定按钮。用一样方法修改另外一个按钮的标题为重置。 (5) 将光标移到第一行,单击菜单插入/表单/复选框。右击复选框,在弹出菜单中选中菜单项表单域属性,出现复选框属性对话框,修更名称(Name属性)属性=水果1。在复选框后键入字符:Banana。用一样方法增长另两个复选框,注意初始状态修改成选中。 (6) 用FontPage建立的网页文件以下: <html> <head> <meta http-equiv="Content-Language" content="en-us"> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <meta name="GENERATOR" content="Microsoft FrontPage 4.0"> <meta name="ProgId" content="FrontPage.Editor.Document"> <title>New Page 2</title> </head> <body> <form method="POST" action="--WEBBOT-SELF--"> <!--webbot bot="SaveResults" U-File="fpweb:///_private/form_results.txt" S-Format="TEXT/CSV" S-Label-Fields="TRUE" --> <p><input type="checkbox" name="水果1" value="香蕉">Banana</p> <p><input type="checkbox" name="水果2" value="苹果" checked>Apple</p> <p><input type="checkbox" name="水果3" value="桔子" checked>Orange</p> <p><input type="submit" value="提交查询内容" name="B1"><input type="reset" value="所有重写" name="B2"></p> </form> </body> </html> 9.3 ASP.NET技术基础 设计静态网页有两种方法:一种是使用记事本,用HTML语言编写,另外一种是使用可视化工具,如FrontPage,Dreamware等。显然,使用可视化工具要方便快捷的多。以往设计服务器端动态网页时,例如ASP,每每只能使用记事本一行一行的写,效率很低。程序员迫切须要一种设计服务器端动态网页的可视化工具,能象使用C#设计Window应用程序同样设计动态网页,使用控件类、属性和事件等面向对象的概念。为了实现这个目的,引入ASP.NET服务器端控件概念。静态网页中通常有一个表单(Form),在表单中能够有多个控件,例如,列表框、编辑框、按钮等等,经过这些控件,完成必定的功能。一样,ASP.NET服务器端控件首先引入运行在服务端WebForm概念,在WebForm中能够放入多个服务器端控件,例如,列表框、编辑框、按钮等等,全部这些控件,都是.NET框架类库中相应类的对象,每一个对象都有本身的属性、方法和事件。这些概念和编制Windows应用程序相应的概念基本相同。这些ASP.NET服务器端控件,也使用HTML标记描述,但这些服务器端控件并不传送这些HTML标记给浏览器解释,而是由Web服务器负责解释,翻译为全部浏览器都能解释的标准HTML标记后传送给浏览器解释,这样就极大地简化了服务器端动态网页的设计,也保证了生成的网页的显示效果和浏览器无关。使用ASP.Net技术建立的服务器端动态网页的扩展名为.aspx。 本节首先介绍ASP.NET服务器端控件基本概念,而后介绍使用记事本编写ASP.NET动态网页的方法,最后介绍如何使用Visual Studio.NET编写ASP.NET动态网页。 9.3.1 HTML服务器端控件 ASP.Net中的HTML服务器端控件和标准的HTML控件有着对应关系,但功能更强大。能够在程序中修改HTML服务器端控件的属性,可以在服务器端响应事件,支持数据绑定等。例如增长一个HTML服务器端控件编辑框用以下HTML语句: <INPUT TYPE=”TEXT” ID=”Text1” MAXLENGTH=16 RUNAT=”SERVER”/> 这里和标准的HTML控件的区别是增长了属性RUNAT=”SERVER”。属性ID是表明这个控件的惟一标志,和Winndows应用程序中的控件属性Name的意义是同样的。HTML服务器端控件是为了方便原来学习HTML或ASP编写Web应用程序的程序员而提供的。若是,你之前是Windows应用程序员,建议使用Web服务器端控件,这些控件不但功能更强大,而使用上更象Windows应用程序中的控件,所以学习更方便。所以这里就不介绍HTML服务器端控件了。 9.3.2 Web服务器端控件 在ASP.NET系统中,除了常规的HTML控件外,还包括Web服务器端控件。同HTML服务器端控件同样,这些控件能够在程序中修改服务器端控件的属性,可以在服务器端响应事件,支持数据绑定等。例如定义一个Web服务器端控件编辑框控件,方法以下: <asp:TextBox id="textBox1" runat="server"/> 服务器端控件不但功能更强大,并且和编制Windows应用程序中的控件使用方法基本一致,所以学习更方便。本书的全部例子都使用Web服务器端控件。 9.3.3 Web Form的事件处理 象Windows应用程序同样,ASP.Net应用程序也采用事件驱动的概念,用户对浏览器的各类操做都被看做事件,事件产生后,Web应用程序用事件处理函数响应事件。但ASP.Net的事件驱动和Windows应用程序的事件驱动有着本质上的区别。Web应用程序的事件产生后,因为事件处理程序在Web服务器端,Web应用程序把事件经过网络使用HTTP协议由浏览器传到Web服务器,在Web服务器执行事件处理程序,把运行结果转变为标准HTML标志的网页,传回浏览器。 在Web事件处理机制中,每一次Web应用程序响应事件都会使得网页从新生成。事实上,一旦服务器完成某一个网页的处理操做并将它传送至浏览器,则会随即移除该网页的全部信息,也就是说,网页中定义的对象和变量在服务器端已不存在了,网页生命周期结束。当Web应用程序再一次响应事件时,服务器又会将上述处理过程重作一次。基于此缘由,咱们说网页是无状态的——即网页变量与控件的数据值并不会保留在服务器上。 所以,咱们增长事件函数时,应考虑网络传播的速度的影响,不能象Windows应用程序那样,响应太多的事件。在网页中,每一个控件都有属性AutoPosBack,其值为true,事件才能自动调用事件处理函数,若是不但愿响应该事件,应将该控件的属性AutoPosBack设为false。 9.3.4 记事本编写ASP.NET动态网页 ASP.NET中的服务器端控件也用HTML标记,但这些服务器端控件的HTML标记并不传送给浏览器解释,而是由Web服务器负责解释,翻译为全部浏览器都能解释的标准HTML标记后传送给浏览器解释。全部ASP.NET服务器端控件都要放到Web窗体(WebForm)中,Web窗体(WebForm)也由Web服务器负责解释。下边是一个最简单的使用服务器端控件的例子: <%@ Page language="c#" %> <html> <head> <title>这是个人第一个ASP.NET网页</title> </head> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) {Label1.Text="如今的时间是:"+DateTime.Now;} void EnterBtn_Click(Object src,EventArgs e) {Label1.Text="如今的时间是:"+DateTime.Now;} </script> <body> <form action="e1.aspx" runat=server> <asp:Label id="Label1" font-size="14" font-bold="true" forecolor="red" Text=""runat=server> </asp:Label> <br> <asp:button text="查看时间" Onclick="EnterBtn_Click" runat=server/> </form> </body> </html> 网页文件第一条语句表示网页中使用C#语言。<html>表示网页文件的开始,</html>表示网页文件的的结束,网页的全部内容都应在这两个标记之间。定义在标记<html>和</html>之间的内容被分为三部分,第一部分:<head>和</head>之间能够设定网页的一些信息,<title>和</title>之间的文字显示在IE浏览器的标题栏中。第二部分:<script language="c#" runat=server>和</script>标记之间能够定义方法,变量或对象,language="c#"表示在此标记之间定义的方法使用C#语言,runat=server表示在此标记之间定义的方法运行在Web服务器端,这里定义了两个方法,方法Page_Load()是Web服务器装载本网页时调用的方法,能够在此方法中作一些初始化工做,方法EnterBtn_Click()是"查看时间"按钮的事件函数。第三部分:<body>和</body>之间是网页在浏览器中显示的内容。<form action="e1.aspx" runat=server>和</form>标记定义Web窗体(WebForm),注意runat=server表示Web窗体由Web服务器解释。在Web窗体中增长了两个控件对象,第一个是Label控件,asp:Label表示本控件是Label控件,id至关Windows应用程序中控件的Name属性,用来区分不一样对象,runat=server表示次控件由Web服务器解释,其他是设定属性值,注意不一样属性用空格分隔。第二个控件是按钮,请注意定义单击事件函数的方法。 将其以文件名e1.aspx存入d:/asp文件夹,若是d:/asp文件夹已被设定为Web站点,能够在IE的地址栏输入:http://localhost/c411.aspx后,看到这个网页。在浏览器端看不到这些代码,用IE菜单查看/源代码,能够看到用超文本编制的网页。 9.3.5 用Visual Studio.NET实现ASP.NET动态网页 用Visual Studio.NET实现上节的例子。具体步骤以下: (1) 运Visaul C#后,则进入开始界面,选择新建项目。打开新建项目对话框,在项目类型中选择Visual C#项目,在模板中选择[ASP.NET Web应用程序],指定项目项目放置的位置为http://localhost/e1,这里http://localhost表明当前激活的宿主目录,即将本应用的全部文件存入宿主目录下的文件夹e1中,点击“肯定”,生成一个空白窗体(WebForm)。用户可在窗体中放入控件。Visual Studio.NET为咱们创建了一个应用项目。 (2) 向项目中添加控件须要使用[Toolbox]窗口,若看不到,能够用菜单视图/工具箱打开这个窗口。 (3) 先选中[Toolbox]窗口中[Web窗体]类型下的[Label]条目,而后在设计的窗体中按下鼠标左键并拖动鼠标,画出一个Label控件。该控件用来显示一行文本。 (4) 使用[Properties]窗口修改Label控件的文本内容和文本字体属性。在右下侧属性窗口中找到[text]属性,把它的值由“Label1”改成”如今的时间是:”;接着在属性窗口中找到[Font]属性,选中Font所在的单元格,单击Font属性左边的“+”号,在出现的子属性中编辑,能够修改Label控件中文本的字体和字号等属性。编辑完成后,单击变成“-”号的方框隐藏Font的子属性;修改Label控件的ForeColor属性,能够修改Label中文本的颜色。 (5) 从[Toolbox]窗口中选中一个Button控件到窗体,在[Properties]窗口中将按钮的[Text]属性分别改成”查看时间”。 (6) 为单击查看时间按钮事件(Click)函数增长语句(双击Click事件): private void Button1_Click(object sender, System.EventArgs e) { Label1.Text="如今的时间是:"+DateTime.Now; } (7) 为Page_Load事件函数增长语句: private void Page_Load(object sender, System.EventArgs e) { Label1.Text="如今的时间是:"+DateTime.Now; } (8) 单击工具栏中蓝色箭头按钮,运行,看一下效果。也可用浏览器看一下,地址为:http://localhost/e1/WebForm1.aspx。请仔细观察,每一步骤Visual Studio.NET都为咱们增长了什么语句。 9.3.6 Code Behind技术 Code Behind技术把界面设计代码和程序设计代码以不一样的文件分开,对于代码的重复使用,程序的调试和维护都是十分方便的。特别是在团队开发中,能够使不一样人员编辑不一样文件,极大地提升了效率。Visual Studio.NET使用了Code Behind技术,当咱们使用Visual Studio.Net建立了一个Web应用程序,将自动建立了两个文件,其中ASP.NET Web网页文件WebForm1.aspx以下: <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="e2.WebForm1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title> WebForm1</title> <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name="vs_defaultClientScript" content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </head> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> </form> </body> </html> 语句<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="e2.WebForm1" %>中的Codebehind="WebForm1.aspx.cs"表示网页的全部代码在文件WebForm1.aspx.cs中,使设计网页的外观和设计代码分离,可同时进行设计,互不影响,也是网页和代码的逻辑关系清楚,增长了易读性。代码文件WebForm1.aspx.cs以下: using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace e2 { /// <summary> /// WebForm1 的摘要说明。 /// </summary> public class WebForm1 : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { // 在此处放置用户代码以初始化页面 } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { // // CODEGEN:该调用是 ASP.NET Web 窗体设计器所必需的。 // InitializeComponent(); base.OnInit(e); } /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } } 9.3.7 ASP.NET和HTML兼容 任何一个静态网页只要把其扩展名修改成aspx,在ASP.NET下仍可运行,运行效果和之前相同。见下例,它是一个普通的静态网页。 <html> <head> <title>学生选课系统</title> </head> <body> <center> <h1>学生选课系统</h1> <form method="POST" action="c411.htm"> <p>姓名:<input type="text" name="T1" size="20"></p> <p>学号:<input type="password" name="T2" size="20"></p> <p>课程:<select size="1" name="D1"> <option selected>微积分</option> <option>代数与几何</option> <option>模拟电路</option> <option>机械原理</option> </select> <input type="submit" value="提交" name="B1"></p> </form> </center> </body> </html> 将其以文件名c411.aspx存入d:/asp文件夹,若是d:/asp文件夹已被设定为Web站点,能够在IE的地址栏输入:http://localhost/c411.aspx后,看到这个网页。 ASP.NET的设计目标之一就是尽量地保持和现有ASP页面的语法及运行库的兼容。但愿将现有ASP页面文件的扩展名改成.aspx,这些页面仍能够在ASP.NET中运行。在大多数状况下该目标已经实现了,但通常要对某些基本代码做出修改,由于ASP.NET已再也不支持VBScript了,并且VB语言自己也发生了变化。 9.3.8 网页中使用C#语句 在网页中,能够插入一些C#语句,具体用法见下例: <% @ Page Language="C#"%> <html> <body> <center> <% for (int i=0;i<8;i++){ %> <font size="<%=i%>">这是个人第一个ASP.NET网页</font><br> <%}%> </center> </body> </html> 在浏览器端看不到这些代码,用IE菜单查看/源代码,能够看到用超文本编制的网页。这样使用C#语句,不是一个好的习惯,不建议使用。 第十章 Web服务器端控件 本章介绍经常使用的Web服务器端控件的属性、事件和方法,以及用Web服务器端控件编制服务器端动态网页的方法。 10.1 经常使用的ASP.NET服务器端控件 10.1.1 Label控件 Label控件用以下方法定义: <asp:Label id="label1" font-size="14" font-bold="true" forecolor="red" Text="标签控件" runat=server></asp:Label>或者 <asp:Label id="Label1" font-size="14" font-bold="true" forecolor="red" runat=server> 标签控件</asp:Label> 下边介绍其经常使用的属性:  属性Text:显示的文本  属性ForeColor 文本的颜色,颜色能够取:红色=System.Drawing.Color.Red。黑色= System.Drawing.Color.Black等等。  字体的属性:黑体为Font.Bold=true,斜体为Font.Italic=true等等。  属性BackColor:背景色  属性id:至关Windows应用程序中控件的Name属性,用来区分不一样对象。  属性sp:Label:表示本控件是Label控件。  属性runat=server表示次控件运行在服务器段,由Web服务器解释。 10.1.2 TextBox控件 Label控件用以下方法定义: <asp:TextBox id="textBox1" runat=server></asp:TextBox> 经常使用的属性以下:  属性:Text 显示的文本  属性:TextMode=SingleLine 为编辑框,TextMode=MultiLine 为多行编辑框,能够有滚动条。TextMode=PassWord 为口令编辑框。  属性:MaxLength 编辑框和口令编辑框时,容许输入的最多字符数。  属性:Rows 多行编辑框时表示行数  事件TextChanged:控件中文本发生变化。 10.1.3 Button、LinkButton和ImageButton控件 Button控件已介绍过了,Text为按钮的标题,单击事件为:Click。 LinkButton控件:为超级连接形式的按钮,Text为按钮超级连接形式的标题,单击事件为Click。使用方法同Button控件,可为其增长单击事件Click的事件函数。 ImageButton控件:有些按钮须要在按钮上增长图案,例如工具条中的按钮,能够使用ImageButton控件。属性ImageUrl为图案的路径,通常最好和网页文件放在同一个目录下,此时,控件定义以下: <asp:ImageButton id="ImageButton1" runat="server" ImageUrl="t1.bmp"></asp:ImageButton 使用方法同Button控件,可为其增长单击事件Click的事件函数。 10.1.4 CheckBox和CheckBoxList控件 CheckBoxList控件能够建立一组若干CheckBox按钮,这些按钮有相同的性质。这些CheckBox按钮能够多选,不选或都选,可用来表示一些可共存的特性,例如一我的的爱好。下面例子在窗口中加一Label控件用来显示某人的爱好,增长两个CheckBox按钮,一个表明是否爱好音乐,一个表明是否爱好文学,每当用鼠标单击CheckBox按钮选择或不选择爱好,Label控件显示实际的爱好。具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的Label控件到窗体,其属性[Text]=“你的爱好是:”。 (3) 放工具箱的CheckBoxList控件到窗体。 (4) 单击属性Items后的按钮,出现集合编辑器对话框。单击添加按钮,增长一个CheckBox按钮,修改其Text属性为”音乐”,用一样方法增长另外一个CheckBox按钮,修改其Text属性为”文学”。注意爱好是能够多选的,所以必须用CheckBox控件。 (5) 设定属性AutoPosBack=true。 (6) 为CheckBox1按钮事件(SelectedIndexChanged)函数增长语句以下: private void CheckBoxList1_SelectedIndexChanged(object sender, System.EventArgs e) { String s="你的爱好是:"; for(int i=0;i<2;i++) { if(CheckBoxList1.Items[i].Selected) s=s+CheckBoxList1.Items[i].Text; } Label1.Text=s; } (7) 编译,运行,选中音乐显示:你的爱好是:音乐,再选中文学显示:你的爱好是:音乐文学,…。 10.1.5 RadioButton和RadioButtonList控件 有一类特性是互斥的,例如性别男女,选择这类特性可用RadioButtonList控件,该控件的最大特色是它有多个按钮,但只能选其中的一个按钮,下面是一个例子,两个单选按钮分别为男和女,用Label控件显示选择的的结果。具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的Label控件到窗体,其属性[Text]=“男”。 (3) 放工具箱的RadioButtonList控件到窗体。 (4) 单击属性Items后的按钮,出现集合编辑器对话框。单击添加按钮,增长一个RadioButton按钮,修改其Text属性为”男”, 修改其Selected属性为true。用一样方法增长另外一个RadioButton按钮,修改其Text属性为”女”,修改其Selected属性为法false。 (5) 设定属性AutoPosBack=true。 (6) 为SelectedIndexChanged事件增长事件函数以下: private void RadioButtonList1_SelectedIndexChanged(object sender, System.EventArgs e) { if(RadioButtonList1.SelectedIndex==0) Label1.Text="男"; else Label1.Text="女"; } (7) 编译,运行,单击RadioButtonList中的两个RadioButton按钮,显示所做的选择。请想想和CheckBox按钮的区别。 10.1.6 Image控件 Image控件用来显示图像,其属性AlternateText为字符串类型,若是图形不被正确显示,则显示此字符串。属性ImageAlign为图形对齐方式。ImageUrl为图形的URL地址。下例,增长3个单选按钮,根据单选按钮那个被选中,显示不一样的图像。具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的RadioButtonList控件到窗体。 (3) 单击属性Items后的按钮,出现集合编辑器对话框。单击添加按钮,增长一个RadioButton按钮,修改Text属性为”图1”,修改Value属性为”p1.jpg”,修改其Selected属性为true。用一样方法增长另外一个RadioButton按钮,修改其Text属性为”图2”, 修改Value属性为”p2.jpg”,修改其Selected属性为法false。 (4) 设定属性AutoPosBack=true。 (5) 为RadioButtonList控件的SelectedIndexChanged事件增长事件函数以下: private void RadioButtonList1_SelectedIndexChanged(object sender, System.EventArgs e) { Image1.ImageUrl=RadioButtonList1.SelectedItem.Value; } (6) 为Page_Load事件函数增长语句: private void Page_Load(object sender, System.EventArgs e) { // 在此处放置用户代码以初始化页面 Image1.ImageUrl=RadioButtonList1.Items[0].Value; } (7) 编译,连接,运行,单击RadioButtonList中的两个RadioButton按钮,显示不一样的图像。 10.1.7 HyperLink控件 HyperLink控件是超级连接控件,用来从一个网页定向到另外一个网页。属性Text为设置超级连接的文字。也能够使用图形完成超级连接,ImageUrl为图形的URL。属性NavigateUrl是定向到另外一个网页的URL。属性Target=_blank,打开一个新窗口,不然在原窗口打开。实现超级连接的例子的具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的HyperLink控件到窗体。 (3) 修改属性Text=” 超级连接”。 (4) 单击属性NavigateUrl后的按钮,出现选择URL对话框,URL类型选择为与根相关的,URL编辑框添入/bookExample/c412.aspx。 (5) 编译,连接,运行,单击HyperLink控件,在原窗口代开另外一个网页。如属性Target=_blank,打开一个新窗口显示新网页。 10.1.8 Table、TableCell和TableRow控件 这是一个表格控件,建立表格的具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的Table控件到窗体。 (3) 单击属性Row后的按钮,出现选择TableRow集合编辑器对话框,单击添加按钮,增长两行。 (4) 选择第0个TableRow,单击属性Cell后的按钮,出现选择TableCell集合编辑器对话框,单击添加按钮,增长三列。修改每列的属性Text,分别为:课程总论、刚体静力学、弹性静力学。 (5) 选择第1个TableRow,单击属性Cell后的按钮,出现选择TableCell集合编辑器对话框,单击添加按钮,增长三列。修改每列的属性Text,分别为:雅舍、孩子、音乐。 (6) 能够看到两行三列的表,运行看一下。 10.1.9 DrowDownList控件 这是一个下拉列表控件,建立下拉列表的具体步骤以下: (7) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (8) 放工具箱的DrowDownList控件到窗体。 (9) 单击属性Items后的按钮,出现选择ListItem集合编辑器对话框,单击添加按钮,增长三项。修改每项的属性Text,分别为:课程总论、刚体静力学、弹性静力学。 (10) 放工具箱的Label控件到窗体,id=Label1。 (11) 放工具箱的Button控件到窗体,为单击肯定按钮事件(Click)函数增长语句(双击Click事件): private void Button1_Click(object sender, System.EventArgs e) { Label1.Text=DropDownList1.SelectedItem.Text; } (12) 编译,连接,运行,从下拉列表选择刚体静力学,单击Button控件,Label1标签控件显示刚体静力学。 10.2 ASP.Net控件数据绑定 所谓数据绑定技术就是把数据集的某个或者某些数据绑定到控件的某些属性上面的一种技术。说的具体些,就是把数据集中某个或者某些数据绑定到Text控件、ListBox控件、ComBox等控件上的可以显示数据的属性上面。当对控件完成数据绑定后,其显示的内容将随着数据集的变化而变化。 10.2.1 数据绑定基础 ASP.NET引入了新的数据绑定语法。这种很是灵活的语法容许开发人员不只能够绑定到数据源,并且能够绑定到简单属性、集合、表达式甚至是从方法调用返回的结果。 DataBind是页和全部服务器控件的方法。当须要更新被绑定的数据时,必须调用此方法。当在父控件上调用DataBind时,该控件的全部子控件也同时调用本身的DataBind方法。例如,当调用DataList1.DataBind()后,将对DataList模板中的每一控件调用DataBind方法。在调用页的DataBind方法,既Page.DataBind(),会致使调用页上的全部控件的DataBind方法,更新页上全部绑定数据。一般从Page_Load事件调用DataBind,以下例所示。 protected void Page_Load(Object Src, EventArgs E) { DataBind();} 下面的示例说明如何将一个服务器控件的属性绑定到另外一个服务器控件的属性。 <html> <head> <script language="C#" runat="server"> void SubmitBtn_Click(Object sender, EventArgs e) { Page.DataBind();//更新页内的全部被绑定的数据 } </script> </head> <body> <h3><font face="宋体">一个服务器控件的属性绑定到另外一个服务器控件的属性</font></h3> <form runat=server> <asp:DropDownList id="StateList" runat="server"> <asp:ListItem>苹果</asp:ListItem> <asp:ListItem>香蕉</asp:ListItem> <asp:ListItem>桔子</asp:ListItem> </asp:DropDownList> <asp:button Text="提交" OnClick="SubmitBtn_Click" runat=server/> <p> 选定的水果:<asp:label text='<%# StateList.SelectedItem.Text %>' runat=server/> </form> </body> </html> 网页中语句text='<%# StateList.SelectedItem.Text %>'是将Label控件的Text属性绑定到DropDownList控件的属性StateList.SelectedItem.Text。符号%#表示数据绑定。函数SubmitBtn_Click中的语句Page.DataBind()更新页内的全部被绑定的数据。若是使用Visual Studio.Net实现,具体步骤以下: (6) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (7) 放工具箱的DrowDownList控件到窗体。单击属性Items后的按钮,出现选择ListItem集合编辑器对话框,单击添加按钮,增长三项。修改每项的属性Text,分别为:课程总论、刚体静力学、弹性静力学。 (8) 放工具箱的Button控件到窗体,为单击肯定按钮事件(Click)函数增长语句(双击Click事件): private void Button1_Click(object sender, System.EventArgs e) { Page.DataBind(); } (9) 放Label控件到窗体,id为Label1。单击属性DataBinding后标题为…的按钮,打开Label1数据绑定对话框,选择自定义绑定表达式(c),在其下编辑框中输入:DropDownList1.SelectedItem.Text。单击肯定按钮。 (10) 运行。 10.2.2 基于变量的数据绑定 ASP.NET 数据绑定语法支持绑定到公共变量、页的属性和页上其余控件的属性。下面的示例说明如何绑定到公共变量和页上的简单属性。注意这些值在DataBind()调用前初始化。 <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e) { Page.DataBind(); } string custID{//属性 get { return "ALFKI"; } } int orderCount{ get { return 11; } } </script> </head> <body> <h3><font face="宋体">到页属性的数据绑定</font></h3> <form runat=server> 客户:<b><%# custID %></b><br> 未结的订单:<b><%# orderCount %></b> </form> </body> </html> 用Visual Studio.Net实现的方法见上例及书。 10.2.3 基于集合的绑定 像DataGrid、ListBox、DrowDownList和HTMLSelect这样的列表服务器控件的列表均可以绑定到数据源。例如绑定到公共语言运行库的集合类型,如ArrayList、DataView、Hashtable和DataReader等。下面的示例说明如何将DrowDownList的列表绑定到ArrayList。 <html> <head> <script language="C#" runat="server"> void Page_Load(Object Sender, EventArgs E) { if (!Page.IsPostBack) { ArrayList values = new ArrayList(); values.Add ("IN"); values.Add ("KS"); values.Add ("MD"); values.Add ("MI"); values.Add ("OR"); values.Add ("TN"); DropDown1.DataSource =values; DropDown1.DataBind(); } } void SubmitBtn_Click(Object sender, EventArgs e) { Label1.Text = "您选择了:" + DropDown1.SelectedItem.Text; } </script> </head> <body> <h3><font face="宋体">数据绑定 DropDownList</font></h3> <form runat=server> <asp:DropDownList id="DropDown1" runat="server" /> <asp:button Text="提交" OnClick="SubmitBtn_Click" runat=server/> <p> <asp:Label id=Label1 font-name="宋体" font-size="10.5pt" runat="server" /> </form> </body> </html> 下面的示例说明如何把数据表绑定到DataGrid。注意使用DataView类要引用命名空间System.Data,即语句<%@ Import namespace="System.Data" %>。具体例子以下: <%@ Import namespace="System.Data" %> <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e ) { if (!Page.IsPostBack) { DataTable dt = new DataTable();//创建一个数据表 DataRow dr;//创建一个数据表的记录变量 dt.Columns.Add(new DataColumn("整数值", typeof(Int32)));//增长字段 dt.Columns.Add(new DataColumn("字符串值", typeof(string)));//包括字段名 dt.Columns.Add(new DataColumn("日期时间值", typeof(DateTime)));//及字段类型 dt.Columns.Add(new DataColumn("布尔值", typeof(bool))); for (int i = 1; i <= 9; i++) {//增长10个记录 dr = dt.NewRow();//创建1个记录对象 dr[0] = i;//为记录的每一个字段赋值 dr[1] = "项 " + i.ToString(); dr[2] = DateTime.Now; dr[3] = (i % 2 != 0) ? true : false; dt.Rows.Add(dr);//把此记录加到数据表中 } dataGrid1.DataSource = new DataView(dt);//为dataGrid指定数据源 dataGrid1.DataBind();//数据更新 } } </script> </head> <body> <h3><font face="宋体">到 DataView 的数据绑定</font></h3> <form runat=server> <asp:DataGrid id="dataGrid1" runat="server" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="3" CellSpacing="0" HeaderStyle-BackColor="#aaaadd" /> </form> </body> </html> 下面的示例说明如何绑定到Hashtable。 <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e) { if (!Page.IsPostBack) { Hashtable h = new Hashtable();//注意哈希表的使用 h.Add ("键 1", "值 1");//哈希表的每个元素是一对键和值 h.Add ("键 2", "值 2"); h.Add ("键 3", "值 3"); MyDataList.DataSource=h;//为列表框指定数据源 MyDataList.DataBind();//数据更新 } } </script> </head> <body> <h3><font face="宋体">到哈希表的数据绑定</font></h3> <form runat=server> <asp:DataList id="MyDataList" runat="server" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="4" CellSpacing="0" > <ItemTemplate> <%# ((DictionaryEntry)Container.DataItem).Key %> : <%# ((DictionaryEntry)Container.DataItem).Value %> </ItemTemplate> </asp:DataList> </form> </body> </html> 标记<ItemTemplate>指定显示格式</ItemTemplate>是按指定显示格式重复显示数据源中的全部数据。本例中应显示3组数据,指定显示格式为:键1:值。 下面介绍如何将ListBox、DrowDownList和HTMLSelect这样的列表服务器控件的列表绑定到数据表的某一字段上。 <%@ Import namespace="System.Data" %> <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e ) { if (!Page.IsPostBack) { DataTable dt = new DataTable();//创建一个数据表 DataRow dr;//创建一个数据表的记录变量 dt.Columns.Add(new DataColumn("Itemid", typeof(Int32)));//增长字段 dt.Columns.Add(new DataColumn("ItemName", typeof(string)));//包括字段名 dt.Columns.Add(new DataColumn("ItemDate", typeof(DateTime)));//及字段类型 dt.Columns.Add(new DataColumn("ItemBool", typeof(bool))); for (int i = 1; i <= 9; i++) {//增长10个记录 dr = dt.NewRow();//创建1个记录对象 dr[0] = i;//为记录的每一个字段赋值 dr[1] = "项 " + i.ToString(); dr[2] = DateTime.Now; dr[3] = (i % 2 != 0) ? true : false; dt.Rows.Add(dr);//把此记录加到数据表中 } StateList.DataSource = new DataView(dt);//为DropDownList指定数据源 StateList.DataTextField="ItemName"; StateList.DataValueField="Itemid"; StateList.DataBind();//数据更新 } } void SubmitBtn_Click(Object sender, EventArgs e) { Label1.Text="StateList的Text="+StateList.SelectedItem.Text+":StateList的Value="+StateList.SelectedItem.Value; } </script> </head> <body> <h3><font face="宋体">到StateList的数据绑定</font></h3> <form runat=server> <asp:DropDownList id="StateList" runat="server"/> <asp:button Text="提交" OnClick="SubmitBtn_Click" runat=server/> <br> <asp:Label id="Label1" runat="server"/> </form> </body> </html> 10.2.4 基于表达式绑定 一般须要在绑定到页或控件以前操做数据。下面的示例说明如何绑定到表达式和方法的返回值。 <html> <head> <script language="C#" runat="server"> void Page_Load(Object Src, EventArgs E) { if (!Page.IsPostBack) { ArrayList values = new ArrayList(); values.Add (0); values.Add (1); values.Add (2); values.Add (3); values.Add (4); values.Add (5); values.Add (6); DataList1.DataSource = values; DataList1.DataBind(); } } String EvenOrOdd(int number) { if ((number % 2) == 0) return "偶数"; else return "奇数"; } </script> </head> <body> <h3><font face="宋体">到方法和表达式的数据绑定</font></h3> <form runat=server> <asp:DataList id="DataList1" runat="server" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="3" CellSpacing="0" > <ItemTemplate> 数字值:<%# Container.DataItem %><%--Container表示数据源--%> 偶/奇:<%# EvenOrOdd((int) Container.DataItem) %><%--绑定到函数返回值--%> </ItemTemplate> </asp:datalist> </form> </body> </html> 10.2.5 基于DataBinder.Eval方法的数据绑定 为将绑定的数据按指定数据类型转化为字符串,能够使用String.Format方法。请看下面的示例,该例要将数据表中字段名为"IntegerValue"的数据转换为货币的数据类型的字符串输出。 <%# String.Format("{0:c}",((DataRowView)Container.DataItem)["IntegerValue"])%> 该语法可能比较复杂,难以记忆。ASP.NET提供了一种静态方法DataBinder.Eval,能够将绑定的数据按指定数据类型转化为字符串。该方法使用很方便,由于它消除了开发人员为强迫将数值转换为所需的数据类型而必须作的许多显式转换。这在数据绑定模板列表内的控件时尤为有用,由于一般数据字段的类型都必须转换。为将整数显示为货币字符串,使用#DataBinder.Eval格式以下: <%#DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:c}") %> DataBinder.Eval是一个具备三个参数的方法,第一个参数是数据源的当前记录,在象DataList、DataGrid或Repeater这样的模板列表中,该参数始终是Container.DataItem,第二个参数是数据表字段名,表示要将此字段的数据转换为第三个参数指定的数据类型的字符串,第三个参数为格式字符串,{0:c}表示货币类型。格式字符串参数是可选的。若是省略它,则 DataBinder.Eval将此字段的数据转换为字段自己的数据类型的字符串,以下例所示,输出为字符串"true"或"false"。 <%# (bool)DataBinder.Eval(Container.DataItem, "BoolValue") %> 具体的实例以下: <%@ Import namespace="System.Data" %> <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e) { if (!Page.IsPostBack) { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("IntegerValue", typeof(Int32))); dt.Columns.Add(new DataColumn("StringValue", typeof(string))); dt.Columns.Add(new DataColumn("DateTimeValue", typeof(DateTime))); dt.Columns.Add(new DataColumn("BoolValue", typeof(bool))); for (int i = 0; i < 9; i++) { dr = dt.NewRow(); dr[0] = i; dr[1] = "项 " + i.ToString(); dr[2] = DateTime.Now; dr[3] = (i % 2 != 0) ? true : false; dt.Rows.Add(dr); } dataList1.DataSource = new DataView(dt); dataList1.DataBind(); } } </script> </head> <body> <h3><font face="宋体">使用 DataBinder.Eval 进行数据绑定</font></h3> <form runat=server> <asp:DataList id="dataList1" runat="server" RepeatColumns="3" Width="80%" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="4" CellSpacing="0" > <ItemTemplate> 订购日期:<%# DataBinder.Eval(Container.DataItem, "DateTimeValue", "{0:d}") %> <p> 数量:<%# DataBinder.Eval(Container.DataItem, "IntegerValue", "{0:N2}") %> <p> 项:<%# DataBinder.Eval(Container.DataItem, "StringValue") %> 订购日期: <asp:CheckBox id=chk1 Checked='<%# (bool)DataBinder.Eval(Container.DataItem, "BoolValue") %>' runat=server/> <p> </ItemTemplate> </asp:Datalist> </form> </body> </html> 控件DataList中的ItemTemplate是模板控件,其功能是将控件DataList的数据源中的全部数据,按ItemTemplate模板控件所指定的格式显示。 10.2.6 列表绑定控件 列表绑定控件通用属性 1. DataSource 属性 DataList、DataGraid和Repeater都提供了DataSource属性。使用DataSource属性指定要绑定到上述三种数据列表控件的数据源,用数据源中的数据填充上述三种数据列表控件。数据源必须是实现System.Collections.ICollection接口的具备相同对象的集合,例如System.Data.DataView(见6.3节最后例子)、System.Collections.ArrayList(见6.3节例1)和System.Collections.Hashtable(见6.3节哈希表例子)。 2. Items 集合 Items是一个集合属性,包含一些具备相同特征的若干对象的集合。DataList、DataGraid和Repeater控件中包含多个Items集合属性,使用Items集合用来以编程的方式控制DataList、DataGraid和Repeater控件中的各项。例如: a) AlternatingItem:DataGraid中全部奇数编号行的项的集合 b) SelectedItem:当前选中的全部项的集合。 c) EditItem:正在编辑的行中全部项的集合。例子见7.2.4 更新数据的语句MyDataGrid.EditItemIndex = (int)E.Item.ItemIndex;。 以上各项都和数据源有关,如下各项和数据源无关。 d) Header:全部列表表头项的集合 e) Footer: 全部列表表尾项的集合 f) Separtor:DataGraid和Repeater控件中分隔符线的集合 g) Page:DataGraid在分页显示中,每页数据项的集合。 3.数据绑定和Items集合的建立 当为DataList、DataGraid和Repeater等列表控件的属性DataSource指定数据源,并执行数据绑定函数DataBind方法后,列表控件将建立Items集合,并从数据源取得显示所需的数据,能够经过Items属性来得到列表控件中各项的内容。注意,只有绑定到数据源的项才包含在Items集合中。页眉、页脚和分隔符不包含在该集合中。下面的示例展现如何使用 Items集合来显示DataList控件中的项。 <%@ Import Namespace="System.Data" %> <html> <script language = "C#" runat="server"> ICollection CreateDataSource() { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("StringValue", typeof(string))); for (int i = 0; i < 10; i++) { dr = dt.NewRow(); dr[0] = "Item " + i.ToString(); dt.Rows.Add(dr); } DataView dv = new DataView(dt); return dv; } void Page_Load(Object sender, EventArgs e) { if (!IsPostBack) { DataList1.DataSource = CreateDataSource();//指定数据源 DataList1.DataBind();//数据绑定 } } void Button_Click(Object sender, EventArgs e) { if (DataList1.Items.Count>0)//Items项数>0 { Label1.Text = "The Items collection contains: <br>"; foreach(DataListItem item in DataList1.Items) { Label1.Text += ((DataBoundLiteralControl)item.Controls[0]).Text + "<br>"; } } } </script> <body> <form runat=server> <h3>DataList Items Example</h3> <asp:DataList id="DataList1" runat="server" BorderColor="black" CellPadding="3" Font-Name="Verdana" Font-Size="8pt"> <HeaderStyle BackColor="#aaaadd"> </HeaderStyle> <AlternatingItemStyle BackColor="Gainsboro"> </AlternatingItemStyle> <HeaderTemplate> Items </HeaderTemplate> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, "StringValue") %> </ItemTemplate> </asp:DataList> <br><br> <asp:Button id="Button1" Text="显示DataList1表第一列的全部内容" OnClick="Button_Click" runat="server"/> <br><br> <asp:Label id="Label1" runat="server"/> </form> </body> </html> 若是使用控件DataGrid,有时须要知道指定行的指定列的数据,表达式DataGrid1.Items[2].Cells[1].Text表示网格控件DataGrid1的第2行第1列的文本。具体例子以下: <%@ Import namespace="System.Data" %> <html> <head> <script language="C#" runat="server"> void Page_Load(Object sender, EventArgs e ) { if (!Page.IsPostBack) { DataTable dt = new DataTable();//创建一个数据表 DataRow dr;//创建一个数据表的记录变量 dt.Columns.Add(new DataColumn("整数值", typeof(Int32)));//增长字段 dt.Columns.Add(new DataColumn("字符串值", typeof(string)));//包括字段名 dt.Columns.Add(new DataColumn("日期时间值", typeof(DateTime)));//及字段类型 dt.Columns.Add(new DataColumn("布尔值", typeof(bool))); for (int i = 1; i <= 9; i++) {//增长10个记录 dr = dt.NewRow();//创建1个记录对象 dr[0] = i;//为记录的每一个字段赋值 dr[1] = "项 " + i.ToString(); dr[2] = DateTime.Now; dr[3] = (i % 2 != 0) ? true : false; dt.Rows.Add(dr);//把此记录加到数据表中 } dataGrid1.DataSource = new DataView(dt);//为dataGrid指定数据源 dataGrid1.DataBind();//数据更新 } } void Button_Click(Object sender, EventArgs e) { Label1.Text=dataGrid1.Items[2].Cells[1].Text; } </script> </head> <body> <form runat=server> <asp:DataGrid id="dataGrid1" runat="server" BorderColor="black" BorderWidth="1" GridLines="Both" CellPadding="3" CellSpacing="0" HeaderStyle-BackColor="#aaaadd" /> <br><br> <asp:Button id="Button1" Text="显示DataGrid1表第二行第一列的内容" OnClick="Button_Click" runat="server"/> <br><br> <asp:Label id="Label1" runat="server"/> </form> </body> </html> 3. Style 使用DataList和DataGraid的属性Style可定义控件的外观。见下例: <asp:DataGrid id="grid" runat="server" visible="false" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> </asp:DataGrid> 其中定义了控件DataGraid的背景为白色,边界为黑实线,宽度为1个象素,字体为"x-small",字体名字为"verdana"。奇数行的背景颜色,标题的字的颜色,背景色。数据项的背景色等等。显示效果见7.1.2的例子。 4. ItemTemplate 属性 控件DataList中的ItemTemplate是模板控件,其功能是将控件DataList的数据源中的全部数据,按ItemTemplate模板控件所指定的格式显示。DataList 控件中项的外观由ItemStyle属性控制。还能够使用 AlternatingItemTemplate 属性来控制 DataList 控件中交替项的内容。具体例子见6.6节中3.数据绑定和Items集合的建立的例子。 DataGraid控件没有ItemTemplate模板,可以使用模板列控件TemplateColumn,在模板列控件中增长ItemTemplate模板本身定义该列的显示控件或显示格式,具体例子见7.1.6列类型的第3和第4个例子。还能够在其中增长EditItemTemplate模板本身定义该列在编辑时使用的控件,具体例子见7.2.5节例子。 Repeater控件的ItemTemplate模板见8.4节例子。 5. 模板中的数据绑定 模板中的数据绑定的例子见上一节中的例子。 6.6.2 使用列表绑定控件 <%@ Import Namespace="System.Data" %> <html> <script language = "C#" runat="server"> ICollection CreateDataSource() { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("StringValue", typeof(string))); for (int i = 0; i < 10; i++) { dr = dt.NewRow(); dr[0] = "Item " + i.ToString(); dt.Rows.Add(dr); } DataView dv = new DataView(dt); return dv; } void Page_Load(Object sender, EventArgs e) { if (!IsPostBack) { DataList1.DataSource = CreateDataSource(); DataList1.DataBind(); } } void Button1_Click(Object sender, EventArgs e) { if (DropDown1.SelectedIndex == 0) DataList1.RepeatDirection = RepeatDirection.Horizontal; else DataList1.RepeatDirection = RepeatDirection.Vertical; if (DropDown2.SelectedIndex == 0) DataList1.RepeatLayout = RepeatLayout.Table; else DataList1.RepeatLayout = RepeatLayout.Flow; DataList1.RepeatColumns=DropDown3.SelectedIndex+1; if ((Check1.Checked ==true) && (DataList1.RepeatLayout == RepeatLayout.Table)) { DataList1.BorderWidth = Unit.Pixel(1); DataList1.GridLines = GridLines.Both; } else { DataList1.BorderWidth = Unit.Pixel(0); DataList1.GridLines = GridLines.None; } } </script> <body> <form runat=server> <h3>DataList Example</h3> <asp:DataList id="DataList1" runat="server" BorderColor="black" CellPadding="3" Font-Name="Verdana" Font-Size="8pt"> <HeaderStyle BackColor="#aaaadd"> </HeaderStyle> <AlternatingItemStyle BackColor="Gainsboro"> </AlternatingItemStyle> <HeaderTemplate> Items </HeaderTemplate> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, "StringValue") %> </ItemTemplate> <AlternatingItemTemplate> * <%# DataBinder.Eval(Container.DataItem, "StringValue") %> </AlternatingItemTemplate> </asp:DataList> <p> <hr noshade align="left" width="300px"> RepeatDirection: <asp:DropDownList id=DropDown1 runat="server"> <asp:ListItem>Horizontal</asp:ListItem> <asp:ListItem>Vertical</asp:ListItem> </asp:DropDownList><br> RepeatLayout: <asp:DropDownList id=DropDown2 runat="server"> <asp:ListItem>Table</asp:ListItem> <asp:ListItem>Flow</asp:ListItem> </asp:DropDownList><br> RepeatColumns: <asp:DropDownList id=DropDown3 runat="server"> <asp:ListItem>1</asp:ListItem> <asp:ListItem>2</asp:ListItem> <asp:ListItem>3</asp:ListItem> <asp:ListItem>4</asp:ListItem> <asp:ListItem>5</asp:ListItem> </asp:DropDownList><br> Show Borders: <asp:CheckBox id=Check1 runat="server" /><p> <asp:LinkButton id=Button1 Text="Refresh DataList" OnClick="Button1_Click" runat="server"/> </form> </body> </html> 若是使用Visual Studio.Net实现模板,具体步骤以下: 本节小结 1. ASP.NET 声明性数据绑定语法使用 <%# %> 表示法。 2. 能够绑定到数据源、页或其余控件的属性、集合、表达式以及从方法调用返回的结果。 3. 列表控件能够绑定到支持 ICollection、IEnumerable 或 IListSource 接口的集合,如 ArrayList、Hashtable、DataView 和 DataReader。 4. DataBinder.Eval 是用于晚期绑定的静态方法。它的语法可能比标准数据绑定语法简单,但性能较低。 10.3 数据验证控件 用户输入了数据,在提交前,首先要对输入的数据进行验证。固然,能够本身编程序进行验证。ASP.NET提供了一些验证控件,能够不用编程完成对输入的数据进行验证。本节介绍如何使用这些数据验证控件。 10.3.1 数据验证概述 对用户输入的数据进行验证,能够在客户端进行。实现原理是当用户输入了信息并单击提交按钮后,用在客户端运行的JavaScript脚本或VBScript脚本对数据验证,只有全部数据正确,才能发送到服务器端处理。此种方法的优势是运行在客户端,所以反应速度快,减轻了服务器和网络的负载。缺点是因为JavaScript脚本或VBScript脚本是以明文的方式嵌入在HTML文档中,客户端能够看到这些脚本程序,若是用户把这段脚本删除,网页也就失去了验证功能,所以这种方法是不安全的。 另外一种数据验证方法是在服务器端进行,当用户输入了信息并单击提交按钮后,把数据马上发送到服务器端,在服务器端验证,若是验证不经过,返回错误信息。这种方法虽然在响应速度比较慢,增长了服务器的负担,但可靠性上要强的不少。 ASP.NET提供了一些验证控件,能够不用编程完成对输入的数据进行验证。下边是一个使用验证控件简单的例子,该例以数据验证控件RequiredFieldValidator为例,介绍数据验证控件属性的使用方法。有些数据用户是必须输入的,这些数据能够用编辑控件,单选或多选按钮等控件输入。能够用控件RequiredFieldValidator对这些控件输入的数据进行验证,检查用户是否输入了数据。控件RequiredFieldValidator的属性ControlToValidate的值选择要验证的控件的id值,能够是编辑控件,单选或多选按钮等。属性ErrorMessage是发生错误时,提示的错误信息。用户用编辑控件textBox1输入姓名,要求必须输入。用控件RequiredFieldValidator1对其输入进行验证,所以属性ControlToValidate= textBox1。属性ErrorMessage=”必须输入姓名”。当单击提交按钮后,若是用户没有输入姓名,则用”必须输入姓名”提示用户。 <html> <body> <form id="Form1" method="post" runat="server"> <p>姓名:<asp:TextBox id=" TextBox1" runat="server"/></p> <p><asp:Button Text="提交" runat=server/></p> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage="RequiredFieldValidator" ControlToValidate="RadioButtonList1"> </asp:RequiredFieldValidator> </form> </body> </html> 10.3.2 经常使用的验证控件 .Net框架类库中提供如下几种验证控件:  RequiredFieldValidator控件  自定义数据验证控件CustomValidator控件  ValidationSummary控件  CompareValidator控件  RegularExpressionValidator 控件 10.3.3 验证控件经常使用的属性  属性ControlToValidate:要验证的控件的id值。  属性ErrorMessage:发生错误时,提示的错误信息。  属性Display:  属性IsValid:  属性Text: 10.3.4 RequiredFieldValidator 上边已介绍用记事本编辑网页如何使用此控件,下边的例子用Visual Studio.Net 编辑。该例子增长一个RadioList控件,输入卡的类型,增长一个编辑控件,输入编号,二者都要求必须输入,用两个RequiredFieldValidator控件验证。步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的Label控件到窗体,其属性[Text]=“RequiredFieldValidator控件的使用”。Id=Label1。 (3) 放工具箱的Label控件到窗体,其属性[Text]=“输入卡号”。 (4) 放工具箱的RadioButtonList控件到窗体,id=RadioButtonList1。 (5) 单击属性Items后的按钮,出现集合编辑器对话框。单击添加按钮,增长一个RadioButton按钮,修改其Text属性为”苹果卡”,修改其Selected属性为法false。用一样方法增长另外一个RadioButton按钮,修改其Text属性为”橡胶卡”,修改其Selected属性为法false。 (6) 放工具箱的Label控件到窗体,其属性[Text]=“输入编号”。 (7) 放工具箱的TextBox控件到窗体,id=TextBox1。 (8) 放工具箱的RequiredFieldValidator控件到窗体, 属性ControlToValidate= RadioButtonList1, 属性ErrorMessage=”必须输入卡类型”。 (9) 放工具箱的RequiredFieldValidator控件到窗体, 属性ControlToValidate=TextBox1, 属性ErrorMessage=”必须输入编号”。 (10) 放工具箱的Button控件到窗体,为其增长单击事件函数以下: private void Button1_Click(object sender, System.EventArgs e) { if(Page.IsValid==true) Label1.Text="已输入了数据"; else Label1.Text="至少有一项未输入了数据"; } (11) 如运行出错,把C:\inetpub\wwwroot\aspnet_client文件夹拷贝到D:\Asp文件夹下。运行 10.3.5 自定义数据验证控件CustomValidator控件 CustomValidator控件容许编程者本身定义一个函数对数据进行验证。通常数据验证分为客户端验证和服务器端验证,能够修改验证控件的属性ClientTarget改变在那端验证,例如:Page.ClientTarget=ClientTarget.Downlevel语句表示要在服务器端验证,而语句Page.ClientTarget=ClientTarget.Uplevel表示在客户端验证,在客户端验证必须在发布目录下包含C:\inetpub\wwwroot\aspnet_client文件夹。所以,编程者要根据在那一端验证,编写不一样的函数,在服务器端验证函数定义以下: void ServerValidate(object source, ServerValidateEventArgs args){//验证语句} 在客户端验证函数定义以下:? void ClientValidate(source,value){//验证语句} 书中的例子以下: <%@ Page Language="C#" %> <html> <head> <script runat=server> void ValidateBtn_OnClick(object sender, EventArgs e) { if (Page.IsValid) { lblOutput.Text = "Page is valid."; } else { lblOutput.Text = "Page is not valid!"; } } void ServerValidate(object source, ServerValidateEventArgs args) { try { int i = int.Parse(args.Value); args.IsValid = ((i%2) == 0); } catch { args.IsValid = false; } } </script> </head> <body> <form runat="server"> <h3>CustomValidator Example</h3> <asp:Label id=lblOutput runat="server" Text="Enter an even number:" Font-Name="Verdana" Font-Size="10pt" /><br> <p> <asp:TextBox id="Text1" runat="server" />    <asp:CustomValidator id="CustomValidator1" ControlToValidate="Text1" ErrorMessage="Not an even number!" OnServerValidate="ServerValidate" runat="server"/> <p> <asp:Button id="Button1" Text="Validate" OnClick="ValidateBtn_OnClick" runat="server"/> </form> </body> </html> </html> 用Visual Studio.NET实现此例,具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放工具箱的Label控件到窗体,其属性[Text]=“CustomValidator 控件的使用”。Id=Label1。 (3) 放工具箱的Label控件到窗体,其属性[Text]=“键入一个偶数”。 (4) 放工具箱的TextBox控件到窗体,id=TextBox1。 (5) 放工具箱的CustomValidator控件到窗体,id=CustomValidator1,属性ControlToValidate=TextBox1, 属性ErrorMessage=Not an even number!。 (6) 放工具箱的Button控件到窗体,为其增长单击事件函数以下: void ValidateBtn_OnClick(Object sender,EventArgs e) { If (Page.IsValid) lblOutput.Text = "Page is Valid!"; else lblOutput.Text = "Page is InValid!"; } (7) 为CustomValidator控件ServerValidate事件增长事件函数以下 private void CustomValidator1_ServerValidate(object source, System.Web.UI.WebControls.ServerValidateEventArgs args) { try { int i = int.Parse(args.Value); args.IsValid = ((i%2) == 0); } catch { args.IsValid = false; } } (8) 10.3.6 ValidationSummary控件 当用户提交了数据后,全部验证控件对数据进行验证,若是没有错误,设置Page.IsValid=true,不然=false。若是在页面中放置了控件ValidationSummary,它将自动显示发现错误的数据验证控件的属性ErrorMessage的内容。能够在5.2.1的例子中,增长一个ValidationSummary控件,运行后看一下效果。 10.3.7 CompareValidator控件 CompareValidator控件能够对两个控件输入的值进行比较。属性ControlToValidate=控制对象的id,属性ValueToCompare=要比较得值,属性Type=比较的数据类型,属性Operator=如何比较,能够是:Equal、NotEqual、GreatrThan、GreatrThanEqual、LessThan、LessThanEqual。用Visual Studio.NET实现此例,具体步骤以下: (1) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。 (2) 放三个Label控件到窗体,其属性[Text]分别为”First string”、”Second string”和”lblOutput”。id分别为Label一、Label2和lblOutput。 (3) 在Label1和Label2控件后,分别放置TextBox控件,id分别为TextBox1和TextBox2。 (4) 放工具箱的CompareValidator控件到窗体,属性ControlToValidate=TextBox1,属性ControlToCompare=TextBox2,属性ErrorMessage=Not an even number!,id=Compare1。 (5) 放两个ListBox控件到窗体,id分别为ListOperator和ListType。 (6) 单击ListOperator属性Items旁的三个小点,在ListItem编辑器对话框中单击添加按钮,增长6个选项,属性Text分别为:Equal、NotEqual、GreatrThan、GreatrThanEqual、LessThan、LessThanEqual,同时,属性Value也变为相应的值。 (7) 单击ListType属性Items旁的三个小点,在ListItem编辑器对话框中单击添加按钮,增长5个选项,属性Text分别为:String、Integer、Double、Date、Currency,同时,属性Value也变为相应的值。 (8) 设置两个ListBox控件属性SelectionMode为Single,不容许多选。(为何?) (9) 设置两个ListBox控件属性AutoPostBack=true。 (10) 为ListOperator事件(SelectedIndexChenged)增长事件函数以下: private void ListOperator_SelectedIndexChanged(object sender, System.EventArgs e) { Compare1.Operator=(ValidationCompareOperator)ListOperator.SelectedIndex; Compare1.Validate(); } (11) 为ListType事件(SelectedIndexChenged)增长事件函数以下: private void ListType_SelectedIndexChanged(object sender, System.EventArgs e) { Compare1.Type=(ValidationDataType)ListType.SelectedIndex; Compare1.Validate(); } (12) 放工具箱的Button控件到窗体,为其增长单击事件函数以下: private void Button1_Click(object sender, System.EventArgs e) { //Label1.Text=DropDownList1.SelectedItem.Text; if(Page.IsValid==true) lblOutput.Text="数据有效"; else lblOutput.Text="数据无效"; } (13) 运行,看一下效果。增长列表内容用以下语句 listBox1.Items.Add("Item9");//在列表最后增长一项 listBox1.SetSelected(1, true);//第一项被选中 listBox1.EndUpdate();//更新 5.2.5 RangeValidator 控件 RangeValidator 控件测试输入控件的值是否在指定范围内。RangeValidator 控件使用四个关键属性执行验证。ControlToValidate 属性包含要验证的输入控件。MinimumValue 和 MaximumValue 属性指定有效范围的最大值和最小值。BaseCompareValidator.Type 属性用于指定要比较的值的数据类型。在执行验证操做以前,要比较的值被转换为此数据类型。能够进行比较的不一样数据类型: 字符串数据类型String、32 位有符号整数数据类型Integer、双精度浮点数数据类型Double、日期数据类型Date、一种能够包含货币符号的十进制数据类型Currency。 下面的示例说明如何在 Web 页上建立 RangeValidator 控件,以检查输入到输入控件的值是否在比较范围内。 <%@ Page Language="C#" %> <html> <head> <script runat="server"> void ButtonClick(Object sender, EventArgs e) { if (Page.IsValid) { Label1.Text="Page is valid."; } else { Label1.Text="Page is not valid!!"; } } </script> </head> <body> <form runat="server"> <h3>RangeValidator Example</h3> Enter a number from 1 to 10: <br> <asp:TextBox id="TextBox1" runat="server"/> <br> <asp:RangeValidator id="Range1" ControlToValidate="TextBox1" MinimumValue="1" MaximumValue="10" Type="Integer" EnableClientScript="false" Text="The value must be from 1 to 10!" runat="server"/> <br><br> <asp:Label id="Label1" runat="server"/> <br><br> <asp:Button id="Button1" Text="Submit" OnClick="ButtonClick" runat="server"/> </form> </body> </html> 10.3.8 RegularExpressionValidator 控件 RegularExpressionValidator 控件也叫正则表达式控件,该控件用来检查输入控件的值是否匹配正则表达式定义的模式。这类验证容许您检查可预知的字符序列,好比身份证号码、电子邮件地址、电话号码和邮编中的字符序列。本节首先讲解一些正则表达式的基本知识,而后将这些基本知识用于数据验证控件。 1.基本模式 模式,是正规表达式最基本的元素,它们是一组描述字符串特征的字符。模式能够很简单,由普通的字符串组成;也能够很是复杂,能够用特殊的字符表示一个范围内的宇符、重复出现等。最简单的匹配就是一个字符串,这种状况下,若是一个字符串含有这个字符串,那么那个字符串就认为是符合匹配要求的。  “^”头匹配 这个模式包含一个特殊的字符^,表示该模式只匹配那些以紧接其后的以字符串。例如:^front,表示以”front”开头的字符串是匹配的,而不以”front”开头的字狩串是不匹配的。  “$”尾匹配 尾匹配“$”的意义是,只有那些以“$”号前面的字符串结尾的字符串才符合匹配的要求,例如:tail$,表示那些以”tail”结尾的字符串是匹配的。结合使用“^”和“S”,能够提供一种整个字符串匹配的模式:^whole$,就表示仅有“whole”字符串符合匹配要求。 2.转义序列 所谓转义序列其实就是一些没法直接在正则表达式中使用的字符,例如标点符号、空格、 回车、换行、制表符等。全部的转义序列都用反斜杠(\)打头。和“C”语言中相似,转义序列也是以“\”开头的,如: 换行:\n 回车:\r 制表符:\t 因为在正则表达式中“^”、“S”和“+”等都有特殊的意义,因此在正则表达式中也须要使用转义序列来表示“\^”“\$”和“\+”。 3.字符簇 在Internet中,正规表达式一般用来舰用户的输人,当用户提交一个Form之后,要判断输入的电话号码、地址、Enail地址、信用卡号码、邮政编码等是否有效,用普通的基于字面的字符是不够的,须要一种能够设定一个字符集合的方法,在正则表达式中,这种方法称为字符簇,一个字符簇是使用方挂号括起来的。下面举一个例子:[ABCabc],上面的字符簇例子表示若是一个字符是“A”或“B”或“C“或“a”或“b”或“C“,那么就符合匹配要求。 当须要一个有顺序的返回的时候,能够使用连字号来表示二个字符的范围,如:[a-z]匹配全部的小写字母。[A-Z]匹配全部的大写字母。[az-AZ]匹配全部字母。[0-9]匹配全部的数字。[0-9\.\-] 匹配全部的数字,句号和减号。[\f\r\t\n]匹配全部的白字符。 前面曾经提到^表示字符串的开头,但它还有另一个含义。当在一组方括号里使用^时,它表示“非”或“排除”的意思,经常用来剔除某个字符。例如,若是要求第一个字符不能是小写字母:^[^a-z], 这个模式与“A4”、“7b”及“+a”是匹配的,但与”a2’、“c6”是不匹配的。下面是几个排除特定字符字符的例子:[“a-z”],除了小写字母之外的全部字符。[^“0-9”],除了数字以外的全部字符。[^\”\’],除了双引号(”)和单引号(’)以外的全部字符。特殊字符”.”(点号)在正规表达式中用来表示除了“新行”以外的全部字符。因此模式”^.5$”与任何两个字符的、以数字5结尾和以其余非“新行”字符开头的字符串匹配。模式”.”能够匹配任何字符串,除了空串和只包括一个“新行”的字符串。 4.重复 到如今为止,已经讨论了如何去匹配一个字母或数字,但更多的状况下,可能要匹配一个单词或一组数字。一个单词由若干个字母组成,一组数字由若干个单数组成。 正则表达式提供了“{}”来执行重复,跟在字符或字符簇后面的花括号({})用来肯定前面的内容重复出现的次数。其中{n,m}表示可能重复n到m次而且包括n和m次,{n,}表示可能重复n次或大于n次。例如:^a{4}$表示aaaa。^a(2,4)$表示aa,aaa,或aaaa。^a{2,}$表示包含多于两个a的字符串。.{2}表示全部的两个字符。 下面是经常使用的一些模式: ^[a-zA-Z0-9_]{1,}$表示全部包含一个以上的字母、数字或下划线的字符串。 ^[0-9]{1,}$ 表示全部的整数。 ^\-{0,1}[0-9]{0,}\.{0,1}[0-9]{0,}$ 表示全部小数。 正则表达式提供了一些简写的特殊字符,可让表达式容易理解: ? {0,1} * {0,} + {1,} 例如:^[0-9]+$ 表示全部的整数。^[0-9]+$ 表示全部的整数。^\-?[0-9]*\.?[0-9]+$表示全部小数。 下例说明如何使用 RegularExpressionValidator 验证一个 5 位数的邮编。 <%@ Page Language="C#" %> <html> <head> <script runat="server"> void ValidateBtn_Click(Object sender, EventArgs e) { if (Page.IsValid) { lblOutput.Text = "Page is Valid!"; } else { lblOutput.Text = "Page is InValid! :"; } } </script> </head> <body> <h3>RegularExpressionValidator Example</h3> <p> <form runat="server"> <table bgcolor="#eeeeee" cellpadding="10"> <tr valign="top"> <td colspan="3"> <asp:Label ID="lblOutput" Text="Enter a 5 digit zip code" runat="server"/> </td> </tr> <tr> <td colspan="3"> <b>Personal Information</b> </td> </tr> <tr> <td align="right"> Zip Code: </td> <td> <asp:TextBox id="TextBox1" runat="server"/> </td> <td> <asp:RegularExpressionValidator id="RegularExpressionValidator1" ControlToValidate="TextBox1" ValidationExpression="\d{5}" Display="Static" ErrorMessage="Zip code must be 5 numeric digits" EnableClientScript="False" runat="server"/> </td> </tr> <tr> <td></td> <td> <asp:Button text="Validate" OnClick="ValidateBtn_Click" runat=server /> </td> <td></td> </tr> </table> </form> </body> </html> 如用Visual Studio.NET实现,只需修改属性ValidationExpressio便可。 10.4 DataGraid控件 10.4.1 DataGrid控件概述 10.4.2 DataGrid控件绑定数据库表 例子e10_4_1联接一个数据库,用DataGrid控件显示的例子以下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>DataGrid显示数据库表</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; string txtCommand="SELECT employeeid, firstname, lastname FROM Employees"; SqlConnection conn = new SqlConnection(txtConn); SqlDataAdapter da = new SqlDataAdapter(txtCommand, conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); grid.DataSource =ds.Tables["MyTable"];//为网格控件指定数据源 grid.DataBind();//数据绑定 // grid.Visible =true; } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <form runat=server> <asp:DataGrid id="grid" runat="server" visible="false" AutoGenerateColumns="true" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> </asp:DataGrid> </form> </body> </html> 其中字符串txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"叫联接字符串,DATABASE时数据源,SERVER是数据库服务器,UID是用户名,PWD为密码。语句SqlConnection conn=new SqlConnection(txtConn)用来创建一个链接。字符串txtCommand ="SELECT employeeid, firstname, lastname FROM Employees"是一个SQL语句,用来从表Employees中取出字段employeeid、firstname、lastname的全部记录。语句SqlDataAdapter da=new SqlDataAdapter(txtCommand,conn)用来创建一个SqlDataAdapter对象,它负责读取数据库数据。用DataGrid控件显示表Employees中字段employeeid、firstname、lastname的数据。Page_Load()函数说明了显示数据的必要步骤。特别要注意DataGrid控件数据绑定的方法。 10.4.3 DataGrid控件对数据库记录分页显示 <%@ Page Language="C#" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.Data" %> <html> <title>分页</title> <script runat="server"> public void Page_Load(Object sender, EventArgs e) { DataGrid1.DataSource = CreateDataSource(); DataGrid1.DataBind(); } private DataTable CreateDataSource() { String strConn = "DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; String strCmd ="SELECT firstname, lastname FROM employees"; SqlDataAdapter cmd = new SqlDataAdapter(strCmd, strConn); DataSet oDS = new DataSet(); cmd.Fill(oDS, "EmployeesList"); return oDS.Tables["EmployeesList"]; } public void PageIndexChanged(Object sender, DataGridPageChangedEventArgs e) { DataGrid1.CurrentPageIndex = e.NewPageIndex; DataGrid1.DataSource = CreateDataSource(); DataGrid1.DataBind(); } </script> <body bgcolor="ivory" style="font-family:arial;font-size:xsmall"> <!-- ASP.NET topbar --> <h2>分页</h2> <form runat="server"> <asp:datagrid runat="server" id="DataGrid1" Font-Size="Smaller" Font-Names="Verdana" CellPadding="2" CellSpacing="2" CssClass="Shadow" BackColor="White" BorderStyle="solid" BorderColor="black" BorderWidth="1" AllowPaging="True" PageSize="3" OnPageIndexChanged="PageIndexChanged"> <PagerStyle PageButtonCount="3" Font-Bold="true" Mode="NumericPages" BackColor="palegreen" /> <AlternatingItemStyle BackColor="Gainsboro" /> <ItemStyle BackColor="White" /> <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="Navy" /> </asp:datagrid> </form> </body></html> AllowPaging="True"表示容许分页,PageSize="3"表示每页3个记录,<PagerStyle PageButtonCount="3" Font-Bold="true" Mode="NumericPages" BackColor="palegreen" />中PageButtonCount="3"表示有3个按钮,Mode="NumericPages"表示按钮形式为数字,也能够修改成Mode="NextPrev",则在网格下部出现<和>,单击<,转向前页,单击>,转向后页。也能够改成字符,例如修改成“前页”和“后页”,能够修改属性PrevPageText=”前页”,NextPageText=”后页”。OnPageIndexChanged="PageIndexChanged"表示单击按钮事件函数是PageIndexChanged。 10.4.4 DataGrid控件对记录排序 <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>按列排序</title> <script runat="server"> SqlConnection conn; protected void Page_Load(Object Src, EventArgs E) { conn = new SqlConnection("server=localhost;uid=sa;pwd=;database=pubs"); if (!IsPostBack) BindGrid("au_id"); } protected void MyDataGrid_Sort(Object sender, DataGridSortCommandEventArgs e) { BindGrid(e.SortExpression); } public void BindGrid(String sortfield) { SqlDataAdapter myCommand = new SqlDataAdapter("select * from Authors",conn); DataSet ds = new DataSet(); myCommand.Fill(ds, "Authors"); DataView Source = ds.Tables["Authors"].DefaultView; Source.Sort = sortfield; MyDataGrid.DataSource=Source; MyDataGrid.DataBind(); } </script> <body> <h3><font face="Verdana">按列排序</font></h3> <form runat="server"> <ASP:DataGrid id="MyDataGrid" runat="server" OnSortCommand="MyDataGrid_Sort" Width="700" BackColor="#ccccff" BorderColor="black" ShowFooter="false" CellPadding=3 CellSpacing="0" Font-Name="Verdana" Font-Size="8pt" HeaderStyle-BackColor="#aaaadd" AllowSorting="true" /> </form> </body> </html> AllowSorting="true"表示容许排序,容许排序的列标题有一下划线,单击标题将产生事件,事件函数由OnSortCommand="MyDataGrid_Sort"定义。现若是用手工产生每一列,排序的例子以下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>按列排序</title> <script runat="server"> SqlConnection conn; protected void Page_Load(Object Src, EventArgs E) { conn = new SqlConnection("DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"); if (!IsPostBack) BindGrid("lastname"); } protected void MyDataGrid_Sort(Object sender, DataGridSortCommandEventArgs e) { BindGrid(e.SortExpression); } public void BindGrid(String sortfield) { SqlDataAdapter myCommand = new SqlDataAdapter("SELECT employeeid, firstname, lastname, title, country FROM Employees", conn); DataSet ds = new DataSet(); myCommand.Fill(ds, "MyList"); DataView Source = ds.Tables["MyList"].DefaultView; Source.Sort = sortfield; MyDataGrid.DataSource=Source; MyDataGrid.DataBind(); } </script> <body> <h3><font face="Verdana">按列排序</font></h3> <form runat="server"> <asp:DataGrid id="MyDataGrid" runat="server" AutoGenerateColumns="false" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana" AllowSorting="true" OnSortCommand="MyDataGrid_Sort"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> <columns> <asp:BoundColumn runat="server" DataField="employeeid" HeaderText="编号" > <itemstyle backcolor="lightblue" font-bold="true" /> </asp:BoundColumn> <asp:BoundColumn runat="server" DataField="firstname" HeaderText="名字" /> <asp:BoundColumn runat="server" DataField="lastname" HeaderText="姓" SortExpression="lastname" /> <asp:BoundColumn runat="server" DataField="title" HeaderText="Position" /> <asp:BoundColumn runat="server" DataField="country" HeaderText="国家" SortExpression="country" /> </columns> </asp:DataGrid> </form> </body> </html> 在DataGrid的5列中,只有DataField="lastname"和DataField="country"有SortExpression属性定义,分别为:"lastname"和"country",所以,只有此两列能够排序,表达式SortExpression= "lastname"表示此列能够排序,按字段"lastname"排序。若是增长语句Source.Sort += " DESC",则降序排序,如无字符" DESC"为生序排序。 10.4.5 用BoundColumn列将标题改成中文 DataGrid控件的属性AutoGenerateColumns=true,将根据数据源的内容自动填充表格,标题默认为是字段名,因为避免不兼容,字段名通常用英文,如但愿如将标题改成中文,能够置AutoGenerateColumns=false,使用列控件BoundColumn手工填充,其中属性HeaderText是标题字符串,能够改为中文,属性DataField是该列显示的字段名。见下例: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>DataGrid标题改成中文</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; string txtCommand="SELECT employeeid, titleofcourtesy, firstname, lastname, title, country FROM Employees"; SqlConnection conn = new SqlConnection(txtConn); SqlDataAdapter da = new SqlDataAdapter(txtCommand, conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); grid.DataSource = ds.Tables["MyTable"]; // grid.DataBind(); } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <form runat=server> <asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> <columns> <asp:BoundColumn runat="server" DataField="employeeid" HeaderText="编号"> <itemstyle backcolor="lightblue" font-bold="true" /> </asp:BoundColumn> <asp:BoundColumn runat="server" DataField="titleofcourtesy" HeaderText="称呼" /> <asp:BoundColumn runat="server" DataField="firstname" HeaderText="姓" /> <asp:BoundColumn runat="server" DataField="lastname" HeaderText="名" /> </columns> </asp:DataGrid> </form> </body> </html> 10.4.6 增长按钮列 若是用手工建立DataGrid表格,除了以上介绍的列控件BoundColumn外,还包括控件ButtonColumn,用来建立一列按钮,能够为按钮增长一个事件函数,控件HyperLinkColumn用来建立一列超级连接字符,控件EditCommandColumn,将自动和编辑命令相关联。控件TemplateColumn,将按指定模板建立所显示的列。 例子e10_4_4是控件ButtonColumn的用法: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>DataGrid增长按钮列</title> <script runat=server> string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; public void Page_Load(Object sender, EventArgs e) { string txtCommand="SELECT employeeid, firstname, lastname FROM Employees"; SqlConnection conn = new SqlConnection(txtConn); SqlDataAdapter da = new SqlDataAdapter(txtCommand, conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); } public void HandleCommands(Object sender, DataGridCommandEventArgs e) { if(e.CommandName=="moreinfo") { int nEmpID=(int)grid.DataKeys[e.Item.ItemIndex]; SqlConnection conn=new SqlConnection(txtConn); String strCmd="SELECT * FROM Employees "+ "WHERE employeeid="+nEmpID.ToString(); SqlCommand cmd=new SqlCommand(strCmd,conn); conn.Open(); SqlDataReader dr=cmd.ExecuteReader(); dr.Read(); MoreInfo.Text=BuildMoreInfoText(dr); dr.Close(); conn.Close(); } } private String BuildMoreInfoText(SqlDataReader dr) { StringBuilder sb=new StringBuilder(); sb.Append("<b>"); sb.Append(dr["lastname"]+"," + dr["firstname"]); sb.Append("</b><br>"); sb.Append(dr["title"] + "<hr>"); sb.Append("<i>"); sb.Append(dr["notes"]); sb.Append("</i>"); return sb.ToString(); } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <form runat=server> <asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false" CssClass="shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana" DataKeyField="employeeid" OnItemCommand="HandleCommands"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true"/> <columns> <asp:BoundColumn runat="server" DataField="employeeid" HeaderText="编号"/> <asp:BoundColumn runat="server" DataField="firstname" HeaderText="名" /> <asp:BoundColumn runat="server" DataField="lastname" HeaderText="姓" /> <asp:ButtonColumn runat="server" Text="更多信息" CommandName="moreinfo"> <itemstyle backcolor="lightblue" font-bold="true" /> </asp:ButtonColumn> </columns> </asp:DataGrid> <asp:Label runat="server" id="MoreInfo" /> </form> </body> </html> 网页中OnItemCommand="HandleCommands"的HandleCommands是按钮列按钮的事件函数,若是有多个按钮列,都用此函数响应。按钮列按钮的事件函数HandleCommands中的语句if(e.CommandName=="moreinfo")是判断是哪个按钮发的事件。 10.4.7 增长HyperLinkColumn列 例子e10_4_5下例是控件HyperLinkColumn的用法: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>DataGrid增长HyperLinkColumn列</title> <script runat=server> string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; public void Page_Load(Object sender, EventArgs e) { string txtCommand="SELECT employeeid, firstname, lastname FROM Employees"; SqlConnection conn = new SqlConnection(txtConn); SqlDataAdapter da = new SqlDataAdapter(txtCommand, conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <form runat=server> <asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false" CssClass="shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana" DataKeyField="employeeid"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> <columns> <asp:BoundColumn runat="server" DataField="employeeid" HeaderText="编号" /> <asp:BoundColumn runat="server" DataField="firstname" HeaderText="名" /> <asp:BoundColumn runat="server" DataField="lastname" HeaderText="姓" /> <asp:HyperLinkColumn runat="server" HeaderText="连接另外一网页" DataNavigateUrlField="employeeid" DataNavigateUrlFormatString="e10_4_5A.aspx?id={0}" DataTextField="lastname" DataTextFormatString="关于{0}更多信息" Target="frInfo"> <ItemStyle BackColor="lightblue" font-bold="true" /> </asp:HyperLinkColumn> </columns> </asp:DataGrid> <asp:Label runat="server" id="MoreInfo" /> </form> </body> </html> 控件HyperLinkColumn中,DataNavigateUrlFormatString="例子e10_4_5A.aspx? id ={0}"是超级连接的网页,本例是c716-1A.aspx,?id={0}是传递的参数,{0}是一个变量,对应DataNavigateUrlField指定的数据库表字段,本例为employeeid,DataTextFormatString为超级连接字符,本例为"关于{0}更多信息"。 例子e10_4_5A.aspx以下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>More Info</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { int nEmpID = Convert.ToInt16(Request["id"]); if (nEmpID<1) { Response.Write("No valid ID specified."); Response.End(); } else GetMoreInfo(nEmpID); } public void GetMoreInfo(int nEmpID) { String strConn = "DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; SqlConnection conn = new SqlConnection(strConn); String strCmd = "SELECT * FROM Employees " + "WHERE employeeid = " + nEmpID.ToString(); SqlCommand cmd = new SqlCommand(strCmd, conn); conn.Open(); SqlDataReader dr = cmd.ExecuteReader(); dr.Read(); MoreInfo.Text = BuildMoreInfoText(dr); dr.Close(); conn.Close(); } private String BuildMoreInfoText(SqlDataReader dr) { StringBuilder sb = new StringBuilder(); sb.Append("<b>"); sb.Append(dr["employeeid"] + " - "); sb.Append(dr["lastname"] + ", " + dr["firstname"]); sb.Append("</b><br>"); sb.Append(dr["title"] + "<hr>"); sb.Append("<i>"); sb.Append(dr["notes"]); sb.Append("</i>"); return sb.ToString(); } </script> <body> <asp:Label runat="server" id="MoreInfo" /> </body> </html> 10.4.8 增长EditCommandColumn列 10.4.9 控件TemplateColumn的用法 例子e10_4_5A是控件TemplateColumn的用法,本例用单选按钮显示bool字段。 <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>显示布尔变量</title> <script runat="server"> public void Page_Load(Object sender, EventArgs e) { if(!Page.IsPostBack) { SqlConnection conn=new SqlConnection("DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"); SqlDataAdapter da=new SqlDataAdapter("SELECT employeeid, titleofcourtesy, firstname, lastname, title, ISNULL(reportsto,0) AS boss FROM Employees", conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); } } bool HasBoss(int bossID) { if (bossID != 0) return true; return false; } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <form runat="server"> <asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="0" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> <columns> <asp:BoundColumn runat="server" HeaderText="ID" DataField="编号"> <itemstyle backcolor="lightblue" font-bold="true" /> </asp:BoundColumn> <asp:TemplateColumn runat="server" HeaderText="Employee Name"> <itemtemplate> <asp:label runat="server" style="margin-left:5;margin-right:5" Text='<%# DataBinder.Eval(Container.DataItem,"TitleOfCourtesy")+"<b>"+ DataBinder.Eval(Container.DataItem,"LastName")+"</b>"+","+ DataBinder.Eval(Container.DataItem,"FirstName") %>' /> </itemtemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText="Reports" headerstyle-horizontalalign="Center" itemstyle-horizontalalign="Center"> <itemtemplate> <asp:checkbox runat="server" enabled="false" checked= '<%# HasBoss((int)DataBinder.Eval(Container.DataItem,"boss")) %>'/> </itemtemplate> </asp:TemplateColumn> <asp:BoundColumn runat="server" DataField="title" HeaderText="Position" /> </columns> </asp:DataGrid> </form> </body> </html> 例子e10_4_5B用图形按钮显示bool字段。 <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <title>用图像显示布尔变量</title> <script runat="server"> public void Page_Load(Object sender, EventArgs e) { if (!Page.IsPostBack) { SqlConnection conn = new SqlConnection("DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"); SqlDataAdapter da = new SqlDataAdapter("SELECT employeeid, titleofcourtesy, firstname,lastname,title,ISNULL(reportsto,0)AS boss FROM Employees",conn); DataSet ds=new DataSet(); da.Fill(ds,"MyTable"); grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); } } String GetProperGifFile(int bossID) { if (bossID != 0) return "checked.gif";//checked.gif应和e10_4_5B.aspx在同一文件夹中 return "unchecked.gif"; } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="0" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> <columns> <asp:BoundColumn runat="server" HeaderText="ID" DataField="employeeid"> <itemstyle backcolor="lightblue" font-bold="true" /> </asp:BoundColumn> <asp:TemplateColumn runat="server" HeaderText="Employee Name"> <itemtemplate> <asp:label runat="server" style="margin-left:5;margin-right:5" Text='<%#DataBinder.Eval(Container.DataItem,"TitleOfCourtesy")+"<b>" +DataBinder.Eval(Container.DataItem,"LastName")+"</b>"+","+ DataBinder.Eval(Container.DataItem, "FirstName") %>' /> </itemtemplate> </asp:TemplateColumn> <asp:TemplateColumn HeaderText="Reports" headerstyle-horizontalalign="Center" itemstyle-horizontalalign="Center"> <itemtemplate> <asp:image runat="server" imageurl='<%# GetProperGifFile((int)DataBinder.Eval(Container.DataItem,"boss"))%>'/> </itemtemplate> </asp:TemplateColumn> <asp:BoundColumn runat="server" DataField="title" HeaderText="Position" /> </columns> </asp:DataGrid> </form> </body> </html> 7.1.9 使用本身的数据库  用Access创建数据库 用Access创建数据库: db1.mdb。创建Student表,记录全部学生信息。包括字段StudentNum(学生编号),字节类型,必填字段,默认值为空,StudentNum为主关键字。字段StudentName(学生姓名),文本,字段大小8,必填字段,默认值为空。字段StudentSex(性别),文本,字段大小2。增长若干数据。存数据库的路径为:D:\asp\StudentDb.mdb  例子以下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data.OleDb" %> <%@ Import Namespace="System.Data" %> <html> <title>DataGrid</title> <script runat=server> public void OnLoadData(Object sender, EventArgs e) { string txtConn="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\ASP\\bookExample\\db1.mdb"; string txtCommand="SELECT * FROM student"; OleDbConnection conn = new OleDbConnection(txtConn); OleDbDataAdapter da = new OleDbDataAdapter(txtCommand, conn); DataSet ds = new DataSet(); da.Fill(ds, "MyTable"); // Display the data grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); grid.Visible = true; } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <!-- ASP.NET topbar --> <h2>数据库的联接</h2> <form runat=server> <asp:linkbutton runat="server" id="btnLoad" text="Go get data..." οnclick="OnLoadData" /> <asp:DataGrid id="grid" runat="server" visible="false" AutoGenerateColumns="true" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> </asp:DataGrid> </form> </body> </html> 10.5 AdRotator控件 Web 页上的广告一般采用广告条(小图片)的形式,单击时使用户重定向到广告商的 Web 页。使用 AdRotator Web 服务器控件可以显示广告条并在一系列广告条间循环。AdRotator 自动进行循环处理,在每次刷新页面时更改显示的广告。使用 AdRotator 控件显示广告步骤: (1) 建立一个Web应用程序框架,项目名为UseAdRotator。 (2) 新建一个XML文件。单击菜单项”项目/添加新项”,弹出标题为添加新项的窗口,在窗口中选中XML文件,文件名为ads.xml,单击打开按钮,增长一个XML文件。文件以下: <Advertisements> <Ad> <ImageUrl>d:\\asp\\bookExample\\p2.JPG</ImageUrl> <NavigateUrl>http://www.sohu.com</NavigateUrl> <AlternateText>search anything</AlternateText> <Impressions>10</Impressions> <Keyword>Topic1</Keyword> <Caption>This is the caption for Ad#1</Caption> </Ad> <Ad> <ImageUrl>d:\\asp\\bookExample\\baobao048.jpg</ImageUrl> <NavigateUrl>http://www.sina.com</NavigateUrl> <AlternateText>sina main site</AlternateText> <Impressions>10</Impressions> <Keyword>Topic2</Keyword> <Caption>This is the caption for Ad#2</Caption> </Ad> </Advertisements> (3) 在窗体中放置控件AdRotator,其属性Name=AdRotator1。 (4) 修改控件AdRotator的属性AdvertisementFile,单击其后的按钮,出现选择XML文件对话框,在对话框中URL(U)处填入ads.xml。 (5) 运行,能够看到一幅图,鼠标移到图中,变为手形,单击能够转到搜狐网站。刷新,能够看到另外一幅图。 XML 文件包含如下预约义的属性。只有 ImageUrl 属性是必需的:  ImageURL 要显示的图像的 URL  NavigateURL 单击 AdRotator 控件时定位到的页面的 URL。  AlternateText 图像不可用时显示的文本(常为图片浏览工具提示)。  Keyword 可用于筛选特定广告的广告类别。经过设置AdRotator的KeywordFilter属性以筛选出 XML 文件中特定类别的广告。  Impression 指示广告的可能显示频率的数值。在 XML 文件中,全部 impression 值的总和不能超过 2,048,000,000 - 1 10.6 Calender控件 Calendar Web 服务器控件在 Web 窗体页上显示一个传统的单月份日历(包含该月在内的6周)。用户可以使用该日历查看和选择日期。Calendar Web 服务器控件最简单的用法以下:(C7-2A.aspx) <html> <head> <script language="C#" runat="server"> void Date_Selected(object s, EventArgs e) { Label1.Text = "Selected date is: " + Calendar1.SelectedDate.ToShortDateString(); } </script> </head> <body> <h3><font face="Verdana">Calendar Example</font></h3> <form runat=server> <asp:Calendar id=Calendar1 onselectionchanged="Date_Selected" runat="server" /> <p> <asp:Label id=Label1 runat="server" /> </form> </body> </html> 其中事件onselectionchanged="Date_Selected"是用户改变选择日期时产生的事件。SelectionMode属性设定Calendar控件中可选择的时间段,Day:可选择任一天;DayWeek:可选择任一天或一周;DayWeekMonth:可选择任一天、一周或一月;None:不能选择日期。C7-2A.aspx网页显示了SelectionMode属性选择不一样质的效果。 <html> <head> <script language="C#" runat="server"> void Page_Load(Object Sender, EventArgs e) { Calendar1.SelectionMode = (CalendarSelectionMode)lstSelMode.SelectedIndex; if (Calendar1.SelectionMode == CalendarSelectionMode.None) Calendar1.SelectedDates.Clear(); } void Date_Selected(object s, EventArgs e) { switch (Calendar1.SelectedDates.Count) { case (0): //None Label1.Text = "No dates are currently selected"; break; case (1): //Day Label1.Text = "The selected date is " + Calendar1.SelectedDate.ToShortDateString(); break; case (7): //Week Label1.Text = "The selection is a week beginning " + Calendar1.SelectedDate.ToShortDateString(); break; default: //Month Label1.Text = "The selection is a month beginning " + Calendar1.SelectedDate.ToShortDateString(); break; } } </script> </head> <body> <h3><font face="Verdana">Date Selection Modes</font></h3> <p> <form runat=server> Choose a Selection Mode: <asp:DropDownList id="lstSelMode" runat=server AutoPostBack=true> <asp:ListItem Value="None" >None</asp:ListItem> <asp:ListItem Selected Value="Day" >Day</asp:ListItem> <asp:ListItem Value="DayWeek" >DayWeek</asp:ListItem> <asp:ListItem Value="DayWeekMonth" >DayWeekMonth</asp:ListItem> </asp:DropDownList> <p> <asp:Calendar id=Calendar1 runat="server" onselectionchanged="Date_Selected" DayNameFormat="FirstLetter" Font-Name="Arial" Font-Size="12px" Height="180px" Width="200px" SelectorStyle-BackColor="gainsboro" TodayDayStyle-BackColor="gainsboro" DayHeaderStyle-BackColor="gainsboro" OtherMonthDayStyle-ForeColor="gray" TitleStyle-BackColor="gray" TitleStyle-Font-Bold="True" TitleStyle-Font-Size="12px" SelectedDayStyle-BackColor="Navy" SelectedDayStyle-Font-Bold="True" /> <p> <asp:Label id=Label1 runat="server" /> </form> </body> </html> 一、显示和选择日期 可视日期:该日期肯定日历中显示哪一个月份。在日历中,用户可在不一样的月份之间移动,从而在不影响当前日期的状况下更改可视日期。 选定的一个或多个日期:在该控件中用户可经过设置SelectionMode属性选择单个日、单个周或单个月份,但只能选择连续的日期。 可设置日历的属性以更改日历的颜色、尺寸、文本以及其余可视特性。默认状况下,该控件显示月中各天、周中各天的标头、带有月份名和年份的标题、用于选择月份中各天的连接及用于移动到下个月和上个月的连接。能够经过设置控制控件中不一样部分的样式的属性,来自定义 Calendar 控件的外观。 SelectionMode属性: 设定Calendar控件中可选择的时间段 Day:可选择任一天; DayWeek:可选择任一天或一周; DayWeekMonth:可选择任一天、一周或一月 None:不能选择日期 SelectDate属性实现选择日期:按所定义的外观样式显示运行时所选定的一天、一周或一月 控件的 DayRender 事件:当在 Calendar 控件中建立(显示)每一个日期单元格时,均会引起 DayRender 事件。经过在 DayRender 事件的事件处理程序中提供代码,能够在建立日期单元格时控制其内容和格式设置。事件处理程序接收一个 DayRenderEventArgs 类型的参数,它包含与此事件相关的数据。DayRenderEventArgs 属性: Cell:获取表示Calendar 控件单元格的 TableCell 对象。 Day:获取表示Calendar 控件中日期的 CalendarDay。 例 dayreader.aspx 为 DayRender 事件编写处理程序,使所显示月份中日期的背景色为黄色。它还说明如何经过向单元格添加 System.Web.UI.LiteralControl 来自定义单元格的内容。 10.7 Visual Studio.Net实现留言板 本例有两个窗口,主窗口负责输入留言,包括输入用户名,留言主题,留言内容,用三个编辑框,输入完毕后,单击提交按钮,将留言存入数据库。单击另外一个查看留言按钮,可连接到另外一个显示留言窗口。显示留言窗口包括一个DataGraid控件,用来显示全部的留言的用户名,主提,留言序号,及按钮列,单击相应按钮,显示当前记录的留言内容。单击返回主窗口按钮,返回主窗口。下边是具体步骤: (1) 用Access2000创建数据库: LiuYanBan.mdb。创建LiuYanTable表,记录全部留言信息。包括字段LiuYanID(留言编号),自动编号类型,为主关键字。字段LiuYanName(留言者姓名),文本,字段大小10,必填字段,默认值为空。字段LiuYanTitle(留言标题),文本,字段大小30,必填字段,默认值为空。字段LiuYanTime(留言时间),时间类型。字段LiuYanContent(留言内容),备注字段,必填字段,默认值为空。增长若干数据。存数据库的路径为:D:\asp\ LiuYanBan.mdb,假设文件夹asp已设为Web网站目录。 (2) 建立一个Web应用程序框架,选择菜单命令创建一个新空白窗体。项目名为LiuYanBan。 (3) 修改WebForm属性,单击属性Style后标题为…的按钮,打开样式生成器对话框,能够修改WebForm的各类风格。单击对话框左侧的各个选项:字体、背景、文本、位置、布局、边缘、列表、其余,能够按本身的爱好修改相应的内容,这里不做修改,所有采用默认值。 (4) 放工具箱的4个Label控件到窗体。修改属性Text分别为:留言板主窗体、用户名、留言主题、留言内容。 (5) 放工具箱的3个TexbBox控件到窗体。修改属性Text都为空,ID=TexbBox1编辑框用来输入用户名,ID=TexbBox2编辑框用来输入留言主题,ID=TexbBox3编辑框用来输入留言内容,其属性TextMode=MultiLine。因为此三项要求必须输入数据,所以应增长3个验证控件。 (6) 放工具箱的Button控件到窗体,Text=”提交留言”。 (7) 在窗体中放置控件oleDbConnection,其属性Name=oleDbConnection1。单击控件oleDbConnection属性ConnectionString的下拉列表的箭头,在列表中选择新建链接,打开数据链接属性对话框,选择提供程序页,选择OLE DB提供程序为Microsoft Jet 4.0 OLE DB Provider后,单击下一步按钮,选择数据库名称为D:\asp\ LiuYanBan.mdb,用户名称为Admin,空白密码,单击测试链接按钮,应出现测试链接成功对话框。按肯定按钮退出。 (8) 在窗体中放置控件oleDbCommand,其属性Name= oleDbCommand1。单击控件oleDbCommand属性Connection的下拉列表的箭头,在列表中单击现有前的+后,选择已有的链接oleDbConnection1。 (9) 为单击提交留言按钮事件(Click)函数增长语句(双击Click事件): private void Button1_Click(object sender, System.EventArgs e) { oleDbConnection1.Open(); //自动增长字段没必要写入 oleDbCommand1.CommandText="Insert Into LiuYanTable(LiuYanName,LiuYanTitle,LiuYanTime,LiuYanContent) Values('" + TextBox1.Text + "', '" + TextBox2.Text + "','" + DateTime.Now + "','" + TextBox3.Text + "')"; oleDbCommand1.ExecuteNonQuery(); oleDbConnection1.Close(); TextBox1.Text=""; TextBox2.Text=""; TextBox3.Text=""; } (10) 单击文件/添加新项(w)…菜单项,出现添加新项对话框,选择Web窗体,窗体名为:WebForm2.aspx,单击打开按钮,建立新窗体。 (11) 在WebForm1放工具箱的HyperLink控件到窗体,Text=”查看留言”,单击属性NavigateUrl后的按钮,出现选择URL对话框,选择URL类型为与根相关的,URL编辑框添入/LiuYanBan/WebForm2.aspx。 (12) 在WebForm2窗体中放置控件oleDbConnection,其属性Name=oleDbConnection1。单击控件oleDbConnection属性ConnectionString的下拉列表的箭头,在列表中选择前边创建的数据库链接。 (13) 在WebForm2窗体中放置控件oleDbDataAdapter,出现添加数据适配器向导对话框,单击下一步按钮,单击下拉列表的箭头,在列表中选择前边创建的数据库链接。单击下一步按钮。 (14) 选择使用SQL语句单选按钮。单击下一步按钮。 (15) 单击高级选项按钮,在高级SQL选项对话框中,全部多选按钮都不选。单击肯定按钮。 (16) 单击查询生成器按钮,在添加表对话框中,选中LiuYanBan数据库,单击添加按钮。再按关闭按钮,关闭添加表对话框。 (17) 选中全部字段,按LiuYanID降序排列,单击肯定按钮。 (18) 单击肯定按钮。单击完成按钮。 (19) 单击sqlDataAdapter1选中它,单击菜单项数据/生成数据集…,打开生成数据集对话框,他选择默认值。按肯定按钮退出。增长控件dataSet,其属性Name=dataSet1。 (20) 在WebForm2窗体中放置控件dataView,其属性Name=dataView1。单击控件dataView1属性Table的下拉列表的箭头,在列表中单击现有前的+后,选择dataSet1中的LiuYanTable。 (21) 在窗体中放置控件Label,其属性Name=Label1。 (22) 在窗体中放置控件DataGrid,其属性Name=DataGrid1。右击DataGrid1,在弹出菜单中选择菜单项自动套用格式,在对话框中选用本身喜欢的格式。 (23) 右击DataGrid1,在弹出菜单中选择菜单项属性生成器,在DataGrid属性对话框中,选中左侧的选项:常规。设置数据源为:dataView1。选中显示页眉,显示页脚,容许排序。选中左侧的选项:列。不选中在运行时自动建立列。将字段:LiuYanName、LiuYanTitle、LiuYanTime从左侧的列表框移到右侧的列表框,表示显示此三个字段。见页眉文本改为中文:留言者姓名、留言标题、留言时间。增长一个Select按钮,增长一个按钮列,页眉为:单击按钮查看留言。命令名为:ReadContent。选中左侧的选项:分页。选中容许分页。 (24) 为按钮列增长事件函数,DataGraid全部按钮都产生事件:ItemCommand,根据命令名加以区分是哪个按钮发的命令。事件函数以下: private void DataGrid1_ItemCommand(object source,System.Web.UI.WebControls. DataGridCommandEventArgs e) { if (e.CommandName == "ReadContent") { Label1.Text=dataSet11.Tables["LiuYanTable"].Rows[e.Item.ItemIndex]["LiuYanContent"].ToString(); } } (25) 为Page_Load事件函数增长语句: private void Page_Load(object sender, System.EventArgs e) { oleDbDataAdapter1.Fill(dataSet11); if(!Page.IsPostBack) { DataGrid1.CurrentPageIndex=0; DataGrid1.DataBind(); } // 在此处放置用户代码以初始化页面 } (26) 为DataGraid1的DataGrid1_PageIndexChanged事件函数增长语句: private void DataGrid1_PageIndexChanged(object source,System.Web.UI.WebControls.DataGridPageChangedEventArgs e) { DataGrid1.CurrentPageIndex=e.NewPageIndex; DataGrid1.DataBind(); } (27) 在WebForm2放工具箱的HyperLink控件到窗体,Text=”输入留言”,单击属性NavigateUrl后的按钮,出现选择URL对话框,选择URL类型为与根相关的,URL编辑框添入/LiuYanBan/WebForm1.aspx。 (28) 运行,出现WebForm1,能够输入一条留言,单击提交按钮,再单击超级连接查看留言,转到WebForm2,单击查看留言按钮,能够在Label1处看到留言,单击超级连接输入留言,转到WebForm1。 第十一章 ASP.NET内建对象 ASP.NET为保持浏览用户的数据和信息,内建了许多对象,包括Application、Response、Request、cookie、Sessions、Cache和Server等对象,以及它们的大量的方法。经过这些对象,能够提供网络开发必不可少的功能,例如当前目录的得到、在线人数、访问网站总人数、网上商店中的购物筐等等。 11.1 Request对象 Request对象主要有如下用途:第一用来来在不一样网页之间传递数据,第二是Web服务器能够使用Request对象获取用户所使用的浏览器的信息,第三是Web服务器能够使用Request对象显示Web服务器的一些信息,最后,能够用Request对象得到Cookie信息。本节主要介绍前三种用途,后边有一节专门介绍Cookie。 11.1.1 用Request对象获取另外一个网页传递的数据 从一个网页连接到另外一个网页时,可能须要传递一些数据到另外一个网页。两个Web网页之间通常经过表单(From)传递,具体传递方法有两个:Post和Get。当数据传递到另外一个网页时,另外一个网页用Request对象的方法取出这些数据。见下例:(e11_1A.aspx) <html> <body> <form action=e11_1B.aspx method=POST runat=server> <asp:Label id=label1" runat=server>用户名:</asp:Label> <asp:TextBox id="textBox1" Text="" runat=server></asp:TextBox> <asp:button text="提交" runat=server/> </form> </body> </html> 其中action是用户单击此按钮后,响应用户程序网页的URL,这里是e11_1B.aspx。语句method=POST是数据用POST方法传到e11_1B.aspx,也能够是get方法。在e11_1B.aspx网页中,是不能用string s= textBox1.Text语句获得输入的内容的,由于textBox1是另外一个网页的对象。必须用语句string s=Request.Form("textBox1")获得输入的内容。若是将属性method="POST"改成method="GET",用语句string s=Request.QueryString("textBox1")获得输入的内容。下边是e11_1B.aspx网页完整文件: <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) {//如用GET方法,修改成:"用户名:"+Request.QueryString("textBox1"); Label1.Text="用户名:"+Request.Form("textBox1");//用POST方法使用的语句 } </script> <body> <form runat=server> <asp:Label id="Label1" runat=server></asp:Label> </form> </body> </html> 如Button按钮改成HyperLink控件如何使用 11.1.2 用Request对象获取客户端浏览器的信息 不一样浏览器或相同浏览器的不一样版本支持不一样的功能,Web应用程序可能要根据不一样的浏览器采起不一样的措施,可用HttpRequest.Browser属性的HttpBrowserCapabilities对象得到用户使用的浏览器信息。见下例: <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) { string s="浏览器的特性以下:"+"<br>"; HttpBrowserCapabilities bc=HttpRequest.Browser; S+="Type="+bc.Type+"<br>"; S+="Name="+bc.Browser+"<br>"; S+="Version="+bc.Version+"<br>"; S+="Major Version="+bc.MajorVersion+"<br>"; S+="Minor Version="+bc.MinorVersion+"<br>"; S+="Platform="+bc.Platform+"<br>"; S+="Is Beta="+bc.Beta+"<br>"; S+="Is Crawler="+bc.Crawler +"<br>"; S+="Is AOL="+bc.AOL +"<br>"; S+="Is Win16="+bc.Win16+"<br>"; S+="Is Win32="+bc.Win32+"<br>"; S+="Supports Frames="+bc.Frames+"<br>"; S+="Supports Tables="+bc.Tables+"<br>"; S+="Supports Cookies="+bc.Cookies+"<br>"; S+="Supports VB Script="+bc.VBScript+"<br>"; S+="Supports Java Script="+bc.JavaScript+"<br>"; S+="Supports Java Applets="+bc.JavaApplets +"<br>"; S+="Supports ActiveX Controls="+bc.ActiveXControls+"<br>"; S+="CDF="+bc.CDF+"<br>"; Label1.Text=s; } </script> <body> <form runat=server> <asp:Label id="Label1" runat=server></asp:Label> </form> </body> </html> 11.1.3 用Request对象获取服务器信息 <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) { string s="服务器的特性以下:"+"<br>"; foreach(string Name in Request.ServerVariables) { s+=Name+":"+Request.ServerVariables(Name)+"<br>" } Label1.Text=s } </script> <body> <form runat=server> <asp:Label id="Label1" runat=server></asp:Label> </form> </body> </html> 11.2 Response对象 与Request是获取客户端HTTP信息相反,Response对象是用来控制发送给用户的信息,包括直接发送信息在浏览器中显示、重定向浏览器到另外一个URL以及设置cookie的值。在ASP.NET中通常不用Response对象发送信息给浏览器,能够用其它方法重定向浏览器到另外一个URL,所以在ASP.Net中使用Response对象的机会愈来愈少了,这里只对Response对象作简单介绍,设置cookie方法在另外一节介绍。 11.2.1 用Response对象发送信息在浏览器中显示 (1) 在浏览器中显示数据,例如:(在ASP.Net不建议这样使用。) <%@ Page language="c#" %> <html> <body> <% Response.Write("<font Size=7>"); Response.Write("Response对象使用"); Response.Write("</font"); Response.Write("<br>"); %> </body> </html> (2) 显示一个文件 <%@ Page language="c#" %> <html> <body> <% System.IO.FileStream fs=new System.IO.FileStream("d:\\asp\\g1.txt",FileMode.Open); IntPtr FileHandle=fs.Handle; Response.WriteFile(FileHand,0,fs.Length); Fs.Close(); %> </body> </html> 11.2.2 用Response对象重定向浏览器 用Response对象重定向浏览器到新浪网主页的例子以下: <html> <script language="c#" runat=server> void EnterBtn_Click(Object src,EventArgs e) { Response.Redirect("http://www.sina.com.cn"); } </script> <body> <form runat=server> <asp:Label runat=server>单击按钮打开新浪网主页</asp:Label> <br> <asp:button text="打开新浪网" Onclick="EnterBtn_Click" runat=server/> </form> </body> </html> 这里实现的功能彻底能够用HyperLink控件实现,请读者试一试。可是若是根据条件用语句实现转向其它网页,使用此语句仍是必要的,例如,有些用户企图不通过登陆直接访问其它网页,在其它网页的Page_Load方法中要进行判断,若是未登陆,可用上述方法直接转向登陆界面。 11.3 Cookie对象 用户用浏览器访问一个网站,因为采用的http的特性,Web服务器并不能知道是哪个用户正在访问,但一些网站,但愿可以知道访问者的一些信息,例如是否是第一次访问,访问者上次访问时是否有未作完的工做,此次是否为其继续工做提供方便等等。用浏览器访问一个网站,能够在此网站的网页之间跳转,当从第一个网页转到第二个网页时,第一个网页中创建的全部变量和对象都将不存在。有时但愿在这些被访问的网页中创建联系,例如一个网上商店,访问者可能从不一样的网页中选取不一样的商品,那么用什么办法记录该访问者选取的商品,也就是通常所说的购物筐如何实现。用Cookie对象能够解决以上问题。 11.3.1 用Cookie对象记录访问的次数 <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) { if(!Page.IsPostBack)//若是用户单击刷新按钮,访问次数不加1 { int Num=1; HttpCookie myCookie=Request.Cookies["VistNum"]; if(myCookie!=null) { myCookie.Value=myCookie.Values+1; Num=myCookie.Values; } else { myCookie=new HttpCookie("VistNum"); myCookie.Values=1; Response.Cookie.Add(myCookie); } Label1.Text="您是第"+Convert.ToString(Num)+"次访问本站"; } } </script> <body> <form runat=server> <asp:Label id="label1" runat=server></asp:Label> </form> </body> </html> 固然,浏览器的Cookies必须设置为容许使用。 11.3.2 网上商店购物筐实现 网上商店网站通常有多个网页,用户能够浏览这些网页,从每一个网页中选择商品,网上商店网站要记录这些要购买的商品,通常把这个功能叫作购物筐,下边的例子介绍购物筐的实现方法。例子中有两个网页,每一个网页有一个CheckBoxList控件,能够选不一样商品,每一个网页都有两个按钮,一个按钮的标题是:把选中商品放入购物筐,另外一个按钮的标题是:结算。 (1) 第一个网页文件e11_3_2A.aspx以下: <html> <script language="c#" runat=server> void button1_Click(Object src,EventArgs e) { string s; HttpCookie myCookie; for(int i=0;i<2;i++) { if(checkBoxList1.Items[i].Selected) { s=checkBoxList1.Items[i].Text; checkBoxList1.Checked=false;//已记录,清除所作选择 myCookie=Request.Cookies[s]; if(myCookie!=null) { myCookie.Value=myCookie.Values+1; } else { myCookie=new HttpCookie(s); myCookie.Values=1; Response.Cookie.Add(myCookie); } } } } void button2_Click(Object src,EventArgs e) { string s="您定购了以下商品:<br>"; HttpCookieCollextion myCookie myCookieS=Request.Cookies; for(i=0;i<myCookieS.Length;i++) { s+=myCookieS[i].Name+":"+myCookieS[i].Value.ToString+"<br>" } label1.Text=s; } </script> <body> <form runat=server> <asp:CheckBoxList id=checkBoxList1 runat=server> <asp:ListItem Text="香蕉"/> <asp:ListItem Text="苹果"/> </asp:CheckBoxList> <br> <asp:button text="把选中商品放入购物筐" Onclick="button1_Click" runat=server/> <br> <asp:button text="结算" Onclick="button2_Click" runat=server/> <br> <asp:HyperLink NaviGateUrl="e11_3_2B.aspx" runat=server/>选择花卉</asp:HyperLink> <br> <asp:Label id="label1" Text="" runat=server></asp:Label> </form> </body> </html> (2) 第二个网页文件e11_3_2B.aspx以下: <html> <script language="c#" runat=server> void button1_Click(Object src,EventArgs e) { string s; HttpCookie myCookie; for(int i=0;i<2;i++) { if(checkBoxList1.Items[i].Selected) { s=checkBoxList1.Items[i].Text; checkBoxList1.Checked=false;//已记录,清除所作选择 myCookie=Request.Cookies[s]; if(myCookie!=null) { myCookie.Value=myCookie.Values+1; } else { myCookie=new HttpCookie(s); myCookie.Values=1; Response.Cookie.Add(myCookie); } } } } void button2_Click(Object src,EventArgs e) { string s="您定购了以下商品:<br>"; HttpCookieCollextion myCookie myCookieS=Request.Cookies; for(i=0;i<myCookieS.Length;i++) { s+=myCookieS[i].Name+":"+myCookieS[i].Value.ToString+"<br>" } label1.Text=s; } </script> <body> <form runat=server> <asp:CheckBoxList id=checkBoxList1 runat=server> <asp:ListItem Text="菊花"/> <asp:ListItem Text="茉莉"/> </asp:CheckBoxList> <br> <asp:button text="把选中商品放入购物筐" Onclick="button1_Click" runat=server/> <br> <asp:button text="结算" Onclick="button2_Click" runat=server/> <br> <asp:HyperLink NaviGateUrl="e11_3_2A.aspx" runat=server/>选择水果</asp:HyperLink> <br> <asp:Label id="label1" Text="" runat=server></asp:Label> </form> </body> </html> (3) 两个个文件都存到宿主目录中,在浏览器中输入地址:http://Localhost/e11_3_2A.aspx,选中某种水果,转到第二个网页e11_3_2B.aspx,选中某种花卉,单击结算按钮,应显示所选的全部商品。固然,本例只是说明问题,由许多不尽合理之处。读者能够采用数据库,用DataGraid控件商品,增长一列,由两个按钮,标题分别是:放到购物筐和从购物筐取出。还应时刻显示购物筐的内容。 Cookies 集合设置 cookie 的值。若指定的 cookie 不存在,则建立它。若存在,则设置新的值而且将旧值删去。 语法 Response.Cookies(cookie)[(key)|.attribute]=value 这里的 cookie 是指定 cookie 的名称。而若是指定了 key,则该 cookie 就是一个字典。Attribute 指定 cookie 自身的有关信息。Attribute 参数能够是下列之一 : Domain 若被指定,则 cookie 将被发送到对该域的请求中去。 Expires 指定 cookie 的过时日期。为了在会话结束后将 cookie 存储在客户端磁盘上,必须设置该日期。若此项属性的设置未超过当前日期,则在任务结束后 cookie 将到期。 HasKeys 指定 cookie 是否包含关键字。 Path 若被指定,则 cookie 将只发送到对该路径的请求中。若是未设置该属性,则使用应用程序的路径。 11.4 Application对象 Application对象生存期和Web应用程序生存期同样长,生存期从Web应用程序网页被访问开始,HttpApplication类对象Application被自动建立,直到没有一个网页被访问时结束,Application对象被自动撤销。所以Application对象中的变量也有相同生存期,而且变量能够被Web应用程序中的全部网页访问。所以,能够在Application对象中创建一些全局的公用变量,因为存储在Application对象中的数值能够被应用程序的全部网页读取,因此Application对象的属性也适合在应用程序的网页之间传递信息。Application对象主要有如下用途:  存储记录在线人数或访问网站总人数的变量。  存储网站共用最新消息,供全部网页更新。  记录网站中个网页同一条广告被点击的次数或时间。  存储供全部网页使用的数据库数据。  不一样用之间通信,例如多用户聊天室,多用户游戏等 本节首先介绍Application对象的用法,而后介绍记录访问网站总人数的实现方法。 11.4.1 Application对象属性 虽然Application对象没有内置的属性,但咱们能够使用如下句法设置用户定义的属性也可称为集合:Application("属性/集合名称")=值,例如,Application("MyVar")="Hello"。用如下语句取出数据:string s= Application("MyVar")。 11.4.2 方法 Application 对象有两个方法,它们都是用于处理多个用户对存储在Application中的数据进行写入的的同步问题。因为存储在Application对象中的数值能够被应用程序的全部网页读取,所以一个用户在修改这个变量时,不容许其它用户修改,这两个方法就是解决这个问题的。  L ock 方 法 Lock 方法阻止其余客户修改存储在 Application 对象中的变量,以确保在同一时刻仅有一个客户可修改和存取 Application 变量。若是用户没有明确调用 Unlock 方法,则服务器将在 .asp 文件结束或超时后即解除对 Application 对象的锁定。  Unlock 方法 和Lock方法相反,Unlock方法容许其余客户修改Application对象的属性。下例介绍一个计数器变量的使用方法。 Application.Lock; Application["counter"]=(Int32)Application["counter"]+1; Application.UnLock; 11.4.3 事件  Application_OnStart事件 第一个浏览器访问Web应用程序网页时,产生的事件。  Application_OnEnd事件 没有浏览器访问时Web应用程序网页时,产生的事件。 Application_OnStart和Application_OnEnd事件的处理过程必须写在global.asax文件之中。 11.4.4 例子:显示访问网站总人数 (1) 创建一个主页文件Default.aspx以下: <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) { if(!Page.IsPostBack)//若是用户单击刷新按钮,计数器不加1 { int num; Application.Lock; Application["counter"]=(Int32)Application["counter"]+1; num=(Int)Application["counter"]; Application.UnLock; label1.Text=Convert.ToString(num); } } </script> <body> <form runat=server> <asp:Label id="label1" Text="" runat=server></asp:Label> <br> <asp:HyperLink id="hLink1" NavigaterUrl="other.aspx" Target="_blank" runat=server> 单击此处转到e1.aspx,计数器不加1。 </asp:HyperLink > </form> </body> </html> (2) 创建other.aspx网页文件以下: <html> <script language="c#" runat=server> void Page_Load(Object src,EventArgs e) { Int num=(Int)Application["counter"]; label1.Text=Convert.ToString(num); } } </script> <body> <form runat=server> <asp:Label id="label1" Text="" runat=server></asp:Label> <br> <asp:HyperLink id="hLink1" NavigaterUrl="default.aspx" runat=server> 单击此处转到dault.aspx,计数器不加1。 </asp:HyperLink > </form> </body> </html> (3) 创建global.asax文件以下: <script language="c#" runat=server> void Application_OnStart(Object src,EventArgs e) { Application.Add("counter",0); } </script> (4) 三个文件都存到宿主目录中,在浏览器重输入URL地址:http://Localhost/,查看显示的计数器数值,单击刷新按钮,查看显示的计数器数值是否改变,转到Other.aspx网页,在转回dault.aspx网页,查看显示的计数器数值是否改变。关闭全部网页,在打开default.aspx网页,显示的计数器值从0开始,这是由于没有网页访问网站时,Application对象被自动撤销。在打开新网页,产生Application_OnStart事件,将counter值为0。为了解决此问题,能够创建一个文件,记录访问网站总人数,初值为0,Application_OnStart事件函数中,从文件取出已访问网站总人数,赋值给counter,Application_OnEnd事件函数中,将counter存到文件中。 (下载源码就到源码网:www.codepub.com) (5) 用记事本建立文件counter_File.txt,其中内容为字符0。存文件到宿主目录中。 (6) 修改global.asax文件以下: <script language="c#" runat=server> void Application_OnStart(Object src,EventArgs e) {//用Server对象对象获得counter_File文件绝对路径 string s=Serve.MapPath(\counter_File.txt); Application.Add("counterFile",s);//保存供Application_OnEnd事件函数使用 System.IO.FileStream fs=new System.IO.FileStream("s",FileMode.OpenOrCreate); System.IO.StreamReader r=new System.IO.StreamReader(fs); s=r.ReadLine(); r.Close(); Application.Add("counter",Convert.ToInt(s)); } void Application_OnEnd(Object src,EventArgs e) {//此时Server对象已不存在,没法用Server对象获得counter_File文件绝对路径 string s= (string)Application("counterFile");//取出保存的计数文件的全路径地址 System.IO.FileStream fs=new System.IO.FileStream("s",FileMode.OpenOrCreate); System.IO.StreamWrite w=new System.IO.StreamWrite(fs); int num=(int)Application("counterFile"); w.Write(num.ToString()); w.Close(); } </script> (7) 再一次访问dault.aspx网页,看是否已解决以上提出的问题。这里还有一个问题,若是用用以下URL访问网页:http://Localhost/Other.aspx,这样计数器就不能计数,解决的方法见Session 对象一节。 11.5 Session对象 前边提到,用浏览器访问一个网站,当在网站的网页之间跳转时,但愿在这些被访问的网页中创建联系,例如一个网上商店的购物筐的实现,这些能够用Cookie实现。用Session对象也能够解决以上问题。 当浏览器开始访问网站的某网页时,Web服务器将自动建立一个Session对象,在Session对象中能够创建一些变量,这个Session对象和Session对象中的变量只能被这个访问者使用,其它访问者不能使用。当用户在网站的网页之间跳转时,Session对象和存储在Session对象中的变量不会被清除,这些变量始终存在。当浏览器离开本网站或超过必定时间和网站没有联系,Session对象被撤销,同时存储在Session中的变量也不存在了。 在ASP中,Session对象的功能本质上是用Cookie实现的,若是用户将浏览器上面的Cookies设置为禁用,那么Session就不能工做。但在ASP.NET中咱们有解决方法,在config.web文件中,咱们将<sessionstate cookieless="false" />设置为true就能够了,也就说,不使用Cookies也能够使用Session。 11.5.1 属性  SessionIDSessionID 属性返回用户的会话标识。在建立会话时,服务器会为每个会话生成一个单独的标识。会话标识以长整形数据类型返回。在不少状况下 SessionID 能够用于 WEB 页面注册统计。  TimeOut Timeout 属性以分钟为单位为该应用程序的 Session 对象指定超时时限。若是用户在该超时时限以内不刷新或请求网页,则该会话将终止。 11.5.2 方法 Session 对象仅有一个方法,就是 Abandon,Abandon 方法删除全部存储在 Session 对象中的对象并释放这些对象的源。若是您未明确地调用 Abandon 方法,一旦会话超时,服务器将删除这些对象。当服务器处理完当前页时,下面示例将释放会话状态。 < % Session.Abandon %> 11.5.3 事件 Session 对象有两个事件可用于在 Session 对象启动和释放是运行过程。  Session_OnStart 事件在服务器建立新会话时发生,当用户第一次浏览网页时,发生Session_OnStart事件。服务器在执行请求的页以前先处理该脚本。Session_OnStart 事件是设置会话期变量的最佳时机,由于在访问任何页以前都会先设置它们。尽管在 Session_OnStart 事件包含 Redirect 或 End 方法调用的状况下 Session 对象仍会保持,然而服务器将中止处理 Global.asa 文件并触发 Session_OnStart 事件的文件中的脚本。为了确保用户在打开某个特定的 W eb 页 时始终启动一个会话,就能够在 S ession_OnStart 事 件中调用 R edirect 方 法。当用户进入应用程序时,服务器将为用户建立一个会话并处理 S ession_OnStart 事 件脚本。您能够将脚本包含在该事件中以便检查用户打开的页是否是启动页,若是不是,就指示用户调用 R esponse.Redirect 方 法启动网页。程序以下 : < SCRIPT RUNAT=Server Language=VBScript> Sub Session_OnStart startPage = "/MyApp/StartHere.asp" currentPage = Request.ServerVariables("SCRIPT_NAME") if strcomp(currentPage,startPage,1) then Response.Redirect(startPage) end if End Sub < /SCRIPT> 上述程序只能在支持 cookie 的浏览器中运行。由于不支持 cookie 的浏览器不能返回 SessionID cookie,因此,每当用户请求 Web 页时,服务器都会建立一个新会话。这样,对于每一个请求服务器都将处理 Session_OnStart 脚本并将用户重定向到启动页中。  Session_OnEnd 事件在会话被放弃或超时发生。若是用户在指定时间内没有请求或刷新应用程序中的任何页,会话将自动结束。这段时间的默认值是 2 0 分 钟。能够经过在 I nternet 服 务管理器中设置“应用程序选项”属性页中的“会话超时”属性改变应用程序的默认超时限制设置。应依据您的 W eb 应 用程序的要求和服务器的内存空间来设置此值。例如,若是您但愿浏览您的 W eb 应 用程序的用户在每一页仅停留几分钟,就应该缩短会话的默认超时值。过长的会话超时值将致使打开的会话过多而耗尽您的服务器的内存资源。对于一个特定的会话,若是您想设置一个小于默认超时值的超时值,能够设置 S ession 对 象的 T imeout 属 性。例如,下面这段脚本将超时值设置为 5 分 钟。 < % Session.Timeout = 5 %> 固然你也能够设置一个大于默认设置的超时值,Session.Timeout 属性决定超时值。你还能够经过 Session 对象的 Abandon 方法显式结束一个会话。例如,在表格中提供一个“退出”按钮,将按钮的 ACTION 参数设置为包含下列命令的 .asp 文件的 URL。 < % Session.Abandon %>  11.5.4 用Session对象实现网上商店购物筐 本例要求和用Cookie实现网上商店购物筐彻底同样,只是用Session对象实现,具体代码以下: (1) 第一个网页文件e11_5_4A.aspx以下: <html> <script language="c#" runat=server> void button1_Click(Object src,EventArgs e) { string s; for(int i=0;i<2;i++) { if(checkBoxList1.Items[i].Selected) { s=checkBoxList1.Items[i].Text; checkBoxList1.Checked=false;//已记录,清除所作选择 if(Session[s]!=null) { Session[s]=Session[s]+1; } else { Session[s]=1; } } } } void button2_Click(Object src,EventArgs e) { string s="您定购了以下商品:<br>"; string keyName; for(i=0;i<Session.Count;i++) { keyName=Session.Keys[i]; s+=keyName+":"+Session[keyName].ToString+"<br>" } label1.Text=s; } </script> <body> <form runat=server> <asp:CheckBoxList id=checkBoxList1 runat=server> <asp:ListItem Text="香蕉"/> <asp:ListItem Text="苹果"/> </asp:CheckBoxList> <br> <asp:button text="把选中商品放入购物筐" Onclick="button1_Click" runat=server/> <br> <asp:button text="结算" Onclick="button2_Click" runat=server/> <br> <asp:HyperLink NaviGateUrl="e11_5_4B.aspx" runat=server/>选择花卉</asp:HyperLink> <br> <asp:Label id="label1" Text="" runat=server></asp:Label> </form> </body> </html> (2) 第二个网页文件e11_5_4B.aspx以下: <html> <script language="c#" runat=server> void button1_Click(Object src,EventArgs e) { string s; for(int i=0;i<2;i++) { if(checkBoxList1.Items[i].Selected) { s=checkBoxList1.Items[i].Text; checkBoxList1.Checked=false;//已记录,清除所作选择 if(Session[s]!=null) { Session[s]=Session[s]+1; } else { Session[s]=1; } } } } void button2_Click(Object src,EventArgs e) { string s="您定购了以下商品:<br>"; string keyName; for(i=0;i<Session.Count;i++) { keyName=Session.Keys[i]; s+=keyName+":"+Session[keyName].ToString+"<br>" } label1.Text=s; } </script> <body> <form runat=server> <asp:CheckBoxList id=checkBoxList1 runat=server> <asp:ListItem Text="菊花"/> <asp:ListItem Text="茉莉"/> </asp:CheckBoxList> <br> <asp:button text="把选中商品放入购物筐" Onclick="button1_Click" runat=server/> <br> <asp:button text="结算" Onclick="button2_Click" runat=server/> <br> <asp:HyperLink NaviGateUrl="e11_5_4A.aspx" runat=server/>选择水果</asp:HyperLink> <br> <asp:Label id="label1" Text="" runat=server></asp:Label> </form> </body> </html> (3) 两个个文件都存到宿主目录中,在浏览器中输入地址:http://Localhost/e11_3_2A.aspx,选中某种水果,转到第二个网页e11_3_2B.aspx,选中某种花卉,单击结算按钮,应显示所选的全部商品。象前边所说,本例不尽合理,读者能够采用数据库,用DataGraid控件商品,增长一列,由两个按钮,标题分别是:放到购物筐和从购物筐取出。还应时刻显示购物筐的内容。 11.6 Server 对象 Server对象提供对服务器上的资源进行访问的方法和属性,主要包括:获得服务器的计算机名称,设置脚本程序的失效时间,将HTML的特殊标记转变为ASCII字符,获得文件的真实路径等等,本节将逐一介绍这些方法。 11.6.1 属性MachineName和ScriptTimeout (1) 属性MachineName 该属性用来获取当前运行Web应用程序的Web服务器的计算机名称,使用方法以下:string s=Server.MachineName;这个计算机名称能够用以下办法查到:打开”控制面板”,选中”系统”中的”计算机名”,应和用Server对象的属性MachineName得到计算机名称一致。 (2) 属性ScriptTimeout Web应用程序因为运行在计算机网络中,因为网络的缘由,一些程序可能没法完成,一直在等待,这将极大消耗Web服务器的资源,为了不这种状况,能够设置程序运行的最长时间,即设置属性ScriptTimeout,在脚本程序运行超过属性ScriptTimeout指定时间以后即做超时处理,也就中止程序运行。如如下代码指定服务器处理脚本程序在100秒后超时:Server.ScriptTimeout=100,其默认值为90秒。 11.6.2 HtmlEncode方法 HTML标记语言中,有些ASCII字符被做为标记,例如字符串:<br>中的<和>都是标记,如须要显示这些字符,必须做特殊处理,例如为了在浏览器中正确显示以下字符串:”<br>是换行标记”,字符串必须写为以下形式: <asp:Label id="label1" Text="%3cbr%3c是换行标记" runat=server></asp:Label>; 也能够用Server对象的属性HtmlEncode方法,用法以下: <asp:Label id="label1" runat=server>Server.HtmlEncode(”<br>是换行标记”)</asp:Label>; 11.6.3 URLEncode方法 URL是Uniform Resource Location(统一资源定位器)的简称,URL用来定位一个网页的。在URL中,有些ASCII字符具备特殊含义,必须作特殊处理。例如http://www.sina.com/中的字符/,用Server对象URLEncode方法处理, string s=”http://www.sina.com/”; 11.6.4 MapPath方法 网页中网页文件的路径通常是以宿主目录为根目录,不一样的系统中,宿主目录所在的实际目录并不相同,并且网页也可能在虚拟目录中。所以网页文件的路径并非网页文件的实际路径。而在用File类处理文件时,则要求文件的地址必须是实际的全路径,Server对象的MapPath方法提供这两种路径的转换方法,例如,f1.aspx文件存在宿主目录下的Test目录下,用Server对象获得f1.aspx文件绝对路径方法以下: string s=Serve.MapPath(\Test\f1.aspx);//这里\表示以宿主目录 也能够用以下语句: string s=Serve.MapPath(Test\f1.aspx);//表示单前网页所在的目录的子目录Test 习题 (1) 如何实现记录访问网站的在线人数。(提示:增长一个Application对象变量做为计数器,Application_Start事件函数中计数器为0,Session_Start事件函数中计数器加1,Session_End事件函数中计数器减1,每一个网页的Page_Load事件函数中用Label控件显示计数器值。) (2) 用Application对象创建一个2人聊天室。若是是多人聊天室,又如何实现。 (3) 用户不通过主页,直接访问网站的某网页,将不能时访问者总数加1,如何防止。 (4) 将书中的例子用Visual Studio.Net实现。 第十二章 可扩展标记语言 12.1 HTML及其缺点 Internet提供了全球范围的网络互连与通讯功能,Web技术的发展更是一日千里,其丰富的信息资源给人们的学习和生活带来了极大的便利。特别是应运而生的HTML(超文本置标语言),以简单易学、灵活通用的特性,令人们发布、检索、交流信息都变得很是简单,从而使Web成了最大的环球信息资源库。然而,电子商务、电子出版、远程教育等基于Web的新兴领域的全面兴起使得传统的Web资源更加复杂化、多样化,数据量的日趋庞大对网络的传输能力也提出更高的要求,人们对Web服务功能的需求也达到更高的标准。而传统的HTML因为自身特色的限制,不能知足这些要求。HTML主要有以下不足:  HTML的标记都是预先定义的,用户不能自定义有意义的标记,可扩展性差。  HTML的显示方式内嵌在数据中,这样在建立文本时,要同时考虑输出格式,若是由于需求不一样而须要对一样的内容进行不一样风格的显示时,要从头建立一个全新的文档,重复工做量很大。不能对数据按照不一样的需求进行多样化显示等个性化服务。  HTML缺少对数据结构的描述,对于应用程序理解文档内容、抽取语义信息都有诸多不便。不能进行智能化的语义搜索。不能对不一样平台、不一样格式的数据源进行数据集成和数据转化等。  HTML语言不能描述矢量图形、数学公式、化学符号等特殊对象。 12.2 SGML(标准通用置标语言) SGML(Standard Generalized Markup Language)是一种通用的文档结构描述置标语言,为语法置标提供了异常强大的工具,同时具备极好的扩展性,所以在数据分类和索引中很是有用。但SGML复杂度过高,不适合网络的平常应用,加上开发成本高、不被主流浏览器所支持等缘由,使得SGML在Web上的推广受到阻碍。 12.3 XML(可扩展置标语言) XML(eXtensible Markup Language)是由W3C于1998年2月发布的一种标准。它一样是SGML的一个简化子集,它将SGML的丰富功能与HTML的易用性结合到Web的应用中,以一种开放的自我描述方式定义了数据结构,在描述数据内容的同时能突出对结构的描述,从而体现出数据之间的关系。XML的优势以下:  XML简单易用,功能强大。  XML容许各个组织、我的创建适合本身须要的标记集合,而且这些标记能够用通用的工具显示。例如定义数学、化学、音乐等专用标记。  XML的最大优势在于它的数据存储格式不受显示格式的制约。通常来讲,一篇文档包括三个要素:数据、结构以及显示方式。XML把文档的三要素独立开来,分别处理。首先把显示格式从数据内容中独立出来,保存在样式表文件(Style Sheet)中,这样若是须要改变文档的显示方式,只要修改样式表文件就好了。XML的自我描述性质可以很好地表现许多复杂的数据关系,使得基于XML的应用程序能够在XML文件中准确高效地搜索相关的数据内容,忽略其余不相关部分。  XML还有其余许多优势,好比它有利于不一样系统之间的信息交流,彻底能够充当网际语言,并有但愿成为数据和文档交换的标准机制。 因为以上优势,XML必将在商务的自动化处理,信息发布,智能化的Web应用程序和数据集成等领域被普遍被使用。 12.4 XML的文档格式 首先介绍XML文档内容的基本单元——元素,它的语法格式以下: 〈标签〉文本内容〈/标签〉 元素是由起始标签、元素内容和结束标签组成。用户把要描述的数据对象放在起始标签和结束标签之间。例如: <姓名>王平</姓名> 不管文本内容有多长或者多么复杂,XML元素中还能够再嵌套别的元素,这样使相关信息构成等级结构。下面的例子中,在<学生>的元素中包括了全部学生的信息,每一个学生都由<学生>元素来描述,而<学生>元素中又嵌套了<编号>、<姓名>、<性别>和<年龄>元素。完整XML文件student.xml内容以下,例1: <?xml version="1.0" encoding="GB2312"?> <?xml-stylesheet type="text/xsl" href="student1.xsl"?> <学生> <编号>001</编号> <姓名>张三</姓名> <性别>男</性别> <年龄>20</年龄> </学生> 除了元素,XML文档中能出现的有效对象是:声明指令、注释、根元素、子元素和属性。  声明指令 声明指令给XML解析器提供信息,使其可以正确解释文档内容,它的起始标识是“<?”,结束标识是“?>”。常见的XML声明就是一个处理指令: <?xml version="1.0" encoding="GB2312"?> 该处理指令指明XML使用的版本号和文档的编码方式是"GB2312"。又如: <?xml-stylesheet type="text/xsl" href="student1.xsl"?> 使用student1.xsl样式表文件显示本XML文档。  注释 注释是XML文件中用做解释的字符数据,XML处理器不对它们进行任何处理。注释是用“<!--”和“ --> ”引发来的,能够出如今XML元素间的任何地方,可是不能够嵌套: <!--这是一个注释-->  根元素和子元素 若是一个元素从文件头的序言部分以后开始一直到文件尾,包含了文件中全部的数据信息,咱们称之为根元素。XML元素是能够嵌套的,那么被嵌套在内的元素称为子元素。在前面的例子中,<编号>就是<学生>的子元素。  属性 属性给元素提供进一步的说明信息,它必须出如今起始标签中。属性以名称/取值对出现,属性名不能重复,名称与取值之间用等号“=”分隔,并用引号把取值引发来。例如: <工资 currency=“US$”> 25000 </工资> 上例中的属性说明了薪水的货币单位是美圆。  XML文档的基本结构 XML文档的基本结构由序言部分和一个根元素组成。序言包括了XML声明和DTD(或者是XML Schema),DTD(Document Type Define,文档定义类型)和XML Schema都是用来描述XML文档结构的,也就是描述元素和属性是如何联系在一块儿的。例如,在例1的文档前面加上以下的序言部分,就构成了一个完整的XML文档: <?xml version="1.0" encoding="GB2312"?> <?xml-stylesheet type="text/xsl" href="student1.xsl"?> <!DOCTYPE employees SYSTEM“employees.dtd”> 一个XML文档中有且仅有一个根元素,其余全部的元素都是它的子元素,在例1中,<学生>就是根元素。  格式良好的”(Well-Formed)XML文档 一个XML文档首先应当是“格式良好的”(Well-Formed),该规定的正式定义位于:http://www.w3.org/TR/REC-xml。“格式良好的”XML文档除了要知足根元素惟一的特性以外,还包括: (1) 起始标签和结束标签应当匹配:结束标签是必不可少的; (2) 大小写应一致:XML对字母的大小写是敏感的,<employee>和<Employee>是彻底不一样的两个标签,因此结束标签在匹配时必定要注意大小写一致; (3) 元素应当正确嵌套:子元素应当彻底包括在父辈元素中,下面的例子就是嵌套错误: <A> <B> </A> </B> 正确的嵌套方式以下: <A> <B> </B> </A> (4) 属性必须包括在引号中;元素中的属性是不容许重复的。 12.5 用XSL文件显示XML文档 因为XML文档只是定义数据的结构,并不包含显示的格式。如要按指定格式显示这些数据,还要使用CSS文件或XSL文件定义显示格式。这里使用三个XSL文件按不一样显示格式显示同一个XML文件。首先定义一个student1.xsl文件显示上边XML文档,文件以下: <?xml version="1.0" encoding="GB2312"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <xsl:for-each select="学生"> <xsl:value-of select="编号"/>, <xsl:value-of select="姓名"/>, <xsl:value-of select="性别"/>, <xsl:value-of select="年龄"/> </xsl:for-each> </xsl:template> </xsl:stylesheet> 将student1.xsl文件和文件student1.xml存到同一文件夹,双击student1.xml文件,打开IE5.0,显示效果以下: 001, 张三, 男, 20 能够定义不一样的xsl文件,以不一样的显示方式显示student1.xml文件。例如student2.xsl文件以下: <?xml version="1.0" encoding="GB2312"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"/> <title>演示2</title> </head> <body> <xsl:for-each select="学生"> <table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1"> <tr> <td width="50%">编号</td> <td width="50%"> <xsl:value-of select="编号"/> </td> </tr> <tr> <td width="50%">姓名</td> <td width="50%"> <xsl:value-of select="姓名"/> </td> </tr> <tr> <td width="50%">性别</td> <td width="50%"> <xsl:value-of select="性别"/> </td> </tr> <tr> <td width="50%">年龄</td> <td width="50%"> <xsl:value-of select="年龄"/> </td> </tr> </table> </xsl:for-each> </body> </html> </xsl:template> </xsl:stylesheet> 例如用student3.xsl以不一样的显示方式显示student1.xml文件。文件以下: <?xml version="1.0" encoding="GB2312"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"/> <title>演示3</title> </head> <body> <p align="center">学生信息</p> <xsl:for-each select="学生"> <form method="POST" action="--WEBBOT-SELF--"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="100%" id="AutoNumber1"> <tr> <td width="45%" align="right">编号:</td> <td width="5%" align="center"> </td> <td width="50%"> <input type="text" name="编号" size="20"> <xsl:attribute name="value"> <xsl:value-of select="编号"/> </xsl:attribute> </input> </td> </tr> <tr> <td width="45%" align="right">姓名:</td> <td width="5%" align="center"> </td> <td width="50%"> <input type="text" name="姓名" size="20"> <xsl:attribute name="value"> <xsl:value-of select="姓名"/> </xsl:attribute> </input> </td> </tr> <tr> <td width="45%" align="right">性别:</td> <td width="5%" align="center"> </td> <td width="50%"> <input type="text" name="性别" size="20"> <xsl:attribute name="value"> <xsl:value-of select="性别"/> </xsl:attribute> </input> </td> </tr> <tr> <td width="45%" align="right">年龄:</td> <td width="5%" align="center"> </td> <td width="50%"> <input type="text" name="年龄" size="20"> <xsl:attribute name="value"> <xsl:value-of select="年龄"/> </xsl:attribute> </input> </td> </tr> </table> <p align="center"> <input type="submit" value="Submit" name="B1"/> <input type="reset" value="Reset" name="B2"/> </p> </form> </xsl:for-each> <p align="center"> </p> </body> </html> </xsl:template> </xsl:stylesheet> 读者能够试一下。 12.6 .NET对XML的支持 首先,建立一个XML文档,文件名为MyXMLFile.xml,内容以下: <?xml version="1.0" encoding="GB2312" ?> <!--这是一个注释--> <bookstore> <book 出版社="电子工业出版社"> <书名>SQL实用全书</书名> <做者>Rafe Colburn</做者> <出版日期>2001年6月</出版日期> <价格>34.00</价格> </book> <book 出版社="清华大学出版社"> <书名>C#高级编程</书名> <做者>Simon Robinson</做者> <出版日期>2002年6月</出版日期> <价格>128.00</价格> </book> <book 出版社="人民邮电出版社"> <书名>ASP.NET从入门到精通</书名> <做者>Chris Payne</做者> <出版日期>2002年1月</出版日期> <价格>41.00</价格> </book> <book 出版社="中国青年出版社"> <书名>精通C#与ASP.NET程序设计</书名> <做者>孙三才</做者> <出版日期>2003年6月</出版日期> <价格>39.00</价格> </book> <book 出版社="电子工业出版社"> <书名>ASP.NET实用全书</书名> <做者>张三</做者> <出版日期>2004年6月</出版日期> <价格>55.00</价格> </book> </bookstore> 请读者用IE浏览器(5.0以上版本)浏览MyXMLFile.xml文件,单击标记前的减号(或加号),看一下效果。 网页文件c8-1-1A.aspx用来读出每本书的书名、做者、出版日期、价格等数据。 <%@ Page Language="C#" %> <%@ Import Namespace="System.Xml" %> <html> <title>读XML文件</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\MyXMLFile.xml"; XmlTextReader dr= new XmlTextReader(FileNameString); while(dr.Read()) if(dr.NodeType==XmlNodeType.Text) ListBox1.Items.Add(dr.Value); } </script> <body> <h2>读XML文件</h2> <form runat=server> <asp:ListBox id="ListBox1" runat="server"/> </form> </body> </html> 当用XmlTextReader读(dr.Read())Xml文档时,每次读出一个节点的数据。 一个Xml文档的元素,能够分为两大类,第一类是文本,第二类是标记。文本是Xml文档的数据,在两个标记之间的文本被称为一个文本节点,例如,<书名>SQL实用全书</书名>中的”SQL实用全书”是一个文本节点。这个节点的类型是:Xml.XmlNodeType.Text。 第二类Xml文档的元素是标记,它能够分为如下几大类:注释标记、声明标记、开始标记,结束标记,每类都被称为一个Xml文档的标记节点,例如,<!--这是一个注释-->是注释标记,注释标记的节点类型为:Xml.XmlNodeType.Comment,注释的内容为dr.Value。<?xml version="1.0" encoding="GB2312" ?>是声明标记,其中包括两个声明:xml version="1.0"和encoding="GB2312",等号前内容的被称为声明的名称(dr.Name),等号后内容的被称为声明的值(dr.Value)。声明标记的节点类型为:Xml.XmlNodeType.XmlDeclartion。<book 出版社="电子工业出版社">是开始标记,book被称为标记名称(dr.Name),出版社被称为属性(dr.AttributeName),"电子工业出版社"被称为属性的值(Value)。开始标记的节点类型为:Xml.XmlNodeType.Element。</book>是结束标记,book被称为标记名称(dr.Name)。结束标记的节点类型为:Xml.XmlNodeType.EndElement。 本网页的Page_Load方法中,用dr.Read()读Xml文档,每次读出一个节点的数据,用语句if(dr.NodeType==XmlNodeType.Text)判断是不是文本节点,若是是文本节点,则把文本内容加到ListBox1。若是但愿只显示书名,则判断语句能够改成:if(dr.NodeType==XmlNodeType.Text && dr.Name==”书名”)。 网页文件c8-1-1B.aspx用来读出标记book的属性,具体内容以下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Xml" %> <html> <title>读XML文件</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\MyXMLFile.xml"; XmlTextReader dr= new XmlTextReader(FileNameString); while(dr.Read()) if(dr.NodeType==XmlNodeType.Element) for(int i=0;i<dr.AttributeCount;i++) ListBox1.Items.Add(dr.GetAttribute(i)); } </script> <body> <h2>读XML文件</h2> <form runat=server> <asp:ListBox id="ListBox1" runat="server"/> </form> </body> </html> 若是,显示注释,改成下列语句: public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\MyXMLFile.xml"; XmlTextReader dr= new XmlTextReader(FileNameString); while(dr.Read()) if(dr.NodeType==XmlNodeType.Comment) ListBox1.Items.Add(dr.Value); } 若是,显示声明,改成下列语句:(见c8-1-1c.aspx) <%@ Page Language="C#" %> <%@ Import Namespace="System.Xml" %> <html> <title>读XML文件</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\MyXMLFile.xml"; XmlTextReader dr= new XmlTextReader(FileNameString); while(dr.Read()) if(dr.NodeType==XmlNodeType.XmlDeclaration) ListBox1.Items.Add(dr.Name+" "+dr.Value); } </script> <body> <h2>读XML文件</h2> <form runat=server> <asp:ListBox id="ListBox1" runat="server"/> </form> </body> </html> 下例用DataGrid控件显示MyXMLFile.xml,(见c8-1-1D.aspx) <%@ Page Language="C#" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Data" %> <html> <title>读XML文件</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\MyXMLFile.xml"; DataSet ds = new DataSet(); ds.ReadXml(FileNameString); DataGrid1.DataSource=ds; DataGrid1.DataMember="book"; DataGrid1.DataBind(); } </script> <body> <h2>读XML文件</h2> <form runat=server> <asp:DataGrid id="DataGrid1" runat="server"/> </form> </body> </html> 12.7 ADO.NET和XML 仔细察看MyXMLFile.xml文件,它和数据库的表有对应关系,标记<bookstore>之间的内容能够看做一个数据库的表,标记<book>之间的内容能够看做一个数据库的表的一个记录,标记<书名>、<做者>、<出版日期>、<价格>能够看做一个数据库的表的字段,这些标记之间的文本能够看做这些字段的数据。咱们知道,一个字段还有一些其余属性,例如,字段的数据类型,为了表示这些属性,能够使用DTD(Document Type Define,文档定义类型)和XML Schema来描述XML文档的数据结构,也就是描述元素和属性是如何联系在一块儿的。微软的.NET系统支持用XML Schema来描述XML文档的数据结构,下例介绍如何使用XML Schema,见文件C8-1-1F.aspx。 <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.IO" %> <html> <title>DataGrid</title> <script runat=server> DataSet ds; public void Page_Load(Object sender, EventArgs e) { string txtConn="DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;"; string txtCommand="SELECT employeeid, firstname, lastname FROM Employees"; SqlConnection conn = new SqlConnection(txtConn); SqlDataAdapter da = new SqlDataAdapter(txtCommand, conn); ds = new DataSet(); da.Fill(ds, "MyTable"); // Display the data grid.DataSource = ds.Tables["MyTable"]; grid.DataBind(); grid.Visible = true; } public void SvaeXmlWithSchema(Object sender, EventArgs e) { ds.WriteXml(Server.MapPath("XmlFile1.xml"), XmlWriteMode.WriteSchema); } public void SvaeXmlNoSchema(Object sender, EventArgs e) { StreamWriter sw = new StreamWriter(Server.MapPath("XmlFile2.xml")); sw.Write(ds.GetXml()); sw.Close(); } </script> <body bgcolor="ivory" style="font-family:arial;font-size:9pt"> <!-- ASP.NET topbar --> <h2>将数据库表存为带XML架构和不带XML架构XML文件</h2> <form runat=server> <asp:linkbutton runat="server" id="SvaeXml1" text="将数据库表存为带XML架构XML文件" οnclick="SvaeXmlWithSchema" /> <br> <asp:linkbutton runat="server" id="SvaeXml2" text="将数据库表存为不带XML架构XML文件" οnclick="SvaeXmlNoSchema" /> <asp:DataGrid id="grid" runat="server" visible="false" AutoGenerateColumns="true" CssClass="Shadow" BackColor="white" CellPadding="2" CellSpacing="2" GridLines="none" BorderStyle="solid" BorderColor="black" BorderWidth="1" font-size="x-small" font-names="verdana"> <AlternatingItemStyle BackColor="palegoldenrod" /> <ItemStyle BackColor="beige" /> <HeaderStyle ForeColor="white" BackColor="brown" Font-Bold="true" /> </asp:DataGrid> </form> </body> </html> 单击两个按钮,能够建立带XML架构和不带XML架构XML文件,文件名为"XmlFile1.xml"和"XmlFile1.xml"。用浏览器察看这两个XML文件,能够看到它们的区别。如建立了有架构的XML文件,能够修改该文件,例如,修改字段类型。用网页文件C8-1-1G能够从新打开带XML架构或不带XML架构XML文件。 <%@ Page Language="C#" %> <%@ Import Namespace="System.Xml" %> <%@ Import Namespace="System.Data" %> <html> <title>读XML文件</title> <script runat=server> public void Page_Load(Object sender, EventArgs e) { string FileNameString="d:\\asp\\bookExample\\XmlFile.xml"; DataSet ds = new DataSet(); ds.ReadXml(FileNameString); DataGrid1.DataSource=ds; DataGrid1.DataMember="MyTable"; DataGrid1.DataBind(); } </script> <body> <h2>读带XML架构和不带XML架构XML文件</h2> <form runat=server> <asp:DataGrid id="DataGrid1" runat="server"/> </form> </body> </html> 12.8 使用Visual Studio.Net创建和显示XML文档 (1) 建立一个Web应用程序框架,项目名为UseXml。 (2) 在窗体中放置控件DataGrid,其属性Name=DataGrid1。 (3) 放工具箱的2个Button控件到窗体,修改属性Text分别为:存为带XML架构的XML文件,读带XML架构的XML文件。 (4) 新建一个XML文件。单击菜单项”项目/添加新项”,弹出标题为添加新项的窗口,在窗口中选中XML文件,文件名为MyXMLFile.xml,单击打开按钮,增长一个XML文件 (5) 在文件添加以下内容: <?xml version="1.0" encoding="GB2312" ?> <!--这是一个注释--> <bookstore> <book 出版社="电子工业出版社"> <书名>SQL实用全书</书名> <做者>Rafe Colburn</做者> <出版日期>2001年6月</出版日期> <价格>34.00</价格> </book> <book 出版社="清华大学出版社"> <书名>C#高级编程</书名> <做者>Simon Robinson</做者> <出版日期>2002年6月</出版日期> <价格>128.00</价格> </book> <book 出版社="人民邮电出版社"> <书名>ASP.NET从入门到精通</书名> <做者>Chris Payne</做者> <出版日期>2002年1月</出版日期> <价格>41.00</价格> </book> <book 出版社="中国青年出版社"> <书名>精通C#与ASP.NET程序设计</书名> <做者>孙三才</做者> <出版日期>2003年6月</出版日期> <价格>39.00</价格> </book> <book 出版社="电子工业出版社"> <书名>ASP.NET实用全书</书名> <做者>张三</做者> <出版日期>2004年6月</出版日期> <价格>55.00</价格> </book> </bookstore> (6) 单击MyXMLFile.xml窗口下的数据,能够看到用表格显示的XML文件。 (7) 为Page_Load事件函数增长语句: private void Page_Load(object sender, System.EventArgs e) { string FileNameString="MyXMLFile.xml"; DataSet ds = new DataSet(); ds.ReadXml(Server.MapPath(FileNameString)); DataGrid1.DataSource=ds; DataGrid1.DataMember="book"; DataGrid1.DataBind(); // 在此处放置用户代码以初始化页面 } (8) 运行,能够看到用表格显示的XML文件。 (9) 打开MyXMLFile.xml文件,单击菜单项”XML/建立架构”,将建立MyXMLFile.xsd文件,打开此文件,能够修改每一个字段的属性。 (10) 为单击存为带XML架构的XML文件按钮事件(Click)函数增长语句(双击Click事件): (11) 为单击读带XML架构的XML文件按钮事件(Click)函数增长语句(双击Click事件): 第十三章 Web服务 Micosoft.Net平台架构中的分布式系统主要包括两部分:用ASP.Net技术构建服务器端动态网页,以及Web服务(Web Service或XML Web Service)。前边章节已详细介绍了构建服务器端动态网页的方法,本节将介绍Web服务的基本概念和构建方法。 13.1 Web服务的概念和用途 Web中不管是静态网页仍是动态网页,数据都被嵌入到网页中,网页的服务对象都是人,用户能够很容易阅读这些网页。但若是一个程序使用这种方式得到数据,会是十分困难的,程序必须从网页中把数据分离,才能加以利用。而用一个程序在Web中得到数据有时又是十分必要的,例如:一个气象台总站但愿经过Internet网得到各个基层气象台的各类资料,在网上以统一的网页对外发布。气象台总站但愿各个基层气象台提供一个Internet网的服务,能根据总站的要求,自动提供相应的资料。相似的例子不少,例如一个很大的单位的总部和下属单位之间信息系统的整合,一个综合网站但愿自动得到其它网站提供的信息等等。这种需求实际上就是Web服务。 为实现这种功能有不少困难,各个基层气象台使用的系统可能彻底不一样,即便使用相同的操做系统,也可能使用不一样数据库系统,数据库中定义的字段可能不一样,数据库应用程序可能使用不一样的语言编制,即便这些彻底相同,还可能数据的表示方式不相同,数据格式,数据的位数等等。为解决这些问题,已提出了许多方案,例如:微软的分布式控件对象模型(DCOM)、对象管理组织(OMG)的公用对象请求代理程序体系结构(CORBA)、SUN公司的远程方法调用(RMI)等等,但这些方法都不能很好的解决以上问题。 Micosoft.Net的Web服务为实现这种功能提供了完整的解决方案。Web服务使用Http协议在Internet网上传输数据和消息,用XML扩展标记语言描述数据,用SOAP表示消息,SOAP是一个简单的、重量轻的基于XML的协议,用于交换Web上的结构化的和模式化的信息。用Micosoft.Net的Web服务实现气象台总站所需功能的大概思路是这样的,每一个基层气象台在本身的系统中提供一个Internet网远程调用函数,该函数用Http协议接受用SOAP表示的调用,并把函数的返回值用XML扩展标记语言描述,用SOAP表示后,用Http协议返回给调用者。气象台总站只要使用Http和SOAP协议逐一调用这些Web远程函数,就能够得到各个基层气象台的资料了。因为这些协议都是被普遍接受的协议,能被不一样的系统所接受,也就解决了以上所提出的问题。 有以上叙述可知,Web服务不追求代码的可移植性,而是提供一个可行的解决方案来加强数据和系统的互操做性。有许多Web服务的定义,比较简单又比较容易理解的描述是:Web服务是一个可经过Http、SOAP和XML协议进行访问的Web远程函数库。 刚才讨论的问题只是Web服务的几个应用,还有许多其它用途,例如:。  应用程序集成 你能够使用Web服务以一种集成的方式整合表面上看上去彻底不一样的现有应用程序。例如许多公司的每一个部门都有定制的软件,产生一系列有用可是孤立的数据和业务逻辑。为了管理上的方便,很是有必要把这些应用程序功能集合到一块儿。利用Web服务,就有可能把现有的应用程序中的数据和功能以Web服务方式提供给其它部门。而后能够建立一个集成的应用程序,加强各部门之间的互操做性。  代码复用 在软件开发行业,大部分开发者都依赖代码复用。过去开发者们为了利用他人已经实现了的代码,或者将代码段复制到本身的代码中,作一些改动以适应本身得须要,或者在服务器或我的计算机上安装一个控件库,让应用程序来访问这个库。这将使得代码有不少个版本,而这些版本间可能只有细微差异,却分散在各个地方。当代码最初的开发者决定对代码更新一下或者改正一下错误,要把这些改变告诉全部使用这些代码的开发者的时候,将是很是困难的。若是咱们把代码放在一个中心位置存储,让全部人都访问这儿,这不是很好吗?这样原创者能够在作了一些增补或者修正以后,可以当即提供给全部使用它的人。用Web服务能够实现以上设想,远程调用Web服务中的方法,就象调用本地函数同样方便。  工做流程解决方案 有些工做是很是复杂的,例如,货物的运输,可能要使用多种交通工具,火车、汽车、轮船等,商业上的一笔交易,都是一个很是复杂的流程,流程的每个环节都由不一样部门的不一样的程序进行控制,如何创建这些控制程序之间的联系,是十分重要的。使用Web服务是一个很好的解决方案。经过Web服务,使各个流程控制程序创建联系,彻底实现自动控制和管理。  新的销售方式 如今软件的销售方式通常是用户把软件买回去,安装在本身的计算机中。有了Web服务,就能够提供软件的服务,按次收费。  由Web服务组成的自动化系统 不远的未来,信息家电将要联接到Internet网上,PDA、手机,甚至各类嵌入式设备也要上网,这些设备和其它设备之间经过Web服务创建联系也是一种可行的方案。 13.2 创建Web服务 Web服务仍采用客户/服务器模式(Cient/Server)。本节介绍在服务器端应作的工做,包括创建供客户端调用的Web服务方法,以及为了客户端使用Web服务方法,提供给客户端描述该Web服务的WSDL文档。 13.2.1 用记事本创建Web服务 创建一个Web服务文件和创建一个普通网页文件的步骤基本同样,下边是一个最简单的Web服务文件,其它程序访问其中的Web服务方法时,将返回参数a和b的和,具体程序代码以下: <%@ WebService Language="C#" Class="MyClass"%> using System; using System.Web.Services; public class MyClass:WebService { [WebMethod] public int MyWebMethod (int a,int b) { return a+b; } //其它WebMethod  } 在文件中,第一行的语句表示这是一个Web服务文件,使用C#语言,Web服务的类名是MyClass。因为创建的Web服务类必须以WebService类为基类,因此必须引入命名空间System.Web.Services,这个Web服务类必须是一个公有类。可供其它程序访问的方法叫Web服务方法,在其头部必须增长关键字[WebMethod],表示这个方法是一个Web服务方法,这个方法必须是一个公有方法。 创建文件后,以asmx为扩展名存盘,存到网站的宿主目录中或其任意子目录中。使用URL定位这个Web服务文件。如今使用浏览器检验这个Web服务,若是把Web服务文件以MyAdd.asmx存到网站的宿主目录中,在浏览器中URL地址栏中输入以下地址:http://localhost/MyAdd.asmx,浏览器中显示以下: 点击MyWebMethod,浏览器中显示以下: 在编辑框中输入两个加数分别为10和20,而后点击invote按钮,在浏览器上显示以下内容,这是用XML标记表示的调用Web服务方法MyWebMethod返回的结果。 <?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">30</string> 13.2.2 用Visual Studio.Net创建Web服务 若是使用Visual Studio.Net创建这个Web服务文件,具体步骤以下: (1) 打开vs.net,新建项目(asp.net web服务),在位置中键入http://localhost/webserver,其中webserver就是项目的名字。单击肯定按钮,建立项目。 (2) 打开Service1.asmx.cx文件以下: using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace webserver { ///<summary> ///Service1的摘要说明。 ///</summary> //(1) public class Service1:System.Web.Services.WebService { public Service1() { //CODEGEN:该调用是ASP.NET Web服务设计器所必需的 InitializeComponent(); } #region Component Designer generated code //Web服务设计器所必需的 private IContainer components = null; /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { } ///<summary> ///清理全部正在使用的资源。 ///</summary> protected override void Dispose(bool disposing) { if(disposing&&components!=null) { components.Dispose(); } base.Dispose(disposing); } #endregion //WEB服务示例 //HelloWorld()示例服务返回字符串Hello World //若要生成,请取消注释下列行,而后保存并生成项目 //若要测试此Web服务,请按F5键 //[WebMethod] //public string HelloWorld() //{ // return "Hello World"; //} } } (3) 下面在//(1)处加入[WebService(Namespace="http://localhost/webserver/")],这是由于SOAP是基于http协议上的,客户端没法知道webservice位于那个服务器上。在实际应用中,好比http://www.ourfly.com上放置这个webservice,则Namespace改成http://www.ourfly.com/webserver。 (4) 下面给这个webservice添加一个方法。微软帮咱们写好了一个以下,以被注解掉。 //[WebMethod] //public string HelloWorld() //{ //return "Hello World"; //} 添加一个本身的方法。方法名称叫show [WebMethod] public string show(string yourname) { return “http://www.ourfly.com”+”欢迎”+yourname; } (5) 如今能够测试这个Web服务,按F5运行,点击show,输入你的名字,而后点击invote按钮,在浏览器上显示以下内容,这是用XML标记表示的调用Web服务方法Show返回的结果。 <?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">http://www.ourfly.com欢迎yyg</string> (6) 打开bin目录,Vs.net已经将proxy作好了.webserver.dll。 (7) 请注意,这里运行只是一种测试,实际上应在其它计算机上生成一个调用此Web服务的程序,能够是Windows应用程序,也能够是控制台程序,或者是ASP.Net程序,便可以是Micosoft.Net系统程序,也能够是其它系统程序,例如Java程序,Linux程序等等,下节将介绍这方面的知识。 13.2.3 服务描述语言(WSDL) WSDL(Web Services Description Language)中文名称为Web服务描述语言。 Web服务提供了一种服务,容许Internet上的计算机使用http和SOAP协议远程调用Web服务方法。你们都知道,为了使用一个函数,首先要看一下函数的使用说明。Web服务方法也存在一样的问题,特别是SOAP协议,它采用XML标记语言描述Web服务中传递的消息,而XML标记语言是能够定义本身的标记的,但SOAP并无提供一种通用的XML标记供Web服务使用,不一样的Web服务中SOAP的XML标记定义可能不一样。所以,为了使不一样系统调用其它系统中的Web服务,必须对调用Web服务的方法及Web服务返回的数据的格式作详细说明即服务描述,并且这种描述也应采用被普遍接受的协议。 WSDL就是Web服务描述语言。WSDL是基于XML的,用WSDL生成一个XML文档,能够提供关于Web服务的操做信息,例如,抽象形式的服务接口信息、数据传输的具体访问协议和格式、供客户端使用该Web服务的细节等等。服务描述是一个使用WSDL语言的XML语法编写的XML文档,定义了Web服务能理解的Web服务消息格式。服务描述起一个协定的做用,用来定义一个Web服务的行为而且指示潜在的客户如何与之交互。因为在micosoft.Net中提供了一些工具,能够自动生成WSDL文档,这里就不介绍WSDL了,能够经过下边方法看到micosoft.Net自动生成的WSDL文档,例如查看上节生成的Web服务,在浏览器中URL地址中输入http://localhost/MyAdd.asmx?WSDL,浏览器中显示该Web服务WSDL文档。 13.3 基于.Net的Web服务客户端程序 Web服务客户端程序是用来调用服务器端的Web服务方法,前边使用浏览器调用Web服务方法,只能算作一种测试,经过这种测试,能够验证Web服务方法的正确性,发现错误。做为客户端程序,不管在何处,采用那种操做系统,但愿只要知道Web服务的所在网址,就能够调用其相关Web服务方法。Web服务客户端程序通常应在Web网上的另外一台计算机中,单作实验或学习,也能够和Web服务在同一台计算机中。本节介绍如何实现基于.Net的Web服务客户端程序。 13.3.1 Web服务客户端程序代理类 Web服务客户端程序是用http和SOAP协议来调用远程的Web服务方法,所以,Web服务客户端程序必须把程序的调用及其参数转化为SOAP协议,传送到Web服务。但这个工做比较繁琐,程序员但愿采用象普通编程语言调用一个方法那样调用Web方法。.NET Framework的SDK提供了一个程序WSDL.EXE,能够自动为Web服务客户端程序生成一个代理程序,该代理程序的功能是,Web服务客户端程序用通常程序语言那样调用Web服务方法,代理程序负责转换为SOAP协议,发送到Web服务方法,由代理程序负责得到Web服务方法返回的数据,因为这些数据也用SOPA协议表示,也要由代理程序转换为转换为通常程序语言可以理解的形式,传送给Web服务客户端程序。下边介绍生成代理程序的具体方法。WSDL.EXE必须在控制台界面下使用,使用的格式以下: WSDL /l:C# /OUT:Hello.cs /protocol:soap http://LocalHost/Hello.asmx?WSDL 其中,/l参数指定编制Web服务客户端程序使用的语言,能够是vb、C#和Jscript,默认值为C#;/OUT参数指定生成的代理类文件的路径和文件名,默认值和Web服务ASMX文件同名,扩展名为参数/l指定的语言的扩展名;参数/protocol指定调用Web服务方法使用的协议,能够是HTTP-GET、HTTP-POST和SOAP协议;http://后边是Web服务ASMX文件的URL。WSDL运行的结果是生成一个Web服务客户端程序代理类的源程序。有了源程序,还要编译源程序生成dll文件,格式以下:csc /t:librrary hello.cs。把生成的hello.dll文件存到Web服务客户端程序项目所在目录的子目录bin下,这个代理类就能够被项目的其它成代码使用了。 13.3.2 HTTP-GET、HTTP-POST和SOAP协议 当构造一个XML Web服务时,它自动支持客户端使用SOAP、HTTP-GET和HTTP-POST协议通信。HTTP-GET和HTTP-POST支持使用URL编码的变量名/变量值对来传送消息,支持这两个协议的数据类型没有支持SOAP协议的数据类型丰富。SOAP是一个简单的、重量轻的基于XML的协议,用于交换Web上的结构化的和模式化的信息。SOAP的整体设计目标是使它保持尽量的简单,而且提供最少的功能。这个协议定义了一个不包含应用程序或传输语义的消息框架。所以,这个协议是模块化的而且很是利于扩展。在SOAP中,使用XML把数据传送到XML Web服务或从XML Web服务取回消息,你能够使用支持丰富的数据类型集。 更多SOAP规格的信息,请看W3C Web站点(http://www.w3.org/TR/soap)。 13.3.3 使用代理类的Web服务客户端程序 (1) 控制台应用程序 using System; class Welcome; { static void Main() { string s; int x,y,z; Console.WriteLine("Please enter first number:"); s=Console.ReadLine(); x=Convert.ToInt(s); Console.WriteLine("Please enter second number:"); s=Console.ReadLine(); y=Convert.ToInt(s); Hollo h1=new Hollo();//代理类对象 z=h1.hello(x,y);//调用Web服务方法 Console.WriteLine("sum:={0}",z); } } (2) Windows应用程序 (3) ASP.Net应用程序 13.3.4 Visual Studio.Net创建Web服务客户端程序 使用Visual Studio.Net很容易创建Web服务客户端程序,这个客户端程序没必要必定和Web服务在同一台计算机中,能够在任意一台Internet网中的计算机中。下边是具体步骤: (1) 打开Visual Studio.Net,新建windows应用程序项目,命名为AddServiceClient,在窗体中增长一个按钮用来调用Web服务的Web方法,三个文本框,两个用来输入两个加数,另外一个用来显示调用Web服务的Web方法后返回的结果。 (2) 创建Web服务客户端程序通常要创建一个代理。选择菜单项”项目”|/”添加Web引用”,在弹出的对话框中的地址栏中输入Web服务的URL,例如Web服务所在的计算机的IP地址是202.206.96.20,Web服务的文件Service1.asmx在网站宿主目录下的子目录webserver中,地址为:http://202.206.96.20/webserver/Service1.asmx。按回车键,出现添加Web引用对话框,如图:单击添加引用按钮,在解决方案资源管理器中,能够看到一个新的引用,以及从Web服务端发到客户端的DISCO和WSDL文档。在解决方案资源管理器中,还能够看到新建立的类,这个类就是Web服务客户端程序的代理程序,该类的用途是把Web服务客户端程序调用Web服务方法转换为SOAP格式。 (3) 为按钮增长事件函数以下: (4) 天出的对话框中再加入一个system.web.webservices的引用,在列表中有。在form1.cs里,加入 using System.Web.Services; using webserver; 而后在 private System.Windows.Forms.Button button1; private System.Windows.Forms.TextBox textBox1; 后面,插入 private webserver.service1 Client 创建一个service1的实例。双击按钮,代码以下: private void button1_Click(object sender, System.EventArgs e) { Client =new Service1(); string name; name=Client.show("龙卷风.NET"); textBox1.Text=name; } 按F5,运行项目,点击按钮,文本框中显示 http://www.ourfly.com欢迎龙卷风.NET 2. Asp.NET web窗口的测试 方法与上面的如出一辙,添加引用,创建service1的实例 在此不在细说。 3.在VB中测试 这个就要相对来讲复杂一些 首先在vb中创建一个”标准EXE”的项目。添加引用:Microsoft Soap Type library。注意:若是没有安装Microsoft Soap Toolkit,是没有这个类型库的。 能够在http://www.ourfly.com中下载。 添加一个text Private Sub Form_Load() Text1.Text = add() End Sub Public Function Add() As String Dim objSoapClient As New SoapClient objSoapClient.ClientProperty("ServerHTTPRequest") = True Call objSoapClient.mssoapinit("http://localhost/webserver/service1.asmx?WSDL", "Service1", "Service1Soap") 这句也能够 objSoapClient.mssoapinit("http://localhost/webserver/service1.asmx?WSDL") Add = objSoapClient.Show("龙卷风.NET") End Function 13.4 创建Web服务客户端程序通常方法 13.5 发布和发现Web服务 完成Web服务开发,如何发布该Web服务,通知客户使用,程序开发者如何发现并定位所需功能的Web服务,是这节要解决的问题。 13.5.1 Web服务目录 和使用因特网上任何其余的资源同样,若是没有某些查找方法的话,是不可可以找到一个特定的Web服务的。Web服务目录提供了一个网址,例如:http://uddi.microsoft.org/,可让Web服务供应者在其上发布他们提供的Web服务的信息。这样的目录甚至能够是Web服务自己,能够编程访问而且提供搜索结果来响应Web服务客户端的查询。使用一个Web服务目录来定位一个提供Web服务的URL,这是很是必要的。 UDDI(统一描述发现和集成规范)规格定义了一个标准方法来发布和发现Web服务的信息,也就是经过UDDI发现指定Web服务的服务描述,该描述是一个使用WSDL语言的XML语法编写的XML文档。与UDDI关联的XML模式定义了四个信息类型,能让开发者使用一个发布的Web服务。这些是:商业信息、服务信息、绑定信息和其余用于服务的规范的信息。 做为UDDI项目的核心控件,UDDI Business Registry(业务登记)容许Web服务开发者发布其Web服务的信息。Web服务使用者能够使用UDDI Business Registry来定位发现Web服务描述文件。更多信息,请看UDDI Web站点(http://uddi.microsoft.com)。 13.5.2 Web服务发现 程序设计者能够经过如下步骤发现所需的Web服务: (1) 首先,访问Web服务目录网址,例如http://uddi.microsoft.org/,查找所需Web服务,将返回一个所需Web服务URL。 (2) 按照返回URL,访问这个网址,例如:http:// 返回Web服务URL/default.discro。.disco文件,是包含链接到其余描述XML Web服务的资源的XML文件,可以编程发现一个XML Web服务。disco是一个包含与其它发现文档、XSD模式和服务描述链接的XML文档。换句话说,使用ASP.NET建立的XML Web服务自动地有提供一个产生发现文档的能力。 (3) 使用Web服务的WSDL创建一个Web服务客户端程序代理类。 (4) 创建Web服务客户端程序,使用代理类访问Web服务方法。 Web服务发现是使用Web服务描述语言WSDL定位或发现一个或多个描述特定的XML Web服务的文件的操做。它让XML Web服务客户端得知一个XML Web服务是否存在而且到哪里找到这个XML Web服务的描述文件。 一个发布的.disco文件,是包含链接到其余描述XML Web服务的资源的XML文件,可以编程发现一个XML Web服务。(脚本之家)