第七章 字 段 对 象
Delphi 4用TField来操纵数据集中的字段。不过,具体的字段实际上都是TField的派生类,因此,应用程序很少需要直接用到TField。
当打开一个数据集的时候,Delphi 4会自动生成动态的、与数据类型有关的字段对象。当然,可以用字段编辑器创建永久的字段对象来代替动态的字段对象。
7.1 具体的字段对象
Delphi 4支持24种类型的字段,它们都是从TField继承下来的,包括:
.TADTFieldOracle8的ADT字段。ADT是Abstract Data Type的缩写;
.TAggregateFieldTClientDataSet中自动维护的总计字段;
.TArrayField数组字段;
.TAutoIncFieldParadox表中的自动增长字段,取值范围从-2,147,483,648到2,147,483,647;
.TBCDFieldBCD字段;
.TBooleanField布尔字段;
.TBlobField二进制数据,理论长度2GB;
.TBytesField二进制数据,理论长度2GB;
.TCurrencyField实数字段,取值范围从5.0E-324到1.7E308;
.TDataSetField代表嵌套表的字段;
.TDateField日期字段;
.TDateTimeField日期和时间字段;
.TFloatField浮点字段,取值范围从5.0 * 10E-324到1.7 * 10E308;
.TBytesField二进制数据,最大字节数255;
.TIntegerField整型字段,取值范围从-2,147,483,648到2,147,483,647;
.TLargeintField长整型字段,取值范围从-263到263;
.TMemoField备注字段,理论长度2GB;
.TNumericField实数,取值范围从3.4 E-4932到1.1E4932;
.TReferenceField一个引用其他数据集的指针;
.TSmallintField整型字段,取值范围从-32,768到32,768;
.TStringField字符串字段,最大字节数8192,包括NULL字符;
.TTimeField时间字段;
.TVarBytesField二进制数据,最大字节数255;
.TWordField无符号整数,取值范围从0到65,535。
7.2 理解字段对象
就像Delphi 4的数据访问构件如TTable、TQuery一样,TField也是非可视的,甚至在设计期都不能直接看到它,只能通过数据访问构件和数据控件间接地访问它。
Delphi 4创建的字段对象与字段的数据类型有关,例如,对于字符串类型的字段,创建的是TStringField对象。对于浮点类型的字段,创建的是TFloatField对象。下面我们就以TFloatField为例,看看怎样操纵字段的显示属性:
.Alignment用于指定数据的对齐方式,如左对齐、居中、右对齐等;
.DisplayWidth用于指定数据的显示宽度(以字符数为单位);
.DisplayFormat用于指定数据的显示格式,如小数点的位置;
.EditFormat指定在编辑数据时显示数据的格式。
对于不同类型的字段对象来说,它们的属性大部分是相同的,但也有一些属性是专用的,例如,TFloatField的Precision属性就是其他字段对象所没有的。字段对象的大部分属性只是影响数据在窗体上的显示属性,但也有些属性如Precision还影响用户输入和修改数据的格式。
在一个打开的数据集中,所有的字段对象要么都是动态的字段对象,要么都是永久的字段对象。下面将详细介绍这两种不同类型的字段对象。
7.3 动态字段对象
默认情况下,当把一个数据集放到数据模块上并且打开它时,Delphi 4就会为数据集中的每一个字段自动生成一个动态的字段对象。之所以说它是动态的,一方面是因为它是自动生成的,另一方面是因为它总是反映物理数据集的情况,对于不同类型的字段来说,生成的字段对象的类型也不同。如果数据集的结构或其他信息发生变化,当应用程序重新打开这个数据集时,就会基于最新的结构和信息重建所有的字段对象。
动态字段对象的生存期是临时的,当数据集关闭时,这些字段对象也跟着消失。
由此可见,动态字段对象的最大特点就是适应性强。利用这个特点,可以编写一些通用的数据库应用程序,它可以打开不同的数据集。要在应用程序中使用动态字段对象,您首先要把一个数据集构件放到窗体或数据模块上,再把一个TDataSource构件放到窗体或数据模块上,设置它的DataSet属性指定数据集构件。把一个数据控件放到窗体上,设置它的DataSource属性指定TDataSource 构件,有必要的话,还要指定要显示哪个字段。最后,把数据集构件的Active属性设为True,打开数据集,此时,就会自动创建动态字段对象。
动态字段对象的不足之处是,要改变字段的显示属性、数据格式,需编写代码。即使您愿意写代码,无论如何也无法改变字段的显示顺序,不能把某些字段暂时隐去,也不能增加新的字段如“计算字段”、“Lookup字段”。而且,也不能改变字段的数据类型。所以,很多情况下,应用程序往往要用永久字段对象来代替动态字段对象。
7.4 永久字段对象
用永久字段对象代替动态字段对象的最大好处是,可以在设计期设置它的属性。此外,永久字段对象还具有以下优势:
.可以选择部分字段;
.可以增加新的字段,包括“计算字段”和“Lookup字段”;
.可以改变原有字段的数据类型。
7.4.1 怎样创建永久字段对象
要创建永久字段对象,就要用到字段编辑器。用永久字段对象能够保证应用程序每次运行时,用户看到的总是这几个字段,即使数据集的结构已发生改变。这样,不管是数据控件还是程序代码都不必担心因为数据集的结构发生变化而导致数据控件和程序代码不能适应。不过,如果一个永久字段对象基于的源字段被删除,就会触发异常。
创建永久字段对象的一般步骤是:
第一步是把一个数据集构件如TTable放到数据模块上,设置DatabaseName属性指定要访问的数据库,设置TableName属性指定要访问的表。
第二步是双击TTable构件,Delphi 4将打开字段编辑器,如图7.1所示。
图7.1 字段编辑器
字段编辑器由标题栏、导航按钮和一个可复选的字段列表组成。
标题栏显示数据集的名称包括数据集所在的窗体或数据模块的名称,例如,假设数据集叫Customers,放在数据模块CustomerData上,标题栏就显示CustomerData.Customers。
四个导航按钮用于浏览数据集的记录,它们能够向前翻一个记录、向后翻一个记录、翻到第一个记录以及翻到最后一个记录。如果数据集的Active属性设为False或者数据集是空的,这四个导航按钮将变灰。
导航按钮下面是一个可复选的列表框,列出了所有的永久字段。刚开始打开字段编辑器的时候,列表是空的,因为默认情况下,Delphi 4生成的是动态字段对象。
第三步是单击鼠标右键,在弹出的菜单中选择“Add Fields”命令,弹出“AddFields”对话框,如图7.2所示。
图7.2 选择字段
选择一个或几个字段,然后单击OK按钮。Delphi 4将根据您选择的字段创建永久字段对象,这些永久字段对象的名称将出现在字段编辑器中,如图7.3所示。
图7.3 列出永久字段对象的名称
导航按钮都是灰的,可能是因为数据集没打开。只要创建了一个永久字段对象,以后每次打开数据集时,Delphi 4就不会创建动态字段对象。对于同一个数据集来说,动态字段对象和永久字段对象不可能同时存在。
每次打开数据集时,Delphi 4都会在数据集中检查每个永久字段对象所基于的字段是否还存在,如果字段已不存在,Delphi 4将触发异常,并且拒绝打开数据集。
一旦生成了永久字段对象,就可以把它看作是一个构件,在对象观察器中设置它的属性,建立事件句柄。事实上,一个永久字段对象就是一个构件。
对于那些数据控件来说,只要创建了一个永久字段对象,就只有创建了永久字段对象的字段才能被数据控件显示。
创建了永久字段对象后,可以在单元文件中找到该字段对象的声明,示例如下:
TypeTForm1 = Class(TForm)
Table1: TTable;
Button1: TButton;
Table1XXH1: TStringField;
Table1XXH2: TBytesField;
Procedure Button1Click(Sender: TObject);
Private{ Private declarations }
Public{ Public declarations }
End;
7.4.2 调整永久字段的顺序
永久字段对象在字段编辑器中的排列顺序就是这些字段在数据库栅格(TDBGrid)中的显示顺序。因此,要改变字段的显示顺序,可以在字段编辑器中改变永久字段对象的排列顺序,调整的办法是:先选择一个字段,然后用鼠标把它拖到另一个位置,也可以按Ctrl+Up键把字段的顺序上移,按Ctrl+Dn键把字段的顺序下移。
如果选择了多个字段,而且这些字段本来并不是连续的,把它们拖到一个新位置后,这些本来不连续的字段现在也变成连续的了。
7.4.3 增加新的字段
使用永久字段对象的另一个好处是,可以增加新的字段或者替换原有的字段。Delphi 4允许增加五种类型的字段:
.Data用于替换原有的字段,例如,改变字段的数据类型;
.Calculated增加一个“计算字段”,该字段的值由OnCalcFields事件给出;
.InternalCalc类似于“计算字段”,用于多层体系结构的客户程序中;
.Lookup增加一个“Lookup”字段;
.Aggregate增加一个“总计字段”。
上述五种类型的字段仅仅用于显示,它们的数据只在运行期有意义,不会影响原来的数据集。
要增加一个新的字段,在字段编辑器上单击鼠标右键,在弹出的菜单中选择“New Field”命令,弹出“New Field”对话框,如图7.4所示
图7.4 创建新字段对话框
“New Field”对话框由三部分组成:“Field Properties”分组框、“FieldType”分组框和“Lookup Definition”分组框。
“Field Type”分组框中有三个单选按钮,用于指定新创建的字段的生成类型(不是数据类型),默认的类型是“Data”,如果选择“Lookup”,下面的“LookupDefinition”分组框就被激活。此外,也可以选择“Calculated”表示要增加一个“计算字段”。
如果数据集是TClientDataSet,“Field Type”框中就多了两个单选按钮,一个是“InternalCalc”,另一个是“Aggregate”。
“Field Properties”分组框用于设置字段的属性,其中,“Name”框用于输入字段的名称,Delphi 4会根据输入的名称字段在“Component”框中生成字段对象的名称。“Type”框用于选择字段的数据类型,Delphi 4支持生成ADT类型的字段。“Size”框用于输入字段的长度,对于String、Bytes和VarBytes类型的字段必须输入长度,其他类型的字段则不必。
7.4.4 增加“Data”类型的字段
增加一个“Data”类型的字段主要是为了替换原有的某个字段,尤其是替换字段的数据类型。例如,假设一个字段的类型本来是TSmallIntField,现在想把它改成TIntegerField,由于通过编程是无法改变一个字段的数据类型的,只能先把这个字段删掉,再增加一个“Data”类型的字段,把它的数据类型设为Integer。
增加一个“Data”类型的字段的一般步骤是:
第一步是在字段编辑器中删除要被替代的永久字段,记住它的名称。
第二步是单击鼠标右键,在弹出的菜单中选择“New Field”命令,弹出“NewField”对话框。在“Name”框输入字段的名称,必须与要替换的字段的名称完全相同,这样才能从原来这个字段获取数据。
第三步是设置新字段的数据类型和长度,必须与原来的数据类型不同。对于String类型的字段来说,仅仅改变字段长度是不够的。要注意的是,数据类型既要有所区别,新的数据类型还必须与原有的数据兼容。
第四步是在“Field Type”分组框内选择“Data”,然后单击OK按钮。至此,就创建了一个新的字段,该字段将代替原有的某个字段。
7.4.5 增加“Calculated”类型的字段
“计算字段”值是在处理OnCalcFields事件的句柄中给出的。增加一个“Calculated”类型的字段的一般步骤是(前面几个公共的步骤省略):
第一步是在“Name”框内输入字段的名称,不能与已有字段的名称相同。
第二步是在“Type”框内选择字段的数据类型,在“Size”框内指定数据长度,如果字段的类型是TStringField、TBytesField或TVarBytesField的话。
第三步是在“Field Type”分组框内选择“Calculated”,然后单击OK按钮。
第四步是在窗体或数据模块上单击数据集构件,然后建立处理OnCalcFields事件的句柄,在句柄中给出“计算字段”的值,程序示例如下:
CityStateZip.Value := City.Value + ', ' + State.Value + ' ' + Zip.Value;
如果没有建立处理OnCalcFields事件的句柄,或者在句柄中没有给出“计算字段”的值,“计算字段”的值就是空的。
要说明的是,如果数据集是TClientDataSet或TQuery,还可以增加一个“InternalCalc”类型的字段。“InternalCalc”类型的字段与“Calculated”类型的字段非常相似,不同的是,对于TClientDataSet来说,“InternalCalc”类型的字段的值随其他字段的值一起被存取。
7.4.6 增加“Lookup”类型的字段
所谓“Lookup”类型的字段,就是它的值能且只能从另一个数据集中获取。
增加一个“Lookup”类型的字段的一般步骤是(前面几个公共的步骤省略):
第一步是在“Name”框内输入字段的名称,不能与已有字段的名称相同。
第二步是在“Type”框内选择字段的数据类型,在“Size”框内指定数据长度,如果字段的类型是TStringField、TBytesField或TVarBytesField的话。
第三步是在“Field Type”分组框内选择“Lookup”,此时将激活下面的“LookupDefinition”分组框。
第四步是在“Lookup Definition”分组框的“Dataset”框内选择一个数据集,这个数据集不能是“计算字段”所在的数据集,否则会触发异常。
第五步是在“Lookup Definition”分组框的“Key Fields”框内选择一个字段作为关键字段,如果要用几个字段作为关键字段,您可以直接在“Key Fields”框内输入这些字段的名称,彼此之间用分号隔开。作为关键字段的字段必须都建立了永久字段对象。
第六步是在“Lookup Definition”分组框的“Lookup Keys”框内选择一个字段作为关键字段,用来与第五步选择的字段匹配。注意:这里列出的是“Dataset”框内指定的数据集中的字段。如果要用几个字段作为关键字段,您可以直接在“Lookup Keys”框内输入这些字段的名称,彼此之间用分号隔开。
第七步是在“Lookup Definition”分组框的“Result Field”框内选择一个字段,该字段的值将返回给新增加的“Lookup”类型的字段。
如果既定义了“计算字段”,又定义了“Lookup”类型的字段,“Lookup”类型的字段的值将先确定,这样,在处理OnCalcFields事件的句柄中,可以引用“Lookup”类型的字段的值。
TField有一个LookupCache属性,对于“Lookup”类型的字段来说,如果把这个属性设为True,当第一次打开数据集时,把“Lookup”类型的字段的值放到缓存中,以后就可以直接从缓存中读取该字段的值,而不必从另一个数据集中去检索。显然,这可以提高应用程序的性能,尤其是当另一个数据集位于远程服务器上的时候。
不过,如果“KeyFields”框内指定的字段太多,最好把LookupCache属性设为False。
注意:如果是在运行期把LookupCache属性设为True,应当调用RefreshLookupList以初始化缓存。
这里顺便要介绍一个技巧,可以通过编程为“Lookup”类型的字段提供数据,这样可以避开从另一个数据集中获取数据。首先要创建一个TLookupList的对象实例,调用Add建立一组数据的列表。然后设置字段的LookupList属性指定TLookupList的对象实例,并且把LookupCache属性设为True。以后,“Lookup”类型的字段的值就从列表中读取。
7.4.7 增加“Aggregate”类型的字段
只能在TClientDataSet建立的数据集中增加“Aggregate”类型的字段,其一般步骤如下:
第一步是在“Name”框内输入字段的名称,不能与已有字段的名称相同。
第二步是在“Type”框内选择字段的数据类型,一般选“Aggregate”。
第三步是在“Field Type”分组框内选择“Aggregate”,然后单击OK按钮。
第四步是在字段编辑器中单击刚刚建立的“Aggregate”类型的字段,设置Expression属性指定一个表达式。
7.4.8 删除永久字段对象
要删除一个永久字段对象,首先要打开字段编辑器,然后选择要删除的字段,按下Del键。也可以用鼠标右键单击要删除的字段,在弹出的菜单中选择“Delete”命令。
对于永久字段对象来说,一旦被删掉,应用程序就不再能访问它。如果以后又想恢复,您只能重新创建一个新的永久字段对象,原先的属性和事件句柄都丢失了。
如果把所有的永久字段对象都删掉,下次打开数据集时,就会自动创建动态字段对象。
7.5 设置永久字段对象的属性
使用永久字段对象的一个优势是可以在设计期设置它的属性、建立事件句柄。例如,可以设置字段的显示宽度、它的值是否能修改。
要设置永久字段对象的属性和建立事件句柄,首先要打开字段编辑器,在字段编辑器中选择一个字段,然后在对象观察器中进行有关操作。
7.5.1 设置字段的显示和编辑属性
TField提供了若干个属性由于设置字段的显示和编辑属性,这些属性如下所列:
.Alignment设置字段在数据控件中的对齐方式(左、居中、右);
.ConstraintErrorMessage当用户的输入违反纠错规则时显示由此属性指定的信息;
.CustomConstraint设置一个本地使用的纠错规则;
.Currency设为True表示按货币格式显示;
.DisplayFormat设置字段在数据控件中的显示格式;
.DisplayLabel设置字段在数据库栅格中的列标签;
.DisplayWidth设置字段在数据控件中的显示宽度;
.EditFormat设置字段在编辑时的显示格式;
.EditMask设置用户编辑数据时必须遵守的规则;
.FieldKind指定字段的生成类型;
.FieldName指定字段的名称;
.HasConstraints如果字段已经有纠错规则,这个属性就返回True;
.ImportedConstraint返回服务器端或数据字典中的纠错规则;
.Index指定字段在数据集中的序号;
.LookupDataSet指定另一个数据集,以从中查找字段的值;
.LookupKeyFields指定一个或几个字段,用于与关键字段匹配;
.LookupResultField指定一个字段,该字段的值将复制给Lookup字段;
.MaxValue指定字段的最大值;
.MinValue指定字段的最小值;
.Name指定永久字段对象的内部名称如Table1XXH;
.Origin返回字段在数据集中原先的名称;
.Precision指定字段的有效位;
.ReadOnly设为True表示字段的值是只读的;
.Size指定字段的长度(以字符为单位);
.Tag给每个字段对象一个识别号;
.Transliterate设为True表示将根据驱动程序进行转换;
.Visible设为False表示这个字段不出现在数据控件中。
要说明的是,有些属性并不适用于某些类型的字段,例如,对于一个TStringField类型的字段来说,它就没有Currency、MaxValue、DisplayFormat等属性,对于一个TFloatField类型的字段来说,它就没有Size属性。
上述属性也可以在运行期设置,例如,要设置Customers表的CityStateZip字段的ReadOnly属性,程序这么写:
CustomersCityStateZip.ReadOnly := True;
注意:不能用字段的名称来访问上述属性,而要用字段对象的名称来访问。
7.5.2 设置用户的输入格式
EditMask属性仅适用于TStringField、TDateField、TTimeField和TDateTimeField类型的字段,用于设置用户的输入格式。
要设置EditMask属性,可以在对象观察器中单击EditMask属性边上的省略号按钮打开“Input Mask Editor”对话框,如图7.5所示。
图7.5 设置EditMask属性对话框
既可以在“Sample Masks”框内选择一种现成的格式,也可以自定义格式。单击“Masks”按钮可以选择更多的格式。
7.5.3 使用默认的显示格式
对于TFloatField、TCurrencyField、TIntegerField、TSmallIntField、TWordField、TDateField、TDateTimeField和TTimeField类型的字段来说,Delphi 4提供了若干个例程,能够使这些字段按默认的格式显示。这些例程都是在SysUtils单元中声明的,包括:
.FormatFloat适合于TFloatField和TCurrencyField;
.FormatDateTime适合于TDateField、TTimeField和TDateTimeField;
.FormatCurr适合于TCurrencyField。
所谓默认的格式,实际上是在“控制面板”中设置的。可以打开“控制面板”,双击“区域设置”的图标,就会看到那儿设置了有关数字、货币、时间和日期的格式。例如,如果区域是United States,对于TFloatField类型的字段来说,它的显示格式就是$1234.56。
当然,无论是在设计期还是在运行期,都可以重新设置DisplayFormat属性和EditFormat属性指定字段的显示格式。也可以在处理OnGetText事件和OnSetText事件的句柄中自定义字段的显示格式。
7.5.4 处理事件
就像大多数构件一样,永久字段对象也有事件,可以在设计期建立处理事件的句柄。下面列出了TField的事件:
.OnChange当字段的值改变的时候触发这个事件;
.OnGetText当程序试图检索字段的值时将触发这个事件;
.OnSetText当程序试图设置字段的值时将触发这个事件;
.OnValidate当字段的值将要写到记录缓冲区中时将触发这个事件。
其中,OnGetText事件和OnSetText事件用于设置字段的显示格式,OnChange事件用于对数据的改变作出反应,例如禁止或允许某个菜单项。OnValidate事件对字段的值进行校验。
7.5.5 调用方法
下面用于列出了TField的方法:
.AssignValue用于对字段赋值,它会自动进行类型转换;
.Clear把字段的值清为NULL;
.GetData把未格式化的数据写到一个缓冲区中;
.IsValidChar判断某个字符对字段来说是否合法;
.SetData把缓冲区中未格式化的数据赋值给字段;
.FocusControl把输入焦点移到字段所在的数据控件上。如果窗体上有几个数据控件显示一个字段的值,FocusControl会把输入焦点移到第一个显示该字段的数据控件上。
7.6 属 性 集
如果多个字段的显示属性是相同的或大致相同的,不必重复它们的属性,可以设置一个字段的属性如Alignment、DisplayWidth、DisplayFormat、MaxValue、MinValue,然后建立一个属性集加到数据字典中。这样,其他字段就可以从数据字段中引入属性集。
要建立一个属性集,首先要打开字段编辑器,选择一个永久字段,然后在对象观察器中设置有关属性。设置好后,在字段编辑器上单击鼠标右键,在弹出的菜单中选择“Save Attributes”命令,弹出“Save Attributes As”对话框,如图7.6所示。
图7.6 保存属性集
默认情况下,属性集的名称是表名加字段名,例如CustomCompany,可以改变这个名称。
也可以在SQL Explorer中创建属性集。在SQL Explorer中创建属性集的好处是,您可以指定字段的类型,而且还可以指定一个数据控件如TDBEdit、TDBCheckBox等,当基于该属性集的字段拖到窗体上时会自动把这个数据控件加到窗体上。
建立了属性集后,其他字段就可以引入这个属性集。首先也要打开字段编辑器,选择要引入属性集的字段,单击鼠标右键,在弹出的菜单中选择“Associate Attributes”命令,弹出“Associate Attributes”对话框,如图7.7所示。
图7.7 引入属性集
选择一个属性集,单击OK按钮,该属性集中的属性将应用到刚才选择的字段中,这就是所谓的引入属性集。
如果以后又不想使用该属性集中的属性,还得打开字段编辑器,选择一个字段,然后单击鼠标右键,在弹出的菜单中选择“Unassociate Attributes”。
7.7 显示、转换和访问字段的值
数据控件能够自动显示字段的值,在允许编辑的情况下,用户可以在数据控件中修改字段的值,并且把修改后的值写回到数据集中,这一切都不需要写代码。
应用程序可以通过TField的Value属性访问字段的值,例如,下面这行代码把CustomersCompany字段的值显示在编辑框Edit1中:
Edit1.Text := CustomersCompany.Value;
从理论上讲,Value属性可以访问任何数据类型的字段的值。不过,很多数据控件只能传递字符串类型的值,因此,需要对Value属性进行转换。
幸运的是,TField中提供了众多的转换功能,可以按特定的数据类型来访问一个字段的值,例如,AsString可以把数字或布尔型的字段按字符串类型来访问。
AsVariant适用于所有的数据类型。要说明的是,转换并不总是成功的,例如,AsDateTime可以把一个字符串转换为日期、时间,但该字符串本身必须能识别为日期时间格式,而不能是一个普通的字符串。有些情况下,虽然转换可以进行,但有可能丢失精度。
用Value属性访问字段的值需要事先知道字段对象的名称,而很多情况下,往往并不知道字段对象的名称,但字段的名称却是知道的。因此,应用程序往往通过字段的序号或名称来访问字段的值。
数据集构件都有一个Fields属性,这是一个数组,它的每一个元素代表一个字段,这样就可以按序号来访问某个字段的值,序号是从0开始的。例如,下面的代码把Customers表的第7个字段显示到编辑框Edit1中:
Edit1.Text := CustTable.Fields[6].AsString;
下面的代码把第7个字段的值设为编辑框Edit1中的内容:
Customers.Edit;
Customers.Fields[6].AsString := Edit1.Text;
Customers.Post;
不过,按序号来访问字段的值不是很安全,因为如果记错了序号,就得不到正确的结果。因此,我们推荐按字段名称来访问字段的值,这就要用到FieldByName函数。
要调用FieldByName函数,得把字段的名称作为参数传递给FieldByName函数。例如,下面的代码把Customers表的CustNo字段显示到编辑框Edit2中:
Edit2.Text := Customers.FieldByName('CustNo').AsString;
下面的代码把CustNo字段的值设为编辑框Edit2中的内容:
Customers.Edit;
Customers.FieldByName('CustNo').AsString := Edit2.Text;
Customers.Post;
在多层体系结构的应用程序中,客户程序向应用服务器申请更新数据时有可能出错,这时候可以用CurValue属性检查字段当前的值,因为字段的值是随时有可能修改的,尤其是在多用户的环境下,因此,知道字段当前的值是很有必要的。
7.8 字段级纠错
所谓字段级纠错,就是事先定义一些规则或条件,字段的值必须符合这些规则或条件,否则就认为出错。字段对象可以借用SQL服务器纠错,也可以建立自定义的纠错。
大多数SQL服务器都定义了纠错功能,而客户程序则可以借用服务器纠错。TField的ImportedConstraint属性可以返回服务器的纠错规则,实际上是一个字符串,其语法类似于SQL语句,例如:
Value > 0 and Value < 100
如果服务器的纠错仍然不能满足要求的话,可以用CustomConstraint属性自己定义纠错规则。这个属性与ImportedConstraint属性不同的是,如果服务器端的纠错规则发生改变,ImportedConstraint属性也将改变,而自定义的纠错规则是不变的。
自定义的纠错只适用于本应用程序,并且只对用户输入的数据进行校验,对发向应用服务器或从应用服务器接收到的数据无能为力。
要自定义纠错规则,就要用到CustomConstraint属性指定纠错规则,并且设置ConstraintErrorMessage属性指定一个信息,当字段的值违反纠错规则时就会显示这个信息。自定义的纠错规则类似于SQL语句的Where部分,例如:X > 0 and X < 100其中,X只是一个符号,用来引用字段的值,符号本身是无关紧要的,只要在纠错表达式中保持一致就行了。
7.9 Oracle 8的对象字段
Delphi 4是发挥Oracle 8对象关联功能的最佳工具,它支持四种最新的字段,包括ADT(抽象数据类型)、数组、数据集(嵌套表)、引用,它们所对应的字段类型如下所列:
.TADTField代表一个ADT字段;
.TArrayField代表一个数组字段;
.TDataSetField代表一个数据集字段;
.TReferenceField代表一个引用字段,指向另一个ADT字段。
上述四种类型的字段统称为对象字段,它们的一个基本特征是包含或引用了另一个字段。如果一个数据集包含对象字段,只要在设计期创建了对象字段的永久字段对象,就会自动为这些对象字段所包含或引用的子字段创建永久字段对象。同时,数据集构件的ObjectView属性自动设为True。在ObjectView属性设为True的情况下,数据集中的字段按照它们的继承关系以树状存储,而不是按原先的序号线性存储。
下面列出了对象字段所特有的一些属性和方法,用于操纵它所包含或引用的子字段:
.Fields用这个属性可以访问对象字段包含或引用的子字段;
.ObjectType返回对象的类型;
.FieldCount返回对象字段所包含或引用的子字段个数;
.FieldValues用这个属性可以访问对象字段包含或引用的子字段的值。
7.9.1 ADT字段
ADT是一种用户自定义的字段类型,有点类似于结构,它可以包含大多数类型的字段,包括数组、引用和嵌套表。
要访问一个ADT类型的字段有几种方式,最好先要创建永久字段对象。假设一个ADT类型的字段叫Address,它包含了四个子字段,分别是Street、City、State、Zip。如果为Address字段创建了永久字段对象的话,将看到下列永久字段对象:
CustomerAddress: TADTField;
CustomerAddrStreet: TStringField;
CustomerAddrCity: TStringField;
CustomerAddrState: TStringField;
CustomerAddrZip: TStringField;
这样,就可以把对象字段的子字段当作一个普通的字段使用,例如:
CityEdit.Text := CustomerAddrCity.AsString;
不过,上述代码假设已经创建了对象字段的永久字段对象,如果没有创建永久字段,就要用下面的方式来访问对象字段的子字段:
CityEdit.Text := Customer.FieldByName('Address.City').AsString;
可以用TADTField的FieldValues属性访问子字段的值,例如:
CityEdit.Text := TAdtField(Customer.FieldByName('Address'))[1];
由于FieldValues属性是TObjectField的默认属性,因此,上述代码可以改为:
CityEdit.Text := TAdtField(Customer.FieldByName('Address')).FieldValues[1];
上述代码也可以改为用Fields属性,例如:CityEdit.Text := TAdtField(Customer.FieldByName('Address').Fields[1].AsString;
甚至还可以这样写:CityEdit.Text :=TADTField(Customer.FieldByName('Address').Fields.FieldByName('City').AsString;
由此可见,通过永久字段对象来访问对象字段的子字段是最简便的。此外,通过数据集构件的FieldValues属性也可以访问ADT字段,例如:
Customer.Edit;Customer['Address.City'] := CityEdit.Text;Customer.Post;
下面这行代码把Address字段的City子字段的值显示到编辑框CityEdit中:
CityEdit.Text := Customer['Address.City'];
7.9.2 数组字段
所谓数组字段,其概念类似于数组,它所包含的子字段的类型是相同的,而ADT字段所包含的子字段的类型可以是不同的。数组字段的子字段可以是普通的字段,也可以是ADT字段,但不能是另一个数组字段。
要访问数据集中的数组字段有几种方式,下面是一种典型的访问方式:
var
OrderDates: TArrayField;
I: Integer;
Begin
For I := 0 to OrderDates.Size - 1 Do
Begin
If OrderDates.Fields[I].Is Null then Break;
OrderDateListBox.Items.Add(OrderDates[I]);
End;
End;
假设有一个数组字段叫TelNos_Array,它有6个字符串类型的子字段。当为TelNos_Array字段创建永久字段对象时,同时将创建6个子字段的永久字段对象:
CustomerTelNos_Array: TArrayField;
CustomerTelNos_Array0: TStringField;
CustomerTelNos_Array1: TStringField;
CustomerTelNos_Array2: TStringField;
CustomerTelNos_Array3: TStringField;
CustomerTelNos_Array4: TStringField;
CustomerTelNos_Array5: TStringField;
下面这行代码访问其中的第一个子字段:
TelEdit.Text := CustomerTelNos_Array0.AsString;
如果没有为数组字段创建永久字段对象,只能通过FieldValues属性来访问子字段的值,但必须把数据集构件的ObjectView属性设为True。例如:
TelEdit.Text := TArrayField(Table1.FieldByName('TelNos_Array')).FieldValues[1];
由于FieldValues属性是TObjectField的默认属性,因此,上述代码可以改为:
TelEdit.Text := TArrayField(Table1.FieldByName('TelNos_Array'))[1];
上述代码也可以这样写:TelEdit.Text := TArrayField(Customer.FieldByName('TelNos_Array').Fields[1].AsString;
7.9.3 数据集字段
通过数据集字段,可以非常简单地维护表格与表格之间的一对多关系。通过TDataSetField的NestedDataSet属性可以访问数据集字段所嵌套的子数据集。
为了支持数据集字段,Delphi 4的TDBGrid构件作了改写,凡是显示数据集字段的栏都有一个省略号按钮,用户单击这个省略号将打开一个新的窗口,这个窗口也用一个TDBGrid构件显示嵌套表的数据。当然,要打开这个窗口,也可以调用TDBGrid的ShowPopupEditor函数,例如:
DBGrid1.ShowPopUpEditor(DbGrid1.Columns[7]);
要访问嵌套表中的数据,最好要为数据集字段创建永久字段对象,然后用TNestedTable或TClientDataSet的DatasetField属性指定这个字段。
7.9.4 引用字段
引用字段所表达和存储的是一个指针,指向另一个ADT对象,这个ADT对象通常是另一个数据集中的一条记录。被引用的ADT对象中的数据可以像嵌套表一样来访问,也可以通过TReferenceField的Fields属性来访问。
与数据集字段一样,在TDBGrid构件中,凡是显示引用字段的栏都有一个省略号按钮,用户单击这个省略号按钮将打开一个新的窗口,这个窗口也用栅格显示被引用的ADT对象中的数据。要打开这个窗口,也可以调用TDBGrid的ShowPopupEditor函数,例如:
DBGrid1.ShowPopUpEditor(DbGrid1.columns[7]);
要访问被引用的ADT对象中的数据,首先要为引用字段创建永久字段对象,然后然后用TNestedTable或TClientDataSet的DatasetField属性指定这个字段。
假设一个引用字段对象叫CustomerRefCity,下面的代码访问它所引用的ADT对象中的某个字段,两行代码的效果是一样的。
CityEdit.Text := CustomerRefCity.Fields[1].AsString;
CityEdit.Text := CustomerRefCity.NestedDataSet.Fields[1].AsString;
要对引用字段赋值,需要用SQL SELECT语句,程序示例如下:
var
AddressQuery: TQuery;
CustomerAddressRef: TReferenceField;
Begin
Address.SQL.Text :='SELECT REF(A) FROM AddressTable A WHERE A.City = "San Francisco"';
AddressQuery.Open;CustomerAddressRef.Assign(AddressQuery.Fields[0]);
End;
7.9.5 显示ADT字段和数组字段
ADT字段和数组字段具有一个共同的特点,它们都包含子字段。Delphi 4对一些数据控件作了改写,像TDBEdit、TDBGrid都支持ADT字段和数组字段。
对于那些具有DataField属性的数据控件来说,它们显示ADT字段和数组字段的值时,会自动用一个下拉列表显示它们的子字段的值。
TDBGrid怎样显示ADT字段和数组字段的值取决于数据集构件的ObjectView属性怎样设置。如果ObjectView属性设为False,每个子字段集中显示在一栏中。如果ObjectView属性设为True,显示ADT字段和数组字段的栏将出现一个下拉的箭头,单击此箭头将使栅格扩展,显示所有子字段的值。