這三句表達式使用RegisterPropertyEditor三種不同的用法:
● 第一種最典型
它注冊了用於所有TComponent類型屬性的屬性編輯器TComponentProperty。通常,當為某種類型屬性注冊屬性編輯器時,它就能應用於所有這種類型的屬性,因此,第二和第三個參數為nil。
● 第二個表達式注冊特定類型的屬性編輯器
它為特定部件的特定屬性注冊屬性編輯器,在這種情況下,編輯器用於所有部件的Name屬性。
● 第三個表達式介於第一個和第二個表達式之間
它為部件TMenu的TMenuItem類型的所有屬性注冊了屬性編輯器。
19.2.2.2 創建事件
事件是部件的很重要的部分。事件是部件必須響應的系統事件與響應事件的一段代碼的聯接。響應代碼被稱為事件處理過程,它總是由部件用戶來編寫。通過使用事件,應用開發者不需要改變部件本身就能定制部件的行為。作為部件編寫者,運用事件能使應用發者定制所有的標准Delphi部件。要創建事件,應當理解:
● 什麼是事件
● 怎樣實現標准事件
● 怎樣定義自己的事件
1. 什麼是事件
事件是聯接發生的事情與某些代碼的機制,或者說是方法指針,一個指向特定對象實例的特定方法的指針。從部件用戶的角度,事件是與系統事件(如OnClick)有關的名稱,用戶能給該事件賦特定的方法供調用。例如,按鈕Buttonl有OnClick方法,缺省情況下Delphi在包含該按鈕的窗體中產生一個為ButtonlClick的方法,並將其賦給OnClick。當一個Click事件發生在按鈕上時,按鈕調用賦給OnClick的方法ButtonlClick:
部件用戶將事件看作是由用戶編寫的代碼,而事件發生時由系統調用的處理辦法。
從部件編寫者角度事件有更多的含義。最重要的是提供了一個讓用戶編寫代碼響應特定事情的場所。
要編寫一個事件,應當理解:
● 事件和方法指針
● 事件是屬性
● 事件處理過程類型
● 事件處理過程是可選的
⑴ 事件是方法指針
Delphi使用方法指針實現事件。一個方法指針是指向特定對象實例的特定方法的特定指針。作為部件編寫者,能將方法指針作為一種容器。你的代碼一發現事情發生,就調用由用戶定義的方法。
方法指針的工作方式就象其它的過程類型,但它們保持一個隱含的指向對象實例的指針。所有的控制都繼承了一個名為Click的方法,以處理Click事件。Click方法調用用戶的Click事件處理過程。
procedure TControl.Click;
begin
if Assigned(OnClick ) then OnClick( Self );
end;
如果用戶給Control的OnClick事件賦了處理過程(Handle),那鼠標點按Control時將導致方法被調用。
⑵ 事件是屬性
部件采用屬性的形式實現事件。不象大多數其它屬性,事件不使用方法來使實現read和write部分。事件屬性使用了相同類型的私有對象域作為屬性。按約定域名在屬性名前加“F”。例如OnClick方法的指針,存在TNotifyEvent類型FOnClick域中。OnClick事件屬性的聲明如下:
type
TControl=class ( TComponent )
private
FOnClick: TNofiFyEvent; { 聲明保存方法指針的域 }
protected
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
象其它類型的屬性一樣,你能在運行時設置和改變事件的值。將事件做成屬性的主要好處是部件用戶能在設計時使用Object Inspector設置事件處理過程。
⑶ 事件處理過程類型
因為一個事件是指向事件處理過程的指針,因此事件屬性必須是方法指針類型,被用作事件處理過程的代碼,必須是相應的對象的方法。
所有的事件方法都是過程。為了與所給類型的事件兼容,一個事件處理過程必須有相同數目和相同類型的相同順序的參數。Delphi定義了所有標准事件處理過程的方法類型,當你創建自己的事件時,你能使用已有的事件類型,或創建新的。雖然不能用函數做事件處理過程,但可以用var參數得到返回信息。
在事件處理過程中傳遞var參數的典型例子是TKeyPressEvent類型的KeyPressed事件。TKeyPressEvent定義中含有兩個參數。一個指示哪個對象產生該事件。另一個指示那個鍵按下:
type
TKeyPressEvent=procedure( Sender: TObject; var key: char) of Object;
通常key參數包含用戶按下鍵的字符。在某些情況下,部件的用戶可能想改變字符值。例如在編輯器中強制所有字符為大寫,在這種情況下,用戶能定義下列的事件處理過程:
procedure TForml.EditlKeyPressed( Sender: TObject; var key: char);
begin
key := Upcase( key );
end;
也可使用var參數讓用戶覆蓋缺省的處理。
⑷ 事件處理過程是可選的
在為部件創建事件時要記住部件用戶可能並不編寫該事件的處理過程。這意味著你的部件不能因為部件用戶沒有編寫處理代碼而出錯。這種事件處理過程的可選性有兩個方面:
① 部件用戶並非不得不處理事件
事件總是不斷地發生在Windows應用程序中。例如,在部件上方移動鼠標就引起Windows發送大量的Mouse-Move消息給部件,部件將鼠標消息傳給OnMouseMove事件。在大多數情況下,部件用戶不需要關心MouseMove事件,這不會產生問題,因為部件不依賴鼠標事件的處理過程。同樣,自定義部件也不能依賴用戶的事件處理過程。
② 部件用戶能在事件處理過程寫任意的代碼
一般說來,對用戶在事件處理過程中的代碼沒有限制。Delphi部件庫的部件都支持這種方式以使所寫代碼產生錯誤的可能性最小。顯然,不能防止用戶代碼出現邏輯錯誤。
2. 怎樣實現標准事件
Delphi帶的所有控制繼承了大多數Windows事件,這些就是標准事件。盡管所有這些事件都嵌在標准控制中,但它們缺省是protected,這意味著用戶無法訪問它們,當創建控制時,則可選擇這些事件使用戶可用。將這些標准事件嵌入自定義控制需要考慮如下:
● 什麼是標准事件
● 怎樣使事件可見
● 怎樣修改標准事件處理過程
⑴ 什麼是標准事件
有兩種標准事件:用於所有控制和只用於標准Windows控制。
最基本的事件都定義在對象TControl中。窗口控制、圖形控制和自定義控制都繼承了這些事件,下面列出用於所有控制的事件:
OnClick OnDragDrop OnEndDrag OnMouseMove
OnDblClick OnDragOver OnMouseDown OnMouseUp
所有標准事件在TControl中都定義了相應的protected動態方法,只是沒有加“On”例如OnClick事件調用名為Click的方法。
標准控制(從TWinControl繼承)具有下列事件:
OnEnter OnKeyDown OnkeyPress OnKeyUp OnExit
正如TControl中的標准事件,窗口控制也有相應protected動態方法。
⑵ 怎樣使事件可見
標准事件的聲明是protected,如果想使用戶在運行時或設計時能訪問它們,就需要將它們重聲明為public和 published。重聲明屬性而不描述它的實現將繼承相同的實現方法,只是改變了訪問級別。例如,創建一個部件並使它的OnClick事件出現在運行時,你可增加下面的部件聲明:
type
TMyControl=class(TCustomControl)
published
property OnClick; { 使OnClick在objectinspector中可見 }
end;
⑶ 怎樣修改標准事件處理過程
如果想修改自定義部件響應某種事件的方法,可以重寫代碼並將其賦給事件。將聯接每個標准事件的方法聲明的protected是出於慎密的考慮。通過,覆蓋實現方法,能修改內部事件處理過程,通過調用繼承的方法,能保持標准事件處理過程。
調用繼承的方法的順序是很重要的。一般首先調用繼承的方法,允許用戶的事件處理過程代碼在你的定制代碼前執行。然而也有在調用繼承的方法之前執行自己的代碼情況出現。
下面是一個覆蓋Click事件的例子:
procedure TMyControl.Click;
begin
inherited Click; { 執行標准處理,包括調用事件處理過程你自己的定制代碼 }
end;
3. 定義自己的事件
定義全新的事件的情況是很少見的。只有當部件的行為完全不同於任何其它事件才需要定義新事件。定義新事件一般包含三個步驟:
● 觸發事件
● 定義處理過程類型
● 聲明事件
● 調用事件
⑴ 觸發事件
定義自己的事件要遇到的第一個關鍵是:當使用標准事件時你不需要考慮由什麼觸發事件。對某些事件,問題是顯然的。例如:一個MouseDown事件是在用戶按下鼠標的左鍵時發生,Windows給應用發送WM_LBUTTONDOWN消息。接到消息後,一個部件調用它的MouseDown方法,它依次調用用戶的OnMouseDown事件處理過程代碼。但是有些事件卻不是那麼可以描述清楚的。例如:滾行槓有一個OnChange事件,可被各種情況觸發,包括按鍵、鼠標點按或其它按制中的改變。當定義事件時,你必須使各種情況的發生調用正確的事件。
這裡有TControl處理WM_LBUTTONDOWN消息的方法,DoMouseDown是私有的實現方法,它提供了一般的處理左、右和中按鈕的方法,並將Windows消息的參數轉換為MouseDown方法的值。
type
TControl = class(TComponent)
private
FOnMouseDown: TMouseEvent;
procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;
Shift: TShiftState);
procedure WMLButtonDown(var Message: TWMLButtonDown);
message M_LBUTTONDOWN;
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); dynamic;
end;
procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, Button, Shift, X, Y); { 調用事件處理過程 }
end;
procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton;
Shift: ShiftState);
begin
with Message do
MouseDown(Button, KeysToShiftState(Keys) + Shift, XPos, YPos); { 調用動態方法 }
end;
procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);
begin
inherited; { perform default handling }
if csCaptureMouse in ControlStyle then
MouseCapture := True;
if csClickEvents in ControlStyle then
Include(FControlState, csClicked);
DoMouseDown(Message, mbLeft, []); { 調用常規的mouse-down 方法 }
end;
當兩種事情-狀態變化和用戶交互—發生時,處理機制是相同的,但過程稍微不同。用戶交互事件將總是由Windows消息觸發。狀態改變事件也與Windows消息有關,但它們也可由屬性變化或其它代碼產生。你擁有對自定義事件觸發的完全控制。