高压电脑

孰能浊以静而清,孰能安以动而生?

博客园 首页 联系 订阅 管理

Delphi中嵌入的数据库应用开发工具如Database Form Expert具有很强大的功能,我们不需要编写任何程序代码便可以快速地创建一个简单的数据库应用程序,甚至还能创建基于多个数据库表的主要──明细型数据库应用程序。

        本章主要介绍用Delphi开发简单的数据库应用程序的一般方法和步骤,首先让读者对Delphi强劲的数据库应用开发工具有一个直观的印象,然后在此基础上进行复杂的数据库应用程序的设计,本章主要包括以下内容:

● 创建数据库应用窗体

         包括用Database Form Expert 或手工方式创建简单的无需编写程序代码的应用程序或者利用多个部件并编写功能复杂的程序代码创建主要──明细型数据库应用程序。

● 在应用程序中控制字段有关的属性

         描述怎样读写数据库表中字段的值和控制字段的显示格式等。

 

         本章所介绍的例子中用到的窗体、数据库表以及相关的文件都是在安装Delphi时缺省安装在C:\DELPHI\DEMOS\DB\MASTAPP目录中,并且用别名DBDEMOS表示这一子目录。 在本章例子中,除特殊声明外,所有的TTable和 TQuery 部件的 DatabaseName 属性都设置为DBDEMOS。

14.1 简单的基于单表的据库应用 

         用Decphi创建显示一个数据库表中的内容的应用非常简单和方便,只需要三个部件,只要将这三个部件通过相关的属性相互联系起来,不需要编写任何程序代码便可以实现。例如,用户想查看数据库表Customer.DB中的内容时,可以按下面步骤来实现: 

14.1.1 选择相关的部件: 

         选择菜单Project/New开始一个新工程,并修改Form1的Caption属性为CustomerFrom1并把Name属性设置为CustomerForm1,然后从部件选择板上的Data Access 页上选取一个Datasounce部件和一个Table部件放到窗体的左上角,它们是非可见的部件, 在窗体中我们看到的只是部件的图标;从Data Control页上选取DBGrid部件放到窗体中前两个部件的下面。完成这些工作之后,窗体如图14.1所示。  

图在CustomerFrom1窗体中放置三个部件 

14.1.2 设置部件的属性 

为了使TDBGrid部件能够显示数据库表Customer.DB中的客户信息,我们必须修改窗体三个部件相关的属性,这些属性的设置如表14.1所示。 

表14.1 CustomerFrom1窗体中三个部件的属性设置

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

属 性 属 性 值

──────────────────────────────

DataSource1.AutoEdit False

DataSource1.DataSet Table1

Table1.DatabaseName DBDEMOS

Table1.TableName CUSTOMER.DB

Table1.Active True

DBGrid1.DataSource DataSource1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

        这里要注意的是:DBDEMOS是Delphi缺省安装时C:\Delphi\DEMO\DB\MASTAPP目录的别名,而且数据库表Customer.DB存在该目录下,用户在使用这一例子时, 请注意这两项设置都是正确的。另外 Datasource1.Dataset,Table1.TableName和DBGrid1.Datasource属性都有下拉式列表框允许用户从可能的值列表中选择它们的值,这样能方便我们进行属性的设置,而且不容易出错。

        Datasouuce1.AutoEdit属性设置为False是为了防止用户修改数据库表中的数据, 在下面的讨论中我们将详细地进行说明。

        Table1.Active设置为True时,Delphi会打开Table1.TableName所指定的数据库表。如果这个数据库表不存在(或表中什么也没有, 即空表), Delphi 会弹出出错信息并且Table1.Active变成False。当Table1.Active被设置成True之后,Table1 部件的一些属性就不能再修改了,如Table1.DatabaseName和Table1.Tablename属性。若要修改它们, 必须首先要将Table1.Active属性设置为False,然后再进行修改,否则,Delphi会弹出错误信息“Cannot perform this operation on an open database”。当看到这个错误信息时,只需把Table1.Active置成False,完成相关的修改后,再把 Table1. Active 属性设置为True。

        当我们把DBGrid1.DataSource的值设置成DataSource1时,Delphi会把Customer.DB中的数据填充到DBGrid1部件中,并且可以用DBGrid1中的滚动条来浏览数据库表中的所有记录。 

14.1.3 运行程序 

        保存文件,命名代码单元为Cust.pas,命名工程名为CustPRJ.DPR,然后按F9编译并运行程序。程序执行之后,我们可以使用滚动条或键盘移动键在字段和记录间移动。但不能修改表中的数据,因为Datasouc1.AutoEdit1属性已被设置为False。

        Cust程序中的三个部件都有各自的特殊用途,三个部件的相关属性在内部相互联系生成最终的应用程序。TTable部件连接磁盘上的实际数据库表和应用程序中其他部件的通道。TTable部件具有打开和关闭、读取、更新以及其他处理磁盘数据库文件的方法。

        TDatasource部件是连接TTable部件和数据浏览部件如TDBGrid部件的桥梁。 TDBGrid部件用于显示数据库表中的数据信息,它为应用程序提供一个直观的界面。图14.2阐述了这三个部件之间的关系。 

Cust程序中三个部件之间的内部关系 

        TDBGrid 部件的奇妙之处在于它知道如何去获取数据库表中的下一条或前一条记录,我们使用滚动条或箭头键便可以完成这项任务。TDBGrid部件不知道如何增加、 删除和修改记录。如果想让 Cust 程序能够修改数据库表中的记录, 只要把 Datasource1 部件的AutoEdit属性设置成True , 并重新编译和运行程序就可以达到目的。 使用箭头键, 把DBGrid的高亮度条定位到某一个字段上,然后键入新值,该字段中的值将被键入的新值所取代,并且当移动到另一条记录时,健入的信息会自动写入数据库表中。如果想放弃所做的改动,只需在离开该字段前按一下Escape键。

        如果想在表中增加新记录,可以把高亮度条移到网格底端的空白记录上并输入新记录的有关字段值。也可以在用户指定的某一条记录的后面插入一条新记录,只要把高亮度条定位到指定的记录上,按Ins键,使可以在该记录的后面插入新记录。

        删除某一条记录时,把高亮度条定位在想删除的记录的任何字段上,按Ctrl+ del键,这时会出现保护信息,我们可以确认是否真的想删除该项记录。

        TDBGrid为用户提供了较完备的功能,用于控制是否编辑、增加或删除记录。 若想禁止对数据库表作任何修改,设置TDBGrid部件的Readonly属性为 True , 并设置 Option.dgEDiting为False(这将为我们提供一个只读的数据库表浏览器而不是数据库编辑器,但它隐含着增加、编辑和删除记录的能力)。TDBGrid部件的这些属性和Option属性其它选项的各种不同组合可以让我们很方便地对数据库表进行有效的浏览、编辑等操作。

        如果我们经常使用像电子表格那样的界面来显示和编辑数据记录,TDBGrid 部件便是一个很方便的工具,但那并不是最友好的用户界面,如果想拥有更优美更直观的界面,我们还可以使用单独的数据浏览部件来显示数据库表中各个字段的值,并利用TDBNavigator部件控制对数据库表的存取。 

14.2 利用TDBNavigator部件创建存取程序 

          我们可以改进一下Cust程序以便它一次只在对话框中显示一个客户的记录信息,并用一个TDBNavigator部件控制对记录存取──允许我们选择一个记录来显示或编辑以及增加和删除记录。完成的应用窗体。

增强的Cust程序

