後一頁 前一頁 回目錄 回首頁 |
利用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中獲得的雇員號碼,用戶可以從中選擇。
|
後一頁 前一頁 回目錄 回首頁 |