?
五、復合控件
復合控件是Delphi控件中非常重要的一種控件,復合控件就是將兩個或兩個以上的控件重新組合成一個新的控件。例如TspinEdit、TlabeledEdit、TDBNavigator等就是復合控件,TDBNavigator其實就是在一個Panel放上若干個Button而已。制作一個復合控件時,我們一般從TwinControl派生控件。
我們這次做的控件是擁有一個Edit編輯框和一個Button按鈕的復合控件,在用戶在編輯框中輸入文字的過程中,Button將隨時顯示編輯框中文字的長度。我們把控件的源碼先展示給大家。
unit EditButton;
interface
uses
? SysUtils, Classes, Controls, StdCtrls, Messages;
type
? TEditButton = class(TWinControl)
? private
??? FEdit: TEdit;
??? FButton: TButton;
??? FText: string;
??? procedure FSetText(AValue: string);
??? procedure OnEditChange(Sender: TObject);
? protected
??? procedure WMSize(var Msg: TMessage);message WM_SIZE;
? public
??? constructor Create(AOwner: TComponent);override;
??? destructor Destroy;override;
? published
??? property Text: string read FText write FSetText;
? end;
?
procedure Register;
implementation
?
procedure Register;
begin
? RegisterComponents('Linco', [TEditButton]);
end;
?
constructor TEditButton.Create(AOwner: TComponent);
begin
? inherited;
? FEdit := TEdit.Create(nil);
? FEdit.Parent := self;
? FEdit.Top := 0;
? FEdit.Left := 0;
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
? FEdit.OnChange := OnEditChange;
? FButton := TButton.Create(nil);
? FButton.Parent := self;
? FButton.Top := 0;
? FButton.Left := Width div 2;
? FButton.Height := Height;
? FButton.Width := Width div 2;
end;
?
destructor TEditButton.Destroy;
begin
? FEdit.Free;
? FButton.Free;
? inherited;
end;
?
procedure TEditButton.FSetText(AValue: string);
begin
? FEdit.Text := AValue;
end;
?
procedure TEditButton.OnEditChange(Sender: TObject);
begin
? FButton.Caption := IntToStr(Length(FEdit.Text));
end;
?
procedure TEditButton.WMSize(var Msg: TMessage);
begin
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
? FButton.Left := Width div 2;
? FButton.Height := Height;
? FButton.Width := Width div 2;
end;
end.
代碼解釋:
(1)、我們首先定義了兩個變量??
?? ?FEdit: TEdit;
??? FButton: TButton;
? 分別代表復合控件中的文字編輯框和按鈕。
(2)所謂復合控件說簡單一點就是在一個共同的基板上將組成復合控件的各個控件(可以叫做子控件)畫出來。所以我們在構造函數中建立各個子控件,然后分別設定它們的位置等屬性。
以文字編輯框為例:
FEdit := TEdit.Create(nil);
的作用是建立編輯框控件。如果Create的參數指定為nil,則子控件在設計狀態是可以響應用戶的操作的;而如果設定為self(即設定子控件的父控件為基板),則子控件在設計時時不可響應用戶操作的,如果設定為self則析構函數中就不用Fedit.Free來銷毀對象了,對象會自動銷毀。
? FEdit.Parent := self;的作用是設定子控件的父控件,如果沒有這一句則控件是無法顯示的。
? FEdit.Top := 0;
? FEdit.Left := 0;
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
這四句是設定控件在基板上的相對位置的,這里的Top,Left不是相對于窗體的,而是相對于基板的。
? FEdit.OnChange := OnEditChange;
則是設定編輯框控件的OnChange(文字改變事件)的處理句柄為OnEditChange;
(1)??? 用戶有可能在設計時或運行時通過代碼改變控件的大小,這時控件中子控件的順序就會變得亂七八糟,所以需要相應控件的WM_SIZE事件(控件大小發生變化的事件)重新設定子控件的位置,大小等。函數WMSize的作用就是這樣的。
安裝控件后發現控件已經可以正確運行了,但是還有一個問題,就是這個控件沒有了Onclick,Onchange等必須的屬性。我們只要為控件增加事件處理句柄屬性,然后把事件處理句柄屬性的讀寫方法都指向子控件的事件處理句柄屬性即可。例如我們為控件增加OnClick事件,這個事件發生在用戶單擊按鈕時,我么只要在Pulished部分增加如下代碼:
property? OnClick:? TnotifyEvent read GetOnClick write SetOnClick
在Private中增加如下方法聲明:
function GetOnclick: TnotifyEvent;
procedure SetOnclick(AValue: TnotifyEvent);
這兩個方法的實現分別為:
function? TeditButton. GetOnclick: TnotifyEvent;
begin
? result := Fbutton.Onclick;
end;
procedure TeditButton. SetOnclick(AValue: TnotifyEvent);
begin
? Fbutton.OnClick := Avalue;
end;
思考題:
1、做一個模仿播放器中的操作按鈕的復合控件,控件由三個按鈕組成,分別是“播放”、“暫停”、“停止”,請按照正常的邏輯關系,處理這三個按鈕的可用/不可用關系。(提示:可以參考TDBNavigator的源代碼)