14.2.1 创建应用程序窗体 

        我们可以非常迅速地创建起来,因为到目前为止我们对创建窗体的方法已经比较熟悉,我们首先把所有的部件都放到窗体中,然后再设置它们的属性。

        开始一个新工程,设置窗体Form1的Name 属性为 Customerform2 , Caption 属性为         CustomerForm2。然后从部件选择板上的Data Access页上选取一个Datasource部件和一个Table部件放在窗体的右上角。再从Data Controls页上选取DBNatvigator部件放在窗体的左上角。

        窗体中其余的部件如图14.3所示。它们是TDBEdit和TLabel部件,按图14.3 所示创建并布置部件,分别命名DBEdit部件为EditCustno、 Editcompany 、 EditAddr1 、EditAddr2、EditCity、EditState、EditZip、EditCountry、EditPhone 、EditFAX、EditTaxRate、EditContact。

        现在我们来连接TTable部件和 TDataSource 部件, 然后连接所有的数据浏览部件和DataSource部件。设置TBNavigator部件和TDBEdit部件的属性,它们的DataSource属性都设置为DataSouce1。我们最后要做的事是连接窗体中各个TDBEdit 部件和它们在数据库表中对应的字段名,通过设置TDBEdit 部件的 DataField 属性来完成。 例如要连接命名为EditCustNo的TDBEdit部件和数据库表中的CustNo字段,具体步骤如下:

①选中窗体中的EditCustNo部件。

②在Object Inspector窗体中,单击DataField属性右边的箭头。

③从下拉列表中选中CustNo字段名。

          对窗体中的其他TDBEdit部件执行以上操作连接到其对应的字段,然后保存文件。 命名代码单元名为Cust2.pas,命名工程名为Cusprj2.DPR。 

14.2.2 使用TDBNavigator部件移动记录指针 

        上述程序运行之后,在数据浏览部件中会显示数据库表中的第一条记录。利用Tab 键可以在字段之间移动,但是不能编辑字段。因为我们为了防止意外修改,设置了Table1的AutoEdit属性值为False。如果想对数据库表中的记录进行编辑、 插入和删除操作或者想显示数据库表中另一条记录, 需要按 TDBNvigator 部件上这些功能所对应的功能按钮。TDBNavigator部件上的按钮和它们的功能如图14.4所示。

TDBNavigator中的按钮 

        TDBNavigator部件的绝大多数功能都可以根据其按钮的图标能够很容易地识别出来,而且TDBNavigator部件本身能感知到很多事情,如当前指针是否在数据库表的开头或尾部。如果用户正在查看数据库表中的最后一个记录,Next和Last按钮将会变灰成为非活动状态。同样, 如果用户当前正在浏览数据库表中的第一条记录, TDBNavigator 上的 First 和Previous按钮会变灰而成为非活动状态。有关各个按钮的作用的更详细说明请查看联机帮助。

        如果用户想修改当前的记录,单击TDBNavigator部件的Edit按钮,然后完成需要做的修改,在做完修改之后,单击Post按钮以便将作的修改写入实际的数据库表中(更新实际的数据库表中的记录在数据库术语中叫作“投寄”记录即PostT)。 如果想取消所做的修改,单击Cancel按钮。Cancel按钮只取消自从上一次往数据库表中投寄记录以来对记录所做的修改。例如,如果用户曾修改了CustNo字段并单击了Post按钮投寄了修改,然后再修改Company字段并按Cancel,那么只有对Company所做的修改将会被取消。也就是说,一旦修改被写入了数据库表中,再按Cancle按钮是无法取消对记录的修改的,要想恢复到以前的状态,用户必须要重新编辑修改记录。值得注意的是,当用户修改了当前的记录,并移动到其他记录时,TDBNavigaator部件会自动地投寄用户对记录的修改。 例如:如果我们修改了记录的Company字段,并没有按Post按钮以更新表中的记录, 而是移动到下一条记录,这时用户对记录的修改也会自动地被写入数据库表中。 

14.2.3 定制TDBNavigator部件 

        TDBNavigator部件中的按钮对我们开发人员来说是很方便的,但对于程序的最终用户来说不一定那么一目了然。为了帮助最终用户或初级用户更方便有效地使用TDBNavigator部件,我们可以设置TDBNavigator部件的ShowHint属性为True,这样当鼠标光标停留在TDBNavigator部件上的某一个按钮上超过大约1秒钟,在屏幕上便会出现该按钮的提示信息。如果我们不想使用TDBNavigator部件本身嵌入的提示信息,我们还可以设置TDBNavigtor部件的Hints属性,为每个按钮指定特定的提示信息,以帮助用户使用TDBNavigator部件。

        TDBNavigator部件中有多个功能按钮,但并不是所有的按钮对每一个数据库应用程序都是需要的,特别是那些不允许修改表中的数据,或修改只是在很严格的控制下进行的数据库应用程序。我们可以通过设置TDBNavigator部件的 VisibleButtons 属性来确定要在TDBNavigator中显示哪些按钮步显示哪些按钮。例如,如果我们不允许用户修改表中的记录,我们就不需要Add、Delete、Post、Cancel 或 Refresh 按钮, 我们设置这些按钮的VisibleButtons属性为False,这样在TDBNavigator部件中将不会出现这些按钮。

        TDBNavigator部件的ConfirmDelete属性和Delete 按钮配合使用对用户删除数据库表中的记录是非常有用的,当ConfirmDelete属性设置为 True (缺省设置), 当用户单击Delete按钮试图删除当前记录时,Delphi会弹出一个确认框,要用户确认是否真的想删除当前记录。这样,在用户进行删除记录的操作时,会更安全一些。如果用户不希望在按下Delete按钮时出现确认框,只要把ConfirmDelete设置为False就可以了。

        还有一些属性可以用来定制TDBNavigator部件的外观和性能,有关这方面的详细信息请参看联机帮助。

14.3 创建主要──明细数据库应用 

        我们前面在介绍的基于单个数据库表的数据库应用程序只能对数据库表进行简单的管理,大多数只用来浏览单个数据库表中的记录信息,如果我们想浏览多个相关的数据库表中的记录信息,就必须要创建主要──明细型数据库应用程序。

        在主要──明细型数据库应用程序中,一个数据库表作为主要表,其中存放着综合信息,其他的数据库表和主要数据库表相关联,它们当中存放着更详细的信息。例如,当数据库表Customer.DB作为主表,它包含着客户的综合信息如编号、姓名、 所在公司的名称等等。而数据库表Orders.DB中包含着每个客户的订货单的详细信息,如订单号、 发货日期、起运日期、发货目的地等信息,这样当在Customer.DB表中查看某一位客户时, 利用其中的字段CustNo与Orders.DB表发生联系,自动地从Orders.DB表中检索出这位客户曾经发来的所有订货单的详细信息。主要──明细型数据库体现了关系数据库的特点,即独立的数据库表之间基于它们共同的字段而发生联系。在这里Customer.DB和Orders.DB拥有一个共同的字段CustNo。

14.3.1 一对多关系的主要──明细型数据库应用程序 

        主要和明细数据库表之间存在一对多的关系,意思是说对于主表中的一条记录,在明细表中有多条记录与之对应。例如,创建一个主要──明细型数据库应用程序,其包括两个表Customer.DB和Orders.DB,它们分别作为主表和明细表,创建好的应用如图14.5所示,窗体中各部件的属性设置  

表14.2 主要──明细型数据库应用中各部件的属性

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

部 件 属 性 属 性 值 注 释

──────────────────────────────────

Table1 Active True

(主表) DatabaseName DBDEMOS

TableName CUSTOMER.DB

──────────────────────────────────

DataSource1 DataSet Table1

AutoEdit False

──────────────────────────────────

Table2 Active True

(明细表) DatabaseName DBDEMOS

TableName ORDERS.DB

IndexFieldNames CUSTNO 指定字段CUSTNO作为

Table2中的索引字段

MasterField CUSTNO 指定与主表发生联系

