《分享(C#高级编程第6版doc)第31章 Windows 窗体.doc》由会员分享,可在线阅读,更多相关《分享(C#高级编程第6版doc)第31章 Windows 窗体.doc(47页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第31章Windows 窗 体基于Web的应用程序在过去几年非常流行。从管理员的角度来看,把所有的应用程序逻辑放在一个中央服务器上是非常吸引人的。但部署基于客户的软件会非常困难,特别是部署基于COM的客户软件。基于Web的应用程序的缺点是它们不能提供丰富的用户体验。.NET Framework允许开发人员创建丰富、智能的客户应用程序,而且不再有部署问题和以前的DLL Hell。无论选择Windows窗体还是WPF(参见第34章),客户应用程序都不再难以开发或部署。Windows窗体已经对Windows开发产生了影响。当应用程序处于初始设计阶段时,是建立基于Web的应用程序还是建立客户应用程序已
2、经很难抉择了。Windows客户应用程序开发起来非常快速和高效,它们可以为用户提供丰富的体验。Visual Basic开发人员对Windows窗体应比较熟悉。创建新窗体(也称为窗口或对话框)也采用把控件从工具箱拖放到窗体设计器上的方式。但是,如果您在创建消息泵和监视消息时使用的是C样式的传统Windows编程,或者您是一位MFC程序员,就会发现现在可以获得需要的低级内部功能了。现在可以重写wndproc,捕获这些消息,但常常并不是真需要它们。本章将主要介绍Windows窗体的如下方面:Form类Windows窗体的类层次结构System.Windows.Forms命名空间中的控件和组件菜单和工
3、具栏创建控件创建用户控件31.1 创建Windows窗体应用程序首先需要创建一个Windows窗体应用程序。下面的示例创建了一个空白窗体,并把它显示在屏幕上。这个示例没有使用Visual Studio 2008,而是在文本编辑器中输入代码,使用命令行编译器进行编译。下面是代码清单:using System;using System.Windows.Forms;namespace NotepadFormspublic calss MyForm : System.Windows.Forms.Formpublic MyForm() STAThreadstatic void Main()Applica
4、tion.Run(new MyForm();在编译和运行这个示例时,会得到一个没有标题的小空白窗体。该窗体没有什么实际功能,但它却是一个Windows窗体。代码中有两个地方需要注意。第一个是使用继承功能来创建MyForm类。下面的代码声明MyForm派生于System.Windows.Forms。public calss MyForm : System.Windows.Forms.FormForm类是System.Windows.Forms命名空间的一个主要类。代码的其他部分如下: STAThreadstatic void Main()Application.Run(new MyForm();
5、Main是C#客户应用程序的默认入口。一般在大型应用程序中,Main()方法不位于窗体中,而是位于类中,它负责完成需要的启动处理。在本例中,我们在项目属性对话框中设置启动的类名。注意属性STAThread,它把COM线程模型设置为单线程单元(Single-Threaded Apartment, STA)。COM交互操作需要STA线程模型,默认为添加到Windows窗体项目中。Application.Run()方法负责启动标准的应用程序消息循环。它有3个重载版本:第一个重载版本不带参数,第二个重载版本把ApplicationContext对象作为其参数,本例中的第三个重载版本把窗体对象作为其参数
6、。在这个示例中,MyForm对象是应用程序的主窗体,这表示在关闭这个窗体时,应用程序就结束了。使用ApplicationContext类,可以对主消息循环何时结束和应用程序何时退出有更多的控制权。Application类包含一些非常有用的功能。它提供了一些静态方法和属性,用于控制应用程序的启动和停止过程,访问由应用程序处理的Windows消息。表31-1列出了其中一些比较有用的方法和属性。表 31-1方法/属性说 明CommonAppDataPath对应用程序的所有用户都通用的数据路径。一般是BasePathCompany NameProduct NameVersion,其中BasePath是
7、C:Documents and Settings usernameApplicationData。如果该路径不存在,就创建一个ExecutablePath这是启动应用程序的可执行文件的路径和文件名(续表) 方法/属性说 明LocalUserAppDataPath类似于CommonAppDataPath,但这个属性支持漫游MessageLoop如果在当前线程上存在消息循环,就返回True,否则返回falseStartupPath类似于ExecutablePath,但不返回文件名AddMessageFilter用于预处理消息。在基于IMessagerFilter的对象上执行,消息可以从消息循环中过
8、滤出来,或者在消息发送到循环中之前进行特殊的处理DoEvents类似于Visual Basic的DoEvents语句,允许处理队列中的消息EnableVisualStyles允许对应用程序的各种可视化元素使用XP可视化样式。它有两个重载版本,接收清单信息。一个重载版本的参数是清单流,另一个重载版本的参数是清单所在的完整名称和路径Exit和ExitThreadExit结束所有当前运行的消息循环,并退出应用程序。ExitThread只结束消息循环,关闭当前线程上的所有窗口在Visual Studio 2008中生成这个示例时,它会是什么样子?首先要注意,创建了两个文件,其原因是Visual Stu
9、dio 2008利用了Framework的部分类特性,把设计器生成的代码放在一个独立的文件中。使用默认名称Form1,这两个文件就是Form1.cs和Form1.Designer.cs。除非选择了Project菜单中的Show All Files选项,否则在Solution Explorer中看不到Form1.Designer.cs。下面是Visual Studio 2008为两个文件生成的代码。第一个文件是Form1.cs:using System;using System.Collections.Generic;using System.ComponentModel;using Syste
10、m.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;namespace VisualStudioFormpublic partial class Form1 : Formpublic Form1()InitializeComponent();这很简单,其中包含一些using语句和一个简单的构造函数。接着是Form1.Designer.cs的代码:namespace VisualStudioFormpartial class Form1/ / Required des
11、igner variable./ private System.ComponentModel.IContainer components = null;/ / Clean up any resources being used./ / true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing)if (disposing & & (components != null)components.Dispose();base.Dispose
12、(disposing);#region Windows Form Designer generated code/ / Required method for Designer support - do not modify/ the contents of this method with the code editor./ private void InitializeComponent()ponents = new System.ComponentModel.Container();this.AutoScaleMode = System.Windows.Forms.AutoScaleMo
13、de.Font;this.Text = Form1;#endregion窗体的设计器文件一般不应直接编辑。唯一的例外是要在Dispose方法中进行特殊的处理。InitializeComponent方法详见本章后面的内容。首先,这个示例应用程序的代码比简单的命令行示例长。在类的开头有好几个using语句,在本例中大多数是不必要的。但保留它们并无大碍。类Form1派生于System. Windows.Forms,与前面的Notepad示例一样,但代码从一开始就不同。Form1.Designer文件的第一行代码如下:private System.ComponentModel.Container co
14、mponents = null;在这个示例中,这行代码并没有做什么工作。当给窗体添加组件时,也就把该组件添加给了组件对象,该组件对象是一个容器。添加到这个容器中的原因与窗体的释放有关。窗体类支持IDisposable接口,因为它是在Component类中执行的。在组件添加到组件容器中时,容器将确保组件被正确地跟踪,并在释放窗体时释放它。如果查看代码中的Dispose方法,就可以看到它: protected override void Dispose(bool disposing)if (disposing & (components != null)components.Dispose();b
15、ase.Dispose(disposing); 在此可以看到,在调用Dispose方法时,也会调用组件对象的Dispose方法,因为组件对象包含其他组件,所以它们也会被释放。Form1类的构造函数在Form1.cs中,如下所示: public Form1()InitializeComponent();注意对InitializeComponent()的调用。InitializeComponent()在Form1.Designer.cs中,顾名思义,InitializeComponent()初始化了添加到窗体上的所有控件,还初始化了窗体的属性。在本示例中,InitializeComponent()
16、如下所示: private void InitializeComponent()ponents = new System.ComponentModel.Container();this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.Text = Form1;这是很基本的初始化代码。该方法与Visual Studio的设计器相关联。使用设计器修改窗体时,这些改动会在InitializeComponent()中反映出来。如果在InitializeComponent()中修改了任意类型的代码,下次在设计器中进行修改时,这些改
17、动就会丢失。每次在设计器中进行修改后,InitializeComponent()都会重新生成。如果需要为窗体或窗体上的控件和组件添加其他初始化代码,就应在调用InitializeComponent()后添加。InitializeComponent()还负责实例化控件,这样在InitializeComponent()之前所有引用控件的调用都会失败,并生成一个空引用异常。要在窗体上添加控件或组件,可以按下Ctrl+Alt+X或者在Visual Studio 2008的View菜单中选择Toolbox。此时Form1应处于设计模式。在Solution Explorer中右击Form1.cs,从弹出的
18、菜单中选择View Designer。选择Button控件,把它拖放到设计器的窗体上。也可以双击该控件,把它添加到窗体上。对TextBox控件进行相同的操作。在窗体上添加了TextBox控件和Button控件后,InitializeComponent()就会扩展,包含如下代码:private void InitializeComponent()this.button1 = new System.Windows.Forms.Button();this.textBox1 = new System.Windows.Forms.TextBox();this.SuspendLayout();/ butt
19、on1/this.button1.Location = new System.Drawing.Point(77, 137);this.button1.Name = button1;this.button1.Size = new System.Drawing.Size(75, 23);this.button1.TabIndex = 0;this.button1.Text = button1;this.button1.UseVisualStyleBackColor = true;/ textBox1/this.textBox1.Location = new System.Drawing.Point
20、(67, 75);this.textBox1.Name = textBox1;this.textBox1.Size = new System.Drawing.Size(100, 20);this.textBox1.TabIndex = 1;/ Form1/this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(284, 264
21、);this.Controls.Add(this.textBox1);this.Controls.Add(this.button1);this.Name = Form1;this.Text = Form1;this.ResumeLayout(false);this.PerformLayout();如果查看该方法中的前3行代码,就会看到TextBox控件和Button控件被实例化了。注意给控件指定的名称textBox1和button1。默认情况下,设计器会使用控件的名称,并在该名称的最后添加一个整数值。在添加另一个按钮时,设计器会使用名称button2,依次类推。下一行代码是SuspendLay
22、out和ResumeLayout对的部分。SuspendLayout()临时挂起控件第一次初始化时发生的布局事件。在该方法的最后,将调用ResumeLayout()方法,把事件重置为正常状态。在包含许多控件的复杂窗体上,InitializeComponent()方法会非常长。要修改控件的属性值,可以按下F4,或从View菜单中选择Properties Window。该窗口允许修改控件或组件的大多数属性。在Properties窗口中进行了修改后,Initialize Component()方法就会重新编写,以反映新属性值。例如,如果在Properties窗口中把Text属性改为My Button
23、,InitializeComponent()就会包含如下代码:/ button1/this.button1.Location = new System.Drawing.Point(77, 137);this.button1.Name = button1;this.button1.Size = new System.Drawing.Size(75, 23);this.button1.TabIndex = 0;this.button1.Text = My Button;this.button1.UseVisualStyleBackColor = true;如果使用的不是Visual Studio的
24、编辑器,就需要在设计中包含InitializeComponent()类型函数。把所有这些初始化代码放在一个地方,有助于使构造函数更简洁,如果有多个构造函数,还需要确保能从每个构造函数中调用初始化代码。类层次结构在设计和构建定制控件时,理解层次结构是非常重要的。如果定制控件派生于已有的控件,例如对于带有额外属性和方法的文本框,就应使定制控件派生于文本框控件,再重写、添加属性和方法,以满足要求。但是,如果创建的控件与.NET Framework中的已有控件不匹配,就必须从3个基类中派生:如果需要自动滚动功能,就从Control或Scrollable Control中派生,如果控件应是其他控件的容器
25、,就应从ContainerControl类中派生。本章的剩余内容将介绍其中的许多类,它们如何协同工作,以及如何使用它们建立具有专业化外观的客户应用程序。31.2 Control类System.Windows.Forms命名空间中有一个特殊的类,它是每个控件和窗体的基类,这个类就是System.Windows.Forms.Control。Control类执行核心功能,创建用户所见的界面。Control类派生于System.ComponentModel.Component类。Component类为Control类提供了必要的基础结构,在把控件拖放到设计界面上以及包含在另一个对象中时需要它。Cont
26、rol类为派生于它的类提供了一个很长的功能列表。这个列表太长,不能在这里全部列出,所以我们仅介绍Control类提供的比较重要的功能。本章的后面在介绍基于Control类的特定控件时,将在一些示例代码中论述属性和方法。下面几小节将按照功能组合方法和属性,把相关的功能放在一起进行讨论。31.2.1 大小和位置控件的大小和位置由属性Height、Width、Top、Bottom、Left、Right以及辅助属性Size和Location确定。区别是Height、Width、Top、Bottom、Left、Right属性值都是一个整数,而Size的值使用一个Size结构来表示,Location的值使
27、用一个Point结构来表示。Size结构和Point结构都包含XY坐标。Point结构一般相对于一个位置,而Size结构是对象的高和宽。Size和Point都位于System.Drawing命名空间。它们非常类似,因为它们都提供了XY坐标对,还拥有用于简单的比较和转换的重写运算符。例如,可以对两个Size结构执行相加操作。对于Point结构,加法运算符已进行了重写,可以把Size结构加到Point结构上,得到一个新的Point。其结果是给某个位置加上某个距离值,得到一个新位置。如果动态创建窗体或控件,这是非常方便的。Bounds属性返回一个Rectangle对象,它表示一个控件区域。这个区域包
28、含滚动条和标题栏。Rectangle也位于System.Drawing命名空间。ClientSize属性是一个Size结构,表示控件的客户区域,不包含滚动条和标题栏。PointToClient和PointToScreen方法是方便的转换方法,它们的参数是Point结构,返回一个Point结构。PointToClient的Point参数表示屏幕坐标,该方法把屏幕坐标转换为基于当前客户对象的坐标。这非常便于进行拖放操作。PointToScreen正好与之相反,它提取客户对象的坐标,把它们转换为屏幕坐标。还有RectangleToScreen和ScreenToRectangle方法,它们具有相同的功
29、能,只是用Rectangle结构代替Point结构。Dock属性确定子控件停放在父控件的哪条边上。DockStyle枚举值用作其属性值。这个值可以是Top、Bottom、Right、Left、Fill和None。Fill会使控件的大小正好匹配父控件的客户区域。Anchor属性把子控件的一条边与父控件的一条边对齐,这与停靠不同,因为它不设置父控件的一条边,而是把到该边界的当前距离设置为常量。例如,如果把子控件的右边界与父控件的右边界对齐,并重新设置父控件的大小,子控件右边界到父控件右边界的距离将保持不变。Anchor属性采用AnchorStyles枚举的值,其值是Top、Bottom、Right
30、、Left和None。通过设置该属性值,可以在重新设置父控件的大小时,动态地设置子控件的大小。这样,当用户重新设置窗体的大小时,按钮和文本框就不会被剪切或隐藏。Dock和Anchor属性与Flow和Table布局控件(详见本章后面的内容)一起使用时,可以创建非常复杂的用户窗口。对于包含许多控件的复杂窗体来说,窗口大小的重新设置比较困难。这些工具有助于完成这个任务。31.2.2 外观与控件外观相关的属性有BackColor和ForeColor,它们把System.Drawing.Color对象作为其值。BackGroundImage属性把基于Image的对象作为其值。System.Drawing
31、.Image是一个抽象类,用作Bitmap和Metafile类的基类。BackColorImageLayout属性使用ImageLayout枚举设置图像在控件上的显示方式,其有效值是Center、Tile、Stretch、Zoom和None。Font和Text属性处理文字的显示。要修改Font属性,需要创建一个Font对象。在创建Font对象时,要指定字体名称、字号和样式。31.2.3 用户交互操作用户交互操作最好描述为控件创建和响应的各种事件。一些比较常见的事件有Click、DoubleClick、KeyDown、KeyPress、Validating和Paint。鼠标事件Click、Dou
32、bleClick、MouseDown、MouseUp、MouseEnter、MouseLeave和MouseHover处理鼠标和控件的交互操作。如果处理Click和DoubleClick事件,每次捕获一个DoubleClick事件时,也会引发Click事件。如果处理不正确,就会出现我们不希望的结果。Click和DoubleClick事件都把EventArgs作为其参数,而MouseDown和MouseUp事件把MouseEventArgs作为其参数。MouseEventArgs包含几个有用的信息,例如单击的按钮、按钮被单击的次数、鼠标轮制动器(鼠标轮上的凹槽)的数目和鼠标的当前XY坐标。如果可
33、以访问这些信息,就必须处理MouseDown或MouseUp事件,而不是Click或DoubleClick事件。键盘事件的工作方式与此类似:需要一些信息来确定处理什么事件。对于简单的情况,KeyPress事件接收一个KeyPressEventArgs,它包含表示被按键的字符值KeyChar。Handled属性用于确定事件是否已处理。把Handled属性设置为true,事件就不会由操作系统进行默认处理。如果需要被按的键的更多信息,则处理KeyDown或KeyUp事件会比较合适。它们都接收KeyEventArgs。KeyEventArgs中的属性包括Ctrl、Alt或Shift键是否被按下。Key
34、Code属性返回一个Keys枚举值,表示被按下的键。与KeyPressEventArgs.KeyChar不同,KeyCode属性指定键盘上的每个键,而不仅仅是字母数字键。KeyData属性返回一个Key值,还设置修饰符。修饰符与值进行OR运算,指定是否同时按下了Shift或Ctrl键。KeyValue属性是Keys枚举的整数值。Modifiers属性包含一个Keys值,它表示被按下的修饰符键。如果选择了多个修饰符键,这些值就进行OR运算。键盘事件以下述顺序来引发:(1) KeyDown(2) KeyPress(3) KeyUpValidating、Validated、Enter、Leave、G
35、otFocus和LostFocus事件都处理获得焦点(或被激活)和失去焦点的控件。在用户用tab键选择一个控件或用鼠标选择该控件时,该控件就获得了焦点。Enter、Leave、GotFocus和LostFocus事件的功能似乎非常类似。GotFocus和LostFocus事件是低级事件,与Windows消息WM_SETFOCUS和WM_KILLFOCUS相关。一般应尽可能使用Enter和Leave事件。Validating和Validated事件在验证控件时发生。这些事件接收一个CancelEventArgs,利用该参数,把Cancel属性设置为true,就可以取消以后的事件。如果定制了验证代
36、码,而且验证失败,就可以把Cancel属性设置为true,且控件也不会失去焦点。Validating事件在验证过程中发生,Validated事件在验证过程后发生。这些事件的引发顺序如下:(1) Enter(2) GotFocus(3) Leave(4) Validating(5) Validated(6) LostFocus理解这些事件的引发顺序是很重要的,可以避免不小心创建递归事件。例如,在控件的LostFocus事件中设置控件的焦点,就会创建一个消息死锁,且应用程序会停止响应。31.2.4 Windows功能System.Windows.Forms命名空间是依赖Windows功能的少数几个
37、命名空间之一。Control类就是一个很好的示例。如果对System.Windows.Forms.dll进行反编译,就会看到UnsafeNativeMethods类的引用列表。.NET Framework使用这个类封装所有的标准Win32 API调用。使用与Win32 API的交互操作,标准Windows应用程序的外观和操作方式就可以通过System.Windows. Forms命名空间获得。支持与Windows交互操作的功能包括Handle和IsHandleCreated属性。Handle属性返回一个包含控件HWND(Windows句柄)的IntPtr。窗口句柄是唯一标识窗口的HWND。可以
38、将控件看作是一个窗口,所以它有相应的HWND。可以使用Handle属性进行任意数量的Win32 API调用。为了访问Windows消息,可以重写WndProc方法。该方法把一个Message对象作为其参数。Message对象是Windows消息的一个简单封装器。它包含HWnd、LParam、WParam、Msg和Result属性。如果希望由系统处理消息,就必须确保把消息传送给base.WndProc(msg)方法。如果希望自己处理消息,就不需要传送消息。31.2.5 杂项功能一些条目较难分类,例如数据绑定功能。BindingContext属性返回一个BindingManager Base对象。
39、DataBindings集合包含一个ControlBindingsCollection,它是控件的绑定对象集合,数据绑定详见第32章。CompanyName、ProductName和Product版本提供了控件的初始数据及其当前版本。Invalidate方法允许使控件的一个区域失效,以进行重新绘制。可以使整个控件失效,或指定要失效的区域或矩形。这会把一个绘制消息传送给控件的WindProc。还可以同时使子控件失效。组成Control类的还有几十个属性、方法和事件。这个列表列出了比较常用的成员,希望您对可用的功能有一个大致的了解。31.3 标准控件和组件前一节介绍了控件常用的一些方法和属性。本节
40、将讨论.NET Framework提供的各种控件,解释每个控件提供的附加功能。下载的示例()包含一个示例应用程序FormExample,这个应用程序是一个MDI应用程序(稍后讨论),包含一个窗体frmControls,其中包含许多具备基本功能的控件。图31-1显示了这个示例的外观。图 31-1 31.3.1 Button控件Button类表示简单的命令按钮,派生自ButtonBase类。该类最常见的用法是编写处理按钮Click事件的代码。下面的代码执行Click事件的处理程序。在单击按钮时,会弹出一个显示按钮名称的消息框。private void btnTest_Click(object se
41、nder, System.EventArgs e)MessageBox.Show(Button)sender).Name + was clicked.);在PerformClick方法中,可以模仿按钮上的Click事件,而无需用户单击按钮。NotifyDefault方法把一个布尔值作为其参数,告诉按钮把它自己绘制为默认按钮。一般情况下,窗体上的默认按钮有略粗的边框。要把按钮标识为默认,可以把窗体上的AcceptButton属性设置为按钮。接着,在用户按下回车键时,就会引发默认按钮的单击事件。图31-2显示了标题为Default的默认按钮(注意黑色的边框)。按钮可以包含图像和文本。图像通过Ima
42、geList对象或Image属性提供。ImageList对象就是由放在窗体上的组件管理的一个图像列表。它们在本章的后面解释。Text和Image都包含Align属性,用以对齐按钮上的文本和图像。Align属性使用ContentAlignment枚举的值。文本或图像可以与按钮的左、右、上、下边界对齐。31.3.2 CheckBox控件CheckBox控件也派生于ButtonBase,用于接受来自用户的二状态或三状态响应。如果把ThreeState属性设置为true,复选框的CheckState属性就可以是以下3个CheckState枚举值之一: Checked:复选框有一个选中标记 Unchec
43、ked:复选框没有选中标记 Indeterminate:在这种状态下,复选框为灰显Indeterminate值只能在代码中设置,不能由用户设置。如果需要告诉用户选项还未设置,就可以使用这个值。如果希望使用布尔值,还可以使用Checked属性。CheckedChanged和CheckStateChanged事件在CheckState或Checked属性改变时发生。捕获的这些事件可以根据复选框的新状态设置其他值。在frmControls窗体类中,几个复选框的CheckedChanged事件由下面的方法处理:private void checkBoxChanged(object sender, Ev
44、entArgs e)CheckBox checkBox = (CheckBox)sender;MessageBox.Show(checkBox.Name + new value is + checkBox.Checked.ToString(); 在每个复选框的Checked属性改变时,都会显示一个消息框,其中包含了改变的复选框名称和新值。31.3.3 RadioButton控件最后一个派生自ButtonBase的控件是RadioButton(单选按钮)。单选按钮一般用作一个组,有时称为选项按钮。单选按钮允许用户从几个选项中选择一个。当同一个容器中有多个RadioButton控件时,一次只能选择
45、一个按钮。所以如果有3个选项,例如Red、Green和Blue,如果Red选项被选中,而用户单击Blue,则Red会自动取消选中。Appearance属性使用Appearance枚举值,即Button或Normal。当选择Normal时,单选按钮看起来像一个小圆圈,在它的旁边有一个标签。选择按钮会填充圆圈,选择另一个按钮会取消对当前选中按钮的选择,使圆圈为空。当选中Button时,RadioButton控件看起来像一个标准按钮,但工作方式类似于开关,选中是指焦点在位置中,取消选中是指正常状态或焦点在位置外。CheckedAlign属性确定圆圈与标签文本的相对位置,它可以在标签的顶部、左右两边或
46、下方。只要Checked属性的值改变,就会引发CheckedChanged事件。这样就可以根据控件的新值执行其他动作。31.3.4 ComboBox控件、ListBox控件和CheckedListBox控件ComboBox、ListBox和CheckedListBox都派生于ListControl类。这个类提供了一些基本的列表管理功能。使用列表控件最重要的事是,给列表添加数据和选择数据。使用哪个列表一般取决于列表的用法和列表中数据的类型。如果需要选择多个选项,或用户需要在任意时刻查看列表中的几个项,最好使用ListBox和CheckedListBox。如果一次只选择一个选项,就可以使用Comb
47、oBox。在使用列表框之前,必须先添加数据。为此,应给ListBox.ObjectCollection添加对象。这个集合可以使用列表的Items属性访问。由于该集合存储了对象,因此可以把任意有效的.NET类型添加到列表中。要标识对象,需要设置两个重要的属性。第一个是DisplayMember属性,这个设置告诉列表控件,在列表中显示对象的哪个属性。另一个是ValueMember属性,它是要返回值的对象属性。如果在列表中添加了字符串,这两个属性就默认使用字符串值。示例应用程序中的frmLists窗体显示了如何把对象和字符串(也是对象)加载到列表框中。该例子使用Vendor对象作为列表数据。Vendor对象只包含两个属性Name和PhoneNo。DisplayMember属性设置为Name