?
六、控件手拉手――控件關聯的實現
控件的關聯在Delphi中也是很常見的,我們可以設定一個控件的某個屬性指向另一個控件。比如我們在窗體上放上Tedit,TpopupMenu兩個控件,然后設定Tedit的PopupMenu屬性為TpopupMenu控件,運行后在Tedit點擊右鍵就會彈出剛才設定的那個TpopupMenu菜單,也就是說Tedit,TpopupMenu聯手完成了任務。再比如TDBEdit控件的DataSource屬性就可以指向一個TdataSource控件,這樣就可以在TDBEdit控件中顯示TdataSource輸出的某個字段的值了。
? 下面我們將寫一個簡單的實現控件關聯的控件。這個控件派生于Tedit,它可以與一個Tlabel控件關聯,在控件的編輯框中輸入文字時,與它關聯的Tlabel控件的文字將隨著它而變化。代碼如下:
unit MyEdit;
interface
uses
? SysUtils, Classes, Controls, StdCtrls;
type
? TMyEdit = class(TEdit)
? private
??? FLinkLabel: TLabel;
??? procedure FSetLinkLabel(AValue: TLabel);
? protected
??? procedure Notification(AComponent: TComponent;Operation: TOperation);
????????? override;
??? procedure Change;override;
? public
? published
??? property LinkLabel: TLabel read FLinkLabel write FSetLinkLabel;
? end;
?
procedure Register;
?
implementation
procedure Register;
begin
? RegisterComponents('Linco', [TMyEdit]);
end;
procedure TMyEdit.Change;
begin
? inherited;
? if LinkLabel <> nil then
??? LinkLabel.Caption := Text;
end;
?
procedure TMyEdit.FSetLinkLabel(AValue: TLabel);
begin
? FLinkLabel := AValue;
? if AValue <> nil then
??? FLinkLabel.FreeNotification(self);
end;
?
procedure TMyEdit.Notification(AComponent: TComponent;
? Operation: TOperation);
begin
? inherited;
? if (Operation = opRemove) and (AComponent = LinkLabel) then
??? LinkLabel := nil;
end;
end.
代碼解釋:
(1)、我們只要將控件的任意一個屬性的類型設定為另外一個控件的類名稱,那么我們就可以在控件的Object Inspector中將這個屬性指向那個控件(或那個控件的派生控件)的一個實例。比如本例中我們增加了LinkLabel屬性,它的類型為 Tlabel,所以我們就可以把LinkLabel屬性指向一個標簽控件。
(2)、請注意FsetLinkLabel中的這段代碼:
?if AValue <> nil then
FLinkLabel.FreeNotification(self);
如果我們將控件關聯屬性指向了一個控件,可是后來又將被指向的控件刪除了,那么我們的控件關聯屬性是不會自動刪除的,這樣就會造成控件關聯屬性指向的控件不存在的現象。我們必須自動感知被關聯控件的刪除并重新設定控件關聯屬性為不指向任何控件,這樣就避免了錯誤的發生。
FLinkLabel.FreeNotification(self);的作用就是這樣的。它調用控件的FreeNotification方法(在Tcomponent中定義)向被指向的控件注冊一個“消息”,當被指向控件被刪除時,會向所有向他注冊的控件發送一個它被刪除的消息,此時向他注冊的控件就會觸發Notification方法,這樣我們就可以自動感知被指向控件的狀態了。這是設計模式中Observer(觀察者)模式的典型應用。
既然向他注冊的控件就會觸發Notification方法,我們就覆蓋父類的Notification方法,寫出如下的代碼:
? if (Operation = opRemove) and (AComponent = LinkLabel) then
??? LinkLabel := nil;
這句話的意思是:如果控件被刪除并且被刪除的控件(因為我們的控件可能向多個控件注冊了消息)是LinkLabel,那么我們就設定LinkLabel屬性不指向任何控件。
(3)覆蓋父類的Change調度方法。在此方法里為連接的LinkLabel的Caption賦值就達到我們的目的了。
思考題:
1、做一個Label控件,給它增加一個DataSource屬性,該屬性可以指向一個TdataSource類型的控件,它有一個GetRecordCount方法。當調用此方法時,就在Label控件中顯示這個DataSource對應的數據集中的記錄的條數。