的字段

MasterSource DataSource1 说明与主表相连接的

数据源即DataSource

──────────────────────────────────

DataSource2 DataSet Table2

AutoEdit False

──────────────────────────────────

DBGrid1 DataSource DataSource1

(对应主表)

──────────────────────────────────

DBGrid2 DataSource DataSource2

(对应明细表)

TableName ORDERS.DB

──────────────────────────────────

DBNavigator1 DataSource DataSource1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

        一对多关系是非常普遍的关系。即便是简单的名字/ 地址数据库都有一对多的关系,因为一个人可能不止一个地址:家庭地址、工作地址、还可能有别墅地址。在本例中,公司的一个客户常常有多个订货单,当我们单击DBNavigator1中的向前、向后按钮时,会移动DBGrid1中的记录指针,而在DBGrid2中会自动显示与DBGridl 中当前记录相关的多条记录,即显示一个客户的信息时,同时会显示该客户的所有订货单的详细信息。 

14.3.2 一对多──多关系的数据库应用 

        前面我们介绍了基于两个表的一对多关系的应用,下面我们介绍怎样创建一个从三个表中浏览数据记录的一对多关系的应用。

         例如:一个客户也许有多张订货单,而每一张订货单中有多个订货项目,这样我们在Customer.DB表和Orders.DB表之间建立一个主要──明细型关系,同时在orders.DB 表和Items.DB表之间建立一个主要──明细型关系。 

窗体中各部件的属性如表14.3所示 

表14.3 一对多──多关系的应用中各部件的属性

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

部 件 属 性 属 性 值 注 释

──────────────────────────────────

Active True

Table1 DatabaseName DBDEMOS

TableName CUSTOMER.DB

──────────────────────────────────

DataSource1 DataSet Table1

AutoEdit False

──────────────────────────────────

Active True

DatabaseName DBDEMOS

Table2 TableName ORDERS.DB

IndexFieldNames CUSTNO

MasterField CUSTNO

MasterSource DataSource1

──────────────────────────────────

DataSource2 DataSet Table2

AutoEdit False

──────────────────────────────────

Active True

DatabaseName DBDEMOS

Table3 TableName ORDERS.DB

IndexFieldNames ORDERNO

MasterField ORDERNO

MasterSource DataSource2

──────────────────────────────────

DataSource3 DataSet Table3

AutoEdit False

──────────────────────────────────

DBGrid1 DataSource DataSource3

──────────────────────────────────

DBNavigator1 DataSource DataSource1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 

        窗体中其余的部件都是TDBEdit和TLabel部件,它们用于显示Customer.DB中的字段值和Order.DB中的字段值。在该例子中,总共连接了三个表, Customer. DB 表是主要表,Orders.DB表在窗体中起到了双重作用,它既是Customer.Db表的明细表,同时又是Items.DB表的主要表,Items.DB表是Orders.DB表的明细表。 

14.4 字段对象的使用 

          Ttable和TQuery部件中有一个TField类型的属性Fiedls,Fields是TField类型的对象的列表,TField对象列表是Delphi数据库中较难以理解的一个对象,它是 TTable 部件和TQuary部件的一部分,它们是不能够选择到窗体中的独立的部件,而且无论是在设计阶段还是在程序运行过程中,它们都没有可见的图像。即使到Object Inspector窗中察看它们也很困难。

        Tfield对象是在打开磁盘上的数据库表时动态产生的,并在数据库表被关闭时自动消失,TField对象可以控制表中的每一列是否在数据浏览部件中显示以及以何种格式显示等等。通过字段编辑器(Fields Editor)我们可以建立永久性的TField 对象列表代替动态的Tfield对象列表供Delphi应用程序使用,通过Fields Editor建立的永久性的字段对象会自动地加入到程序库单元的TForm类型定义中 ,它们保存在应用程序中,即使数据库表的基本结构发生了改变,它也是一直保留着,当然如果修改后的表中使得原来所定义的字段对象不再存在,Delphi应用程序在运行过程中会给出现错误信息。 

14.4.1 字段对象的类型 

字段对象TField对应数据库记录中的各个字段,因为数据库记录中的字段有多种数据类型,因此对记录字段可能出现的每一种数据类型都有一个独立的TField类型与之对应。TField的类型如表14.4所示 

表14.4 字段对象的类型

━━━━━━━━━━━━━━━━━━━━━━━━━

字段对象的类型 对应的数据类型

─────────────────────────

TBooleanField 布尔型数据

TCurrentyField 货币型数据

TStringField 字符串数据

TIntegerField 整数型数据

TBLOB 大二进制对象

━━━━━━━━━━━━━━━━━━━━━━━━━ 

         在大多数情况下可能使用的是TStringField和TIntegerField类型的字段对象, 从编程的角度来看这些TField对象的不同类型是完全相同的,应用程序是根本不必关心TField对象的实际类型,它们之间的主要区别在于:它们内部保留的以及它们和数据库表之间传递的数据类型不一样。

14.4.2 创建永久性的字段对象 

        我们知道字段对象在设计和运行阶段都是不可见的,它既可以随着磁盘上的数据库文件被打开时动态地生成也可以通过字段编辑器Fields Editor来创建它。 在应用程序中使用Fields Editor可以为数据库表中的字段创建相应的永久性的TField对象,TField 部件是不可见的部件,但是通过它,我们可以定义数据库表中各字段的显示属性和显示顺序以及控制字段的取值范围等。下面的例子,告诉我们如何使用Fields Editor定义Customer.DB表中的四字段,并在网格中显示表中的记录信息。

操作步骤:

1、建立一个基于 customer. DB 表的数据库应用窗体, 并在窗体中用一个网格显示customer.DB中的全部字段,详细方法请参见14.1节,建好的窗体如图14.1所示。

2、设置窗体中Table1的Active属性为True,使网格显示表中的记录。

3、选中Table1并双击鼠标左键,打开字段编辑器Fields Editor, 缺省情况下字段列表为空。

4、单击鼠标右键弹出一个弹出式菜单,然后选择Add Fields菜单项,缺省情况下表Customer.DB中的全部字段被选进字段列表框。 从字段列表框中选择你要在网格中显示的字段,具体做法是:单击Custno字段,并按住CTR键,再单击Company、Phone、LastInviceDate字段,然后单击OK按钮,确认被选择的四个字段,时窗体中的DBGrid1中只显示刚才被选中的四个字段值,而不再显示表中其它的字段值。

5、改变字段的显示顺序。单击LastInvoiceDate 字段并将它拖放到字段列表框中的第三行,即处于Company和Phone字段之间。此时窗体中显示Customer.DB 表中记录的字段将按新的顺序显示。

6、选择Close按钮,关闭字段编辑器Fields Editor。

7、按F9,运行上述程序。

14.4.2 字段对象的属性设置 

     虽然字段对象是不可见的对象,但是它同样具有很多的属性。在程序设计阶段,我们通过一定的方式可以设置它的有关属性,下面是设置字段对象的属性的方法和步骤。

1、选择窗体中的table1。

2、双击table1,打开字段编辑器Fields Editor。

3、选择要设置属性的字段。

4、在Object Inspector中修改字段对象的属性。

     我们可以按上述方法设置Table1中各字段对象的有关属性,当我们选择Custno字段并修改其属性,窗体内会出现对话

字段对象的属性

     修改字段CustNo的Alignment属性为taCenter,此时网格中显示的CustNo 字段值由原来的右对齐变成了居中。

表14.5中列出了字段对象在设计阶段可以修改的属性以及属性说明 

表14.5 字段对象的重要属性

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

属 性 注 释

─────────────────────────────────

Alignment 说明字段值的显示方式:左对齐、右对齐、居中

─────────────────────────────────

