Windows编程
2.1 窗口
Windows应用程序一般都有一个窗口,窗口是运行程序与外界交换信息的界面。一个典型的窗口包括标题栏、最小化按钮、最大/还原按钮、关闭按钮、系统菜单图标、菜单、工具条、状态栏、滚动条、客户区等。程序员的工作之一是设计符合自己要求的窗口,C#用控件的方法设计界面。编程另一个工作是在用户区显示数据和图形。
2.2 Windows的事件系统
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)是Windows98、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编程接口等等。
Windows98、2000和XP操作系统并不包含.Net框架类库,为了运行C#程序,必须安装.Net FrameWork。.Net FrameWork目前有1.0版本和1.1版本。
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。启动Windows操作系统命令行提示符,在屏幕上输入一行命令:d:回车,cd Charp回车,键入命令:
csc.exe所在的目录\csc /t:winexe /r:system.dll,System.Windows.Forms.dll e1.cs
命令中的/t:winexe表示要建立一个Windows应用程序,/r表示要引入的命名空间。也可以用记事本建立一个批处理文件g.bat,将以上命令内容拷贝到文件中,运行g.bat,和在命令行提示符键入命令效果相同。以上方法在FrameWork SDK 1.0中实现。如果一切正常e1.cs文件将被编译,编译后生成可执行文件e1.exe。运行可执行文件e1.exe,屏幕上出现一个窗口如右图。
可以在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) 单击"新建项目(J)"按钮,出现如图1.2.2B对话框。在"项目类型(P) "列表框中选择"Visual C#项目",在"模板(T) "列表框中选择"Windows应用程序",在"名称(N)"编辑框中键入e2_4_2,在"位置(L)"编辑框中键入D:\csarp。也可以单击"浏览"按钮,在打开文件对话框中选择文件夹。单击"确定"按钮,创建项目。出现如图2.4.2A界面。生成一个空白窗体(Form1)。
图2.4.2A
(3) 在e2_4_2文件夹中下有两个文件夹和8个文件,一般只修改Form1.cs文件。右击Form1窗体,在快捷菜单中选择菜单项"查看代码(C)",可打开Form1.cs文件。Visual Studio.Net生成的Form1.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_4_2//定义命名空间,///为解释
{ //此处可定义其它类
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form//Form1类定义
{ //此处可定义自己的变量,这些变量和运行程序生命周期相同
/// <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[设计]"的窗口。向Form1窗体中添加控件需要使用工具箱窗口,若看不到,可以用"视图"|"工具箱"菜单项打开这个窗口(见图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
完成以上设计后,集成环境生成的Form1.cs文件如下,背景为黑色的代码是新增代码。
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace e2_4_2
{
/// <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)。它可以为我们显示一个方案的树形结构,以及它所包含的项目及项目中的文件。这里的项目是Windows应用程序项目,和后边的Web项目有些区别。
一个项目一般要放在一个文件夹中,例如上边的例子,项目e2_4_2的所有文件都在文件夹e2_4_2中,共有两个文件夹和7个文件,它们的用途如下:
l bin文件夹:其中包含Debug子文件夹,存储生成带调试信息的可执行C#程序。还可以包含Release子文件夹,存储生成不带调试信息的可执行C#程序。不用打开Visual Studio.Net,双击可执行C#程序,就可运行这个程序。
l obj文件夹:包含编译过程中生成的中间代码。
l AssemblyInfo.cs:创建项目自动添加。包含各种属性设置,例如,项目最终创建的可执行文件或DLL文件中的信息,如标题、描述、公司名等。一般用工具修改该程序,不要直接修改。
l Form1.cs:窗体文件,程序员一般只修改该文件。
l Form1.resx:资源文件。程序员用集成环境提供的工具修改,不要直接修改。
l e2_4_2.suo:解决方案用户选项文件,记录用户关于解决方案的选项。
l e2_4_2.csproj:项目文件,记录用户关于项目的选项。
l e2_4_2.sln:解决方案文件。
为了以后重新用Visual Studio.Net打开该解决方案,必须保存除了两个文件夹以外的所有文件,实际上,由于文件夹e2_4_2不太大,可以保存整个e2_4_2文件夹。如果重新开始一个解决方案,首先用"文件"|"关闭解决方案"菜单项,关闭当前项目,再新建一个项目。为了用Visual Studio.Net修改以前的程序,必须打开保存的项目文件(扩展名为csproj),或者使用"文件"|"打开项目"菜单项,打开保存的项目,同时打开项目所在的解决方案。另外不同版本的Visual Studio.Net的项目文件和解决方案文件可能不兼容。