Data-Browse型Data-Aware控件的制作
在MIS系統中,使用得最常見的當屬數據感知控件了。學習如何編寫自已的組件,一條很重要的原則就是從自己熟悉的
組件入手,派生出新的合乎自己要求的控件。在數據感知控件中,Data-Browse型是非常簡單而又實用的。所以下面
我們就講解一個一個自定義的TSunDBText(功能等同於TDBText)的編寫:
TSunDBText = class(TCustomLabel)
{...}
end;
因為DBText僅起著顯示數據的作用,即是個具有數據感知功能的標簽,又根據Delphi的習慣,從Custom系列控件派生,
所以我們選擇了TCustomLabel。
如何具有數據感知功能,其實大部分的書上都沒有講得很明白,包括Delphi Help。大致上有這麼幾點,我們慢慢敘述:
一:選擇一個能數據感知的DataLink對象,通常我們選用TFieldDataLink,表示僅與一個數據庫字段關聯
二:組件必須提供DataSource和DataField兩個設計時可讀寫屬性
三:組件必須處理FDataLink的OnDataChange事件,以反映數據字段的變化
四:必須提供Notification重載方法,以便當關聯的TDataSource組件在設計期或者運行期刪除時,能得到通知,並且
反映出這種改變
五:習慣上,我們還要處理CM_GETDATALINK消息,以符合VCL內部通信的要求
六:為了組件的靈活性,建議提供一個Field屬性,以便可以利用TField的方法來增強編程的適用性
下面我們就來一一講解:
private
FDataLink:TFieldDataLink;
constructor Create(AOwner:TComponent);override;
begin
inherited;
FDataLink := TFieldDataLink.Create; {創建一個TFieldDataLink的對象}
FDataLink.OnDataChange := DataChange;
{
將TFieldDataLink對象的OnDataChange事件處理交給TSunDBText的DataChange來處理
這樣就要以感知數據庫字段的變化,並且進行自己想要的處理
}
end;
destructor Destroy;override;
begin
{
這裡很簡單,僅僅是回收資源而已
}
FDataLink.Free;
FDataLink := nil;
inherited;
end;
從上面我們已經看到,數據感知的關鍵就在於DataChange事件,大致上我們可以知道,是需要將該字段的顯示值賦予
本標簽組件的Caption屬性即可,所以下面的代碼便是做這部分工作的。
(注意,之所以要用GetFieldText來完成賦值工作,是出於多方面的考慮,包括設計期和運行期,及異常情況)
procedure TSunDBText.DataChange(Sender: TObject);
begin
Caption := GetFieldText; {read only displaytext when make an data browing component}
end;
function TSunDBText.GetFieldText: String;
begin
{
正常情況下,只需要取得FDataLink所代表的Field的DisplayText即可,如果你開發的不是Data-Browse控件,請用其他
Field屬性
}
if(FDataLink <> nil) then
Result := FDataLink.Field.DisplayText
else if(csDesigning in ComponentState) then Result := Name else Result := ;
{
如果是處在設計期,則標簽中顯示的是組件的名稱,如果是運行期,則標簽為空;當然,這只是習慣而已,但對於使用Delphi IDE的人來說,
幾乎就是規則。
}
end;
如果沒有DataSource和DataField這兩個屬性,那麼控件幾乎就沒法使用了。這也是重要步驟。
property DataSource:TDataSource read GetDataSource write SetDataSource;
property DataField:String read GetDataField write SetDataField;
這些方法十分簡單,只要存取TFieldDataLink之DataSource和FieldName屬性則可。
function TSunDBText.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
{僅需簡單地返回TFieldDataLink之DataSource屬性
從這就可以看出,DataSource屬性的寫方法也是對FDataLink賦值}
end;
procedure TSunDBText.SetDataSource(Value: TDataSource);
begin
FDataLink.DataSource := Value; {如上所雲,對DataSource屬性賦值}
if(Value <> nil) then Value.FreeNotification(self);
{
這裡很重要,TComponent提供了一個FreeNotification(AComponent:TComponent)方法,這樣,當Value釋放時,就會自動調用AComponent的Notification
方法,我們就可以調整對應的Data-Browse控件,以反映這種變化。如TSunDBText對應的DataSource組件刪除掉,則應該反映在標簽上
}
end;
function TSunDBText.GetDataField: String;
begin
Result := FDataLink.FieldName; {獲得TFieldDataLink之FieldName屬性,也就知道了連接的是哪一個數據庫字段名稱}
end;
procedure TSunDBText.SetDataField(const Value: String);
begin
FDataLink.FieldName := Value; {這應該不用解釋了}
end;
接上面的,我們說過Notification的由來,下面就進行相關的處理:
procedure TSunDBText.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if(Operation = opRemove) and (FDataLink <> nil) and (AComponent = DataSource) then
DataSource := nil;
end;
當關聯組件被刪除時,而且被刪除的正好是本身關聯的DataSource組件,就應該設置其DataSource屬性為空
function TSunDBText.GetField: TField;
begin
Result := FDataLink.Field;
end;
最後,數據感知控件還要響應CM_GETDATALINK消息。通常處理是將TDataLink作為Message的Result域返回
procedure CMGetDataLink(var Message:TMessage);message CM_GETDATALINK;
procedure CMGetDataLink(var Message:TMessage);
begin
Message.Result := Integer(FDataLink);
end;
到此,基本上組件的功能就已齊備了,然而,有一點許多組件編寫者都沒有注意到,即Action的應用。如果自定義組件能融入VCL的Action機制,
必然可以使自定義控件的功能更強大,更為一些高級使用者喜歡。
下一次,我們就來講一講Action之來龍去脈。