Calculated 当该属性值为True时,表明该字段的值是根据其它字

段的值计算得来的。否则该字段是数据库表中的字段

─────────────────────────────────

DisplayLabel 说明字段在网格部件中显示时的标题,缺省情况下字

段的标题就是字段名

─────────────────────────────────

DisplayWidth 说明字段在网格中显示时所点的列宽度,即字符数

─────────────────────────────────

DisplayFormat 说明字段在显示和编辑状态下的显示格式和输入的过

and EditMask 滤条件(限定用户输入字段值的范围)。

─────────────────────────────────

FieldName 在数据库表中对应于该字段对象的字段名称

─────────────────────────────────

Index 指定该字段对象在数据集部件中的逻辑位置,如Table1

中的第一个字段对象的Index值为0

─────────────────────────────────

Name 字段对象的名称,缺省情况下,它是TTable、TQuery

部件的名称加上字段的名称。如上例中的CUSTNO字段

对象的Name属性值为Table1CUSTNO,通过字段对象的

Name属性可以访问该字段的值,如Table1CUSTNO.Value

─────────────────────────────────

ReadOnly 说明该字段是否能被修改,当该属性值为True时,该

字段的不能被修改

─────────────────────────────────

Visible 当该属性值为True时,在与之相连的网格部件中将不

显示该字段

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

      根据表14.5中的属性, 我们可以修改上例中一些字段的某些属性, 使网络中显示表Customer.DB中的记录更符合我们的工作习惯。修改的属性如表14.6所示, 经过修改后的程序运行结果如图14.10所示。

表14.6 修改后的字段对象的属性

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

字 段 属 性 属 性 值

─────────────────────────────

CustNo DisplayLabel 客户编号

─────────────────────────────

Company DisplayLabel 公司名称

─────────────────────────────

Phone DisplayLabel 电话号码

─────────────────────────────

LastInvoiceDate DisplayLabel 购买日期

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

修改字段对象的属性  

14.4.4 字段对象的访问  

     字段对象在应用程序中有动态生成的,也有通过字段编辑器Fields Editor 创建的永久性的,它们虽然在设计和运行阶段都是不可见,但是它们跟其他的对象一样都拥有自己的属性、方法和事件,因此我们在应用程序中是可以对字段对象进行控制和访问的。

因为动态字段对象是没有自己的名字的,永久性的字段对象有自己的名字,所以对这两种字段对象的访问方法是不一样的。

14.4.4.1 动态字段对象的访问

     动态字段对象存在于数据集部件TTable和TQuery部件中,它们是随着磁盘上的数据库文件的打开而动态生成的,并且每一个字段对象对应于数据库表中的一个字段(即记录的一列),TTable或TQrery部件中所有的字段对象存在属性Fields列表中,Fields列表中的字段对象就像数组元素一样拥有自己的索引号,我们可以用这个索引号来访问字段对象。索引号在程序运行时赋值,从0开始,表中最左边的一列(第一个字段)的索引号为0,紧接着右边一个为1,以此类推。访问这些属性的方法和处理其他对象一样。 

Table1.Fields[0].DisplayLabel:='标识符' 

     上述代码让我们访问与Table1相连的数据库表中的第一个字段,并为该字段指定一个标题,这是通过设置它的DisplayLabel属性值为一个特定的标识符来实现的。

    通过索引号来访问Fields属性中的字段在使用For循环对列号进行迭代时会非常有用。但是在大多数简单应用程序中,通过列名(字段名)来访问字段会更加明白而且易读。在TTable部件中,提供了一个名为FieldByName的方法以便让我们通过列名访问字段对象。

Table1.FieldByName('CustNo').DisplayLabel:='标识符'

通过这种途径同样可以访问CUSTOMER.DB表中的CustNo字段, 并为该字段指定一个标题信息。

    现在我们可以建立一个允许用户通过字段名和索引号来访问Customer.DB 表中的字段对象的简单窗体。

字段对象的访问

    在该应用窗体的运行过程中,我们通过程序来访问其中的字段对象并设置有关的属性,这一控制过程我们放在窗体的OnCreate事件处理过程中。

例14.1 在窗体的Oncreate事件处理过程中访问字段对象。 

procedure TForm1.FormCreate(Sender:TObject);

Begin

with Table1 Do

begin

{通过索引号访问字段对象}

Field[0].DisplayLabel:='客户编号';

{通过字段名访问字段对象}

FieldByName('Company').DisplayLabel:='公司名称';

FieldByName('Phone').DisplayLabel:='电话号码';

FieldByName('LastInvoiceDate').DisplayLabel:='购买日期';

end;

end;  

在程序运行过程中访问字段对象 

14.4.4.2 永久性字段对象的访问 

     通过字段编辑器Fields Editor 建立的永久性字段对象的访问相对于动态字段对象的访问要简单得多,我们在程序中可以直接通过字段对象的名称(即Name属性)进行访问。

例如:

Table1CustNo.DisplayLabel:='客户编号';

Table1CustNo.DisplayWidth:=12;

14.4.4.3 字段对象的读取和赋值 

    通过字段对象的Value属性,我们可以读取字段对象的值,例如在如图14.13所示的窗体中,单击Read按钮便可以将Customer.DB表中当前记录的COMPANY字段的值读取到编辑框Edit1中。 

读取字段对象的字段值

窗体中各部件的属性如表14.7所示 

表14.7 各部件的属性

━━━━━━━━━━━━━━━━━━━━━━━━

部件的属性 属 性 值

────────────────────────

Button1.Caption &Read

Button1.Name Button1

Label1.Caption 字段值

Label1.Name Label1

Edit1.Text

Edit1.Name Edit1

━━━━━━━━━━━━━━━━━━━━━━━━

 

其它部件的的属性跟前面的例子一样。

为Read按钮编辑的OnClick事件处理过程如下: 

procedure Form1.TButton1Click(Sender:TObject);

begin

Edit1.Text:=Table1Company.Value;

end;

    在这里要注意的是:从字段对象中读取字段值时必须要将它赋给与之数据类型相匹配的变量,否则会出错。在上面的程序代码中,Table1Company的类型是TStringField 即是字符串类型的字段,而编辑框Edit1的属性Text的类型也是字符串型的, 因而它们是匹配的。如果类型不匹配,则要经过一定的转换才能够相互赋值。如: 

Edit1.Text:=Table1CustNo.Value 

    这条代码在运行过程中将会出错,因为TablelcustNo是TFloatField 类型即是数值型数据,要在编辑框Edit1中显示数值型数据要经过下列转换: 

Edit1.text:=Table1CustNo.AsString; 

        AsString是字段对象的属性,通过字段对象的AsString属性可以读取字段值并且将它转换成字符串类型。字段对象的字段值可以转换成以下几种类型的数据:

AsString: 将字段值转换成字符串数据

AsBoolean: 将字段值转换成布尔型数据

AsDateTime: 将字段值转换成日期时间数据

AsFloat: 将字段值转换成数值型数据

AsInteger: 将字段值转换成整数型数据

下面的程序代码是从字段对象中读取字段值并将它显示在编辑框Edit1中, 或者将字段值赋给相匹配的变量。 

CustNoDouble: Double;

CustNoInt: Integer;

CustNoString: String;

{在Edit1中显示字段值}

Edit1.Text:=Table1Company;{类型相匹配,不需要转换}

Edit1.Text:=Table1CustNo.AsString;{类型不匹配,需要转换}

{将字段值赋给变量}

CustNoDouble:=Table1CustNo.Value;{类型相匹配,不需要转换}

CustNoInt:=Table1CustNo.AsInteger;{类型不匹配,需要转换}

CustNoString:=Table1CustNo.AsString;{类型不匹配,需要转换}

