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中獲得的雇員號碼,用戶可以從中選擇。