多層資料庫開發七:字 段 對 象
來源:互聯網
上載者:User
第七章 字 段 對 象
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屬性訪問欄位的值需要事Crowdsourced Security Testing道欄位對象的名稱,而很多情況下,往往並不知道欄位對象的名稱,但欄位的名稱卻是知道的。因此,應用程式往往通過欄位的序號或名稱來訪問欄位的值。
資料集構件都有一個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欄位和數組欄位的欄將出現一個下拉的箭頭,單擊此箭頭將使柵格擴充,顯示所有子欄位的值。