14.4.5 设定字段对象的显示格式 

    我们即可以在设计阶段设定字段对象的显示格式,也可以在运行过程中通过程序代码来设定字段对象的显示格式。

    例14.2 在如图14.10所示的窗体中,再增加一个TaxRate字段, 并在程序设计过程中设定它的显示格式为0.00%,即设置TaxRate字段对象的DisplayFormat属性为0.00% , 若TaxRate的值为0.085那么在网格部件中其显示的格式为8.50%。

    在运行过程中我们通过程序代码来设定字段Phone的显示格式, 美国的电话表示形式与中国的表示形式不一样(如美国808-555-0269,中国(808) 5550269 ), 为此我们将phone 字段的值表示成中国式的形式。 具体方法是:在 Object Inspector 中选取Table1phone对象,并为此对象的OnGetText事件编写如下程序代码:

TForm1.Table1PhoneGetText(Sender:TField;

Text:OpenString;DisplayText:Boolean);

begin

If DisplayText then

begin

Text:=Table1Phone.Value;

Delete(Text,4,1);

Delete(Text,7,1);

Insert('(',Text,1);

Insert(')',Text,1);

end;

end;

图14.14 设定字段对象的显示格式

14.4.6 自定义字段以及计算字段对象的创建 

    有时候为了使应用程序完成所期望的工作,我们要在数据库表现有字段的基础上增加一些自定义的字段,这些字段并不是数据库表中实际存在的字段,它们常常是根据数据库表中的其它的字段动态地计算出来的,因而它们常常被称为计算字段。

例如我们创建一个浏览ORDERS.DB表中记录的应用如图14.15所示。

浏览ORDERS.DB表中的记录  

首先,我们想在显示OREDRES.DB表的网格中增加一个自定义的字段对象,完成以下步骤:

1、双击窗体中的Table1,打开字段编辑器Fields Editor。

2、在Fields Editor窗口中,单击鼠标右键,选择New Fields菜单项。

3、Delphi显示New Fields对话框。选择Field Type列表框中的Currency 项, 并在Field Name文体框中输入Balance , 这样我们自定义了一个 CurrencyField 类型的字段Balance。Delphi会自动地填入相应的字段对象名,其缺省值为Table1Balance。如图14.16所示。 

图14.16 New Field 对话框  

4、单击Ok按钮,关闭New Field对话框。当Fields Editor 窗口重新出现时, 注意Balance已经出现在Fields列表框中。

5、在Fields Editor 窗口中单击鼠标右键, 并选择 Add Fields 菜单项, 打开AddFields对话框。

6、从Available Fields 列表框中, 按住 Ctrl 键并单击鼠标左键, 选择字段:

OrderNo、CustNo、SaleDate、ShipData、ItemsTotal、Amountpaid以及Balance.

7、单击OK按钮,关闭Add Fields对话框,得到如图14.17所示的Fields Editor窗口。 

图14.17 字段编辑器Fields Editor

8、双击Fields Editor的控制盒关闭字段编辑器Fields Editor。

    至此我们已经为Table1创建了一个自定义的字段对象Balance,下面我们把Balance字段设置成计算字段对象,使其显示每一个客户的现金余额,即此字段的值是由ORDERS. DB表中ItemsTotal和Amountpaid字段的值计算而来的。为使应用程序实现这种计算功能,完成以下步骤:

1、在Object Inspector中选择自定义字段对象Table1Balance,修改其 Calculated属性值为True。即定义Balance字段为计算字段。

2、在Object Inspector窗口中,选择Table1部件的Event页。

3、双击OnCalcField事件,为Table1OnCalcField编写事件处理过程如下:

procedure TForm1.Table1OnCalcFields(DataSet:TDataSet);

begin

Table1Balance.Value:=Table1ItemsTotal.Value-Table1AmountPaid.Value;

end;  

浏览ORDERS.DB 中的记录  

14.5 查询数据库中的记录

       数据库中储存着大量的数据信息,如何充分有效地查询其中的数据,对用户而言是至关重要的。如果想查询数据库,首先要确定要查询的字段要么是数据库表中的关键字段,要么是辅助索引。如果我们查询的是Paradox或dBASE数据库系统中的表,这是唯一的选择。

    一般而言,查询数据库中的记录的方法有两种:Gotokey方法和Findkey方法。两种方法十分相似,主要区别在于我们如何指定查找值。这两种方法的思想是在指定列(字段)中寻找指定的查找值,如果在数据库表中找到了这个值,表中的记录指针便指向该记录,这样我们便查询到了我们需要的记录,进而可以访问找到的记录中的各项数据。 

14.5.1 使用GotoKey方法查找数据记录 

使用Gotokey方法查询数据库中的记录的具体步骤如下:

1、确保要查找的字段是关键字或已经为它定义了辅助索引,并保证TTable部件的属性列表中有关键字段名或辅助索引名。

2、通过调用GotoKey方法,把要查找的TTable部件置成查找模式。

3、把查找值送进被查找的Field的查找缓冲区。

4、调用TTable部件的GotoKey方法,并测试它的返回值判断查找是否成功。

    如果查找成功,GotoKey返回一个True值,并且表中的记录指针指向找到的记录。 如果查找失败,GotoKey返回False,表中的记录指针不发生变化。

    在这里要注意的是如何给Field的查找缓冲区赋值, 我们知道字段对象是不可见的对象,它们没有自己的名字,在大多数情况下,要使用TTable部件的FieldByName 方法到字段列表中查找字段对象以便为它赋值。但字段缓冲区也是没有名字的,当TTable部件处于查找模式时,我们只要把查找值赋给字段对象的AsString属性就可以了。AsString的作用不只是它的表面意思。它是一个转换属性,任何赋给字段对象的AsString属性的字符串都将转换成该字段对象应于数据库表中的字段的数据类型。当然AsString不能将查找值转换成BLOB、Bytes、Memo和Graphic类型的数据,用户一般也不会查找这种数据类型的字段。

下面便是说明使用Gotokey方法查找数据记录的例子。

    例14.3 当用户在Edit1部件中输入客户号码并单击查找按钮,程序便开始在Table1中查找这个客户号。如果查找成功,查找信息“查找成功”便会显示在标签Label1上,被查询到的客户的电话号码显示在标签Label2上。表中的记录指针将转移到该客户记录处。并且在网格DBGrid1中以高亮度显示这一条记录。  

查询数据库中的记录

    下面的程序清单是查询按钮上的OnClick事件的处理程序,它是使用Gotokey方法查找数据库中的记录的。

procedure TForm1.Button1OnClick(Sender:TObject);

begin

with Table1 do

begin

Label1.Caption:=' ';

Label1.Caption:=' ';

IndexFieldName:='CustNo';

setkey;

FieldByName('CustNo').AsString:=Edit1.Text;

If GotoKey then

begin

Label1.Caption:='查找成功';

Label1.Caption:=FieldByName('Phone').AsString;

end;

else

Label1.Caption:='查找失败';

end;  

查询数据库中的记录 

14.5.2 使用FindKey方法查找数据库中的记录 

    虽然使用上面的Gotokey方法在数据库中查找记录效果不错,但是Delphi 还提供了一种更加容易的查找方法,这就是Findkey方法,两种方法虽然很相似,但是Findkey方法更简单明了一些。

例14.4 我们可以使Findkey方法代替上面例子中的处理程序,下面是程序代码:

procedure TForm1.Button1OnClick(Sender:TObject);

var

SeekValue:string;

begin

with Table1 do

begin

Label1.Caption:=' ';

Label1.Caption:=' ';

IndexFieldName:='CustNo';

SeekValue:=Edit1.Text;

If FindKey([SeekValue]) then

begin

Label1.Caption:='查找成功';

Label1.Caption:=FieldByName('Phone').AsString;

end;

else

Label1.Caption:='查找失败';

end;

        Findkey方法和Gotokey方法的根本区别在于查找值要作为参数传递给Findkey 函数。而GOtokey是不带参数的, 它假定用户已经把查找值赋给了代表着被查找到的字段的查找缓冲区。

        Findkey接受的参数是放在方括号中的,是用逗号分开的查找值数组。 数组中的每一个值都对应于特定列的查找值,即参数中允许有多个查找值,Findkey 允许用户同时查找数据库表中的多个列。上面的程序清单中的Findkey函数只接受了变量Seekvalue这一个查找值,这个查找值对应表中的字段CustNo,CustNo是表中的关键字段。如果要同时查找表中的多个字段,必须把要查找的多个字段名赋给TTable部件的IndexFieldName属性,并用逗号分开各字段,然后把每个字段的查找值赋给Findkey的参数数组中。 

14.5.3 利用GotoNearest和FindNearest执行不精确查找 

    在我们上面讨论的查找中,要么查找成功要么查找失败,因为我们查找的是特定查找值的一个精确匹配值。Delphi还提供了一种查找方法,即不精确查找,这样的查找绝对不会失败,它总是给用户查找出一个结果来,也许这结果并不是用户需要的,但这个查找出来的结果是最接近用户要求的。在Delphi中是利用GotoNearest和FineNearest两种方法来执行不准确查找的,它们总是从数据库中查找出与查找值最接近的匹配值。如果它们查找到与查找值精确匹配的值,那当然最好不过了,如果他们找不到精确匹配的值,它们就会把与用户指定的查找值最接近的记录提交给用户。

        GotoNearest的使用方法和Gotokey一样,FindNearest的使用方法和Findkey一样。跟Gotokey一样,使用GotoNearest时必须要把查找值赋给字段的查找缓冲区,两者的不同之处在于查找值的说明方式不一样,使用GotoNearest时, 说明的查找值可以是完整的也可以是不完整的,如果要对'Dunteman'进行不精确查找,在给字段的查找缓冲区赋查找值时,可以使用'Dunteman'、'Dun'或者`Du'作为查找值, 这样查找出来的结果会尽可能地接近这个值的。

     如果没有找到与用户指定的查找值精确匹配的记录,Delphi会调整记录指针并停留在与查找值最接近的第一个记录上。例如如果查找`Dunteman'时,没有找到精确匹配的值,记录指针可能会停留在`Dunwoody'上或者停留在更远一些的'Event'上;如果查找'Du' 没有找到精确匹配的值,记录指针可能停留在‘Duncan’上,甚至‘Dunteman'之前, 总之Delphi会自己地调整记录指针,使之指向最接近查找值的记录并将该记录作为查找的结果提交给用户。

         GotoNearest和FindNearest都返回一个Boolean值以表明查找是否成功。 它们一般都是成功的,它们总是要把记录指针移到某处。

下面的例子是用GotoNearest方法进行不精确查找。

    例14.5 创建好的窗体,在编辑框中输入一个不完整的客户所在的公司名称,并且按“不精确查找”按钮,然后观察一下查找的结果并注意记录指针指向那一条记录。反复试验几次便会理解GotoNearest是如何工作的。

利用GotoNearest方法执行不精确查找

窗体中的“不精确查找”按钮的事件处理过程代码如下:

procedure TForm1.Button1Click(Sender: TObject);

begin

with table1 do

begin

IndexFieldNames:='Company';

setkey;

FieldByName('Company').AsString:=Edit1.text;

GotoNearest;

label3.caption:=FieldByName('Company').AsString;

end;

end;

    读者可以利用 FindNearest 方法执行上面的不精确查找, 具体使用方法可以参看Findkey方法的使用。

在上面的例子中要设置table1的IndexFieldNames属性为Company。

GotoNearest方法进行不精确查找

14.6 修改数据库中的记录 

    我们掌握了字段对象的概念和如何查找数据库中的记录之后,下面我便可以很方便地修改数据库中现存的记录了,一般来说,在程序中修改数据库中的记录包括下面这些步骤:

1、在数据库中找到要修改的记录,并将记录指针移至该记录。

2、调用Edit方法将与数据库表相连的TTable部件设置成编辑状态。

3、修改一个或多个字段。

4、调用post方法将修改后的记录写入数据库。

    以上这几个步骤只是概述性的,具体实现时还有很多细节需要留心,我们通过一个例子来演示上面的全过程,以便让读者进一步地了解和掌握修改记录的方法。

   例14.6 我们为四个按钮分别编写了事件处理过程,用来遍历数据库中的记录并对每个客户记录的Company字段进行修改, 在程序对记录进行更新操作时窗口中的控件都是无效的,在这个例子中我们还编写了一个简单的异常代码块用来确保在更新过程中出现异常时使控件恢复正常操作。 

修改数据库记录

14.6.1 Edit方法Post方法 

    为了能让用户通过程序修改数据库表中的记录,TTable部件必须要处在编辑状态下。在大多数情况下,数据库表都是以浏览(只读方式)方式打开的,也就是说它的每一个字段可以被读取介不能被编辑修改。调用Edit 方法能够将 TTable 部件置成编辑状态, 当TTable部件处于编辑状态后,我们才可以通过程序修改当前记录指针所指向的记录,但这样修改后的记录不会立即被写入到磁盘上的实际数据库表中。要想保存对记录的修改,必须要调用Post方法,Post方法才真正将我们对记录的修改写入实际的数据库表中。

一般来说,用来扫描整个数据库表并修改每个记录的某一个字段的程序如下所示:

with Table Do

begin

DisableControls;{在修改记录的过程中,使其它部件无效}

First; {将记录指针指向第一条记录}

while not EOF do

begin

<读取记录的一个字段值到一个变量中>

<做适当的修改>

Edit; {将TTable部件置成编辑状态}

<将修改后的字段值写回到其对应的字段>

post; {将修改后的记录写回数据库}

next; {修改下一条记录}

end;

enablecontrols; {恢复其它部件的功能}

end;

    程序都是对TTable部件进行操作,因此使用With语句来防止错误的扩散是很有意义的。在这里要注意Disablecontrols方法和EnableControls方法的使用。DisableControls方法是在程序修改TTable部件中的记录时,切断TTable部件与数据访问部件TDatasource 部件的联系。否则,在对TTable中的每一修改之后,TDataSource 部件都会更新窗体中所有数据浏览部件的显示内容,这样会急剧减慢处理过程而且浪费时间。EnableControls方法是与DisableControle方法执行相反的操作,它是用来恢复TTable部件与TDatasource部件的联系并促使所有的数据浏览部件更新显示。

    调用First方法是将记录指针移到数据库表中的第一条记录, 确保程序从表中的第一条记录开始进行修改。调用Next方法是将记录指针从当前的记录移到下一条记录,这样保证了从表中的第一条记录开始逐条记录进行修改,直到修改完最后一条记录。如果不调用Next方法,程序将会陷入无穷的死循环。 

14.6.2 实现异常保护的TRY...FINALLY语句 

    上面的程序存在着潜在的危险,在实际应用过程中,可能因为某些原因使得对数据库表的更新不能进行下去。如当程序试图执行Post方法将修改后的记录写回磁盘时,而又因为某种原因磁盘没有准备好,这时便出现了异常。当出现异常时,应用程序会暂停下来并且会弹出一对话框显示有关的错误信息,在用户单击错误信息对话框之后,程序将继续执行到某一个地方去,而这个地方常常不是用户所能预料到的。 在我们的程序中, 在执行Post方法之前,窗体中所有的部件与TTable部件都已失去联系。因此,这种异常将导致窗体中显示的数据和数据库无关。

        Object Pascal中的Try...Finally语句为我们解决上述异常问题提供了一个解决方法。在Delphi中仍然采用了这一语句用来处理异常问题。实际上,Try...Finally 语句是把两组语句组合在一起。语句的Try部分包含了可能产生异常的程序代码,Finally部分包含了即使发生了异常也必须执行的一条或多条语句。 在本例中, Finally 部分只包含了EnableControls方法调用这一条语句,我们将前面的代码改写并组合进Try...Finally 语句: 

with Table Do

begin

DisableControls;{在修改记录的过程中,使其它部件无效}

Try;

First; {将记录指针指向第一条记录}

while not EOF do

begin

<读取记录的一个字段值到一个变量中>

<做适当的修改>

Edit; {将TTable部件置成编辑状态}

<将修改后的字段值写回到其对应的字段>

post; {将修改后的记录写回数据库}

next; {修改下一条记录}

end;

enablecontrols;

Finally;{出现异常时,执行下面的程序}

enablecontrols; {恢复其它部件的功能}

end; {结束Try...Finally语句}

end;

    在保留字Try和Finally之间的代码跟前面的代码是一样的,它们用于在记录之间移动记录指针并处理对记录的修改,这一段代码可能会出现异常,当异常发生时,我们想保证执行EnableControls, 以便窗体中各控件恢复与 TTable 部件的联系, 因此我们必须将EnableControls语句放在Finally和结束语句End之间。

    在这里要特别注意,请读者们不要混淆了Try...Finally语句和Try...Except 语句。如果真正想在发生异常时采取相应的处理,就要使用Try...Except语句。Try... Finally语句只是用来处理当异常出现时,使应用程序执行Finally部分的语句, 使程序继续执行下去。Try...Except语句是实现异常处理,Try...Finally语句是实现异常保护。

    有了上述这些概念,我们便可以提供这个例子的一些程序代码,它涉及了所有这些内容。

程序清单:修改数据库中的记录 

unit Unit26;

interface 

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, Grids, DBGrids, ExtCtrls, DB, DBTables, Buttons; 

type

TForm1 = class(TForm)

DataSource1: TDataSource;

customerTable: TTable;

Panel1: TPanel;

DBGrid1: TDBGrid;

Panel2: TPanel;

UpperCaseFirstAddBtn: TButton;

UpperCaseSecondAddBtn: TButton;

MixedCaseFirstAddBtn: TButton;

MixedCaseSecondAddBtn: TButton;

BitBtn1: TBitBtn;

procedure ForceCase(TargetField:String;ToUpper:Boolean);

procedure UpperCaseFirstAddBtnClick(Sender: TObject);

procedure MixedCaseFirstAddBtnClick(Sender: TObject);

procedure UpperCaseSecondAddBtnClick(Sender: TObject);

procedure MixedCaseSecondAddBtnClick(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end; 

var

Form1: TForm1; 

implementation

const

upper=true;

Mixed=False; 

{$R *.DFM}

Function IsUpper(ch:char):Boolean;

begin

If (ch>='A')and(ch<='Z')then

IsUpper:=true

else

IsUpper:=False;

end;

procedure TForm1.ForceCase(TargetField:String;ToUpper:Boolean);

var

WorkBuffer:string;

i:Integer;

begin

with customerTable do

begin

DisableControls;

TRY

First; {将记录指针移到第一条记录处 }

While not EOF do

begin

WorkBuffer:=FieldByName(TargetField).AsString;

If ToUpper then

for i:=1 to Length(WorkBuffer)do

WorkBuffer[i]:=UpCase(WorkBuffer[i])

else

begin

for i:=1 to Length(WorkBuffer) do

If IsUpper(WorkBuffer[i]) then

WorkBuffer[i]:=chr(ord(WorkBuffer[i])+32);

WorkBuffer[1]:=UpCase(WorkBuffer[1])

end;

Edit;

FieldByName(TargetField).AsString:=WorkBuffer;

post;

Next;

end;

Finally

enableControls;

end;

end;

end; 

procedure TForm1.UpperCaseFirstAddBtnClick(Sender: TObject);

begin

ForceCase('Addr1',Upper);

end; 

procedure TForm1.MixedCaseFirstAddBtnClick(Sender: TObject);

begin

ForceCase('Addr1',Mixed);

end;

 

procedure TForm1.UpperCaseSecondAddBtnClick(Sender: TObject);

begin

ForceCase('Addr2',Upper);

end;

 

procedure TForm1.MixedCaseSecondAddBtnClick(Sender: TObject);

begin

ForceCase('Addr2',Mixed);

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

customerTable.open;

end; 

end. 

14.7 插入和删除记录 

    虽然我们使用DBD或者在应用程序窗体中用TDBNavigator可以插入、删除表中的记录,但是任何重要的数据库应用程序都是根据最终用户的命令完成此类操作的。同样,如果我们掌握了字段对象及其用法,修改数据库中的记录,插入和删除记录将变得非常容易。

    要想删除表中的某一条记录,首先将记录指针移到该记录处,然后调用delete方法,这样,当前指针所在的记录就会被删除,而且我们在进行删除操作时,不必将TTable部件设置成编辑状态。当前指针所在的记录被删除之后,被删除记录下面的所有记录都向前移动,记录指针自动移到紧挨着被删除的记录的下一条记录。在删除记录的过程中没有提醒用户是否真的想删除当前记录的信息确认框,因此在进行此项操作时要倍加小心,如果是开发应用程序,最好的办法是提供一个确认信息框确保用户不会意外删除记录。

    插入一条记录也很简单,Delphi为用户提供两种方法用来插入记录到现存数据库表中,一种方法是在当前记录指针所在的记录处插入记录;另一种方法是在数据库表的尾部插入记录。这两种方法是分别调用Insert方法和Append方法实现的。但是无论是调用Insert方法还是调用Append方法在具有索引的数据库表中插入记录,增加到索引表中的记录都将按照索引顺序写入到数据库表中,也就是说对于索引表,调用Insert和Append方法的效果是一样的。事实上,Append方法只适用于那些没有索引的表,这种没有索引的表并不十分有用因而通常不创建这种表。几乎任何情况下我们都是用Insert方法来插入记录。

    用户在插入记录时一般可以采用两种方式插入:逐步插入即首先建立一条空记录,然后再填充记录的各个字段,最后再将记录写回到磁盘,共分三个独立的操作步骤;而使用InsertRecord方法便可以一次将插入记录的操作完成。 

14.7.1 逐步插入方法 

    逐步插入方法分为三个明确的步骤:先调用TTable部件的Insert方法在TTable中创建一条新的空记录,然后填充该记录的各个字段,最后调用post方法把新记录写到磁盘上的实际数据库文件中,在填充并传送记录以前,考虑插入记录到表中的什么位置是毫无意义的,假设插入的表是有索引的,在调用post方法时,Delphi会自动地把插入的新记录按照索引顺序插入到表中的正确位置。如果插入的表中没有索引,那么新记录将插入到当前指针所在记录的后面。

因此,采用逐步插入方法插入记录的程序代码一般如下形式:

With Table do

begin

Insert; {插入一条空白记录}

<填充该记录的各个字段>

post; {将插入的记录写回到磁盘文件}

end;

对于没有索引的数据库表,可以用Append方法替代Insert方法把新记录插入到表的尾部。 

14.7.2 调用InsertRecord插入记录 

    对于简单的应用程序,Delphi允许用户用一条语句插入一个新记录,而且这个新记录可以带有任意多个新字段值。InsertRecord方法把新记录中字段的赋值语句和psot方法调用组合进一条语句中。

       InsertRecord方法把记录的各个字段值组合成一个字段值数组作为它的唯一参数。在字段值数组中,可以为插入的记录的每个字段提供一个字段值,或从最左一列开始依次为任意多个字段赋值。 也就是说用户可以从表的最左边一列起, 把多个列的值同时传递给InsertRecord,直到所有字段都被赋值。用户也可以省略后面的字段,InsertRecord会用空值填充这些没有赋值的字段。用户还可以对那些明确希望用空值填充的字段传递保留字NIL来标明该字段为空。

如我们希望在Customer.DB表中插入一条记录,可以用下面的代码来实现: 

InsertRecord(['2000',NIL,NIL,NIL]); 

在上面的程序代码中,我们只填充了四个字段:CustNo、Company、Add1 、 Add2 。InsertRecord会自动将其它字段赋以空值。

例14.7 在这个例子中,我们在CustNo.DB表中插入和删除记录,都是在程序中完成这类操作的,而不再是使用DBD或数据浏览部件完成。 

插入/删除记录 

程序清单:

unit tt; 

interface 

uses

SysUtils, Windows, Messages, Classes, Graphics, Controls,

StdCtrls, Forms, DBCtrls, DB, DBGrids, Buttons, DBTables, Grids,

ExtCtrls,Mask,Dialogs;

 

type

TForm1 = class(TForm)

DBGrid1: TDBGrid;

DBNavigator: TDBNavigator;

Panel1: TPanel;

DataSource1: TDataSource;

Panel2: TPanel;

customerTable: TTable;

BitBtn1: TBitBtn;

Label1: TLabel;

Label2: TLabel;

BitBtn2: TBitBtn;

BitBtn3: TBitBtn;

CustNoEdit: TEdit;

CompEdit: TEdit;

procedure FormCreate(Sender: TObject);

procedure BitBtn2Click(Sender: TObject);

procedure BitBtn3Click(Sender: TObject);

procedure FormActivate(Sender: TObject);

private

{ private declarations }

public

{ public declarations }

end;

 

var

Form1: TForm1;

 

implementation

 

{$R *.DFM}

 

procedure TForm1.FormCreate(Sender: TObject);

begin

customerTable.Open;

end;

 

procedure TForm1.BitBtn2Click(Sender: TObject);

begin

If (Length(CustNoEdit.text)=0)and

(Length(CompEdit.text)=0)

then

MessageDlg('没有输入新记录的字段值!',mtError,[mbCancel],0)

else

with customerTable do

begin

IndexFieldNames:='CustNo';

If FindKey([CustNoEdit.text]) then

MessageDlg('已经存在这条记录!',mtError,[mbCancel],0)

else

InsertRecord([StrToInt(CustNoEdit.text),CompEdit.text,nil]);

CustNoEdit.text:=' ';

CompEdit.text:=' ';

end;

 

end;

 

procedure TForm1.BitBtn3Click(Sender: TObject);

begin

If (Length(CustNoEdit.text)=0)and

(Length(CompEdit.text)=0)

then

MessageDlg('没有输入删除的记录的字段值!',mtError,[mbCancel],0)

else

with customerTable do

begin

IndexFieldNames:='CustNo';

If FindKey([CustNoEdit.text]) then

begin

If MessageDlg('你确定要删除这条记录吗?',mtConfirmation,

[mbYes,mbno],0)=mrYes then Delete;

end

else

MessageDlg('没有你要删除的记录!',mtError,[mbCancel],0);

CustNoEdit.text:=' ';

CompEdit.text:=' ';

end;

end;

 

procedure TForm1.FormActivate(Sender: TObject);

begin

CustNoEdit.setfocus;

end; 

end. 

14.8 输入数据的有效性验证 

    当用户向一个数据库表中插入新记录或修改原有记录时,我们必须确保用户输入的数据是有效的,为此Delphi通过三种不同的途径用来验证用户输入的数据是否有效。

   这三种途径是:基于数据库表的有效性验证、基于字段的有效性验证、基于记录的有效性验证。

基于数据库表的有效性验证:

   在用户创建数据库表时就建立有效性验证机制,如在使用DBD创建一个表时, 我们可以为创建的数据库表说明一些验证手段,包括字段的最大值,最小值,图形字段的显示格式等等。在设定这些有效性验证机制时,不需要编写任何程序代码。基于数据库表的有效性验证是当数据写到数据库之前,由数据库本身来执行。Delphi也执行一些有效性验证,如在数据写到数据库之前Delphi会验证每一个字段是否被填入相应的值,有关这种途径来验证数据的有效性的详细情况请参考DBD的使用。

基于字段的有效性验证:

一般有两种方法来进行这种方式的有效性验证。

①为记录中需要设置有效性验证的字段编写Onvalidate事件处理过程。这样每当该字段的值被修改时,该字段的OnValidate事件处理过程就会被调用,进而对被修改的字段值进行验证。

②对于记录中要求非空的字段(如口令或关键字等),我们必须首先设置这些字段的Required属性为True,然后为这些字段编写OnValidate事件处理过程,这样在修改现存记录或插入新记录时,在写入数据库之前,如果要求非空的字段中没有填入适当的字段值,那么会出现错误信息提示用户必须输入字段值。

基于记录的有效性验证:

这种验证方式一般在TTable部件的BeforePost事件处理过程中进行处理,即在记录写回到数据库之前对记录的每个字段值进行有效性验证。

例14.8 在程序中对字段值的有效性进行验证。

1. 创建一个用TEdit部件浏览ORDERS.DB表的应用,如图14.25所示。

2. 修改TDataSource部件的AutoEdit属性为True。

3. 双击TTable部件打开字段编辑器Fields Editor,并单击SaleDate字段。

4. 在Object Inspector中双击SaleDate字段对象的OnValidate事件, 为该字段对象编写事件处理过程如下: 

TForm1.Table1SaleDateValidate(Sender:TField);

begin

If SaleDate.Value>Now then

raise Exception.Create('不能输入一个未来的日期');

end;

    当这个应用程序运行时,用户修改或插入ORDERS.DB中的记录时, 该应用程序会对销售日期(SaleDate)字段的值进行验证,该字段值不能晚于系统的当前日期,程序中调用Now方法获得系统的当前日期。如果字段值大于系统的当前日期会出现一错误信息提示框,告知用户不能输入一个未来的日期。

使用TDBComBox部件和TDBLookupComBox部件来限制用户输入字段值的范围。

    创建查看orders.db表的应用,创建好的窗体如图14.25所示。窗体中显示Terms 字段的是TDBComBox部件,显示EmpNo字段的是TDBLookupComBox部件。 

图14.25 用数据浏览部件限制用户的输入 

TDBComBox和TDBLookupComBox部件的属性值如表14.8所示: 

表14.8 窗体中各部件的属性设置

━━━━━━━━━━━━━━━━━━━━━━━━━━━

部 件 属 性 属 性 值

───────────────────────────

DataField Terms

DBComBox1 DataSource DataSource1

Items Prepaid

Net 30

COD

───────────────────────────

DataField EmpNo

DataSource DataSource1

DBLookupComBox LookupSource DataSource2

KeyField EmpNo

LookupField EmpNo

───────────────────────────

DataSource1 DataSet Table1

AutoEdit True

───────────────────────────

DataSource2 DataSet Table1

AutoEdit True

───────────────────────────

Table1 DatabaseName DemosDB

TableName orders.db

───────────────────────────

Table2 DatabaseName DemosDB

TableName orders.db

━━━━━━━━━━━━━━━━━━━━━━━━━━━

    该应用运行时,当用户修改和插入记录到ORDERS.DB表中时,Terms字段的值可以从组合框中的Prepaid、Net30、COD三个值中任选,EmpNo字段的值是从另一个表Employee中获得的雇员号码,用户可以从中选择。

 

posted on 2010-02-14 09:42  高压电脑  阅读(405)  评论(0编辑  收藏  举报