作為組件制作的開始,應該了解一些概念,我以為這些概念是非常重要的,將可以作為以後實踐的理論基礎。
一、組件的簡要層次結構
一般情況下,VCL的組件可以從Tcomponent為開始。其最明顯的特征就是它的屬性可以在設計時通過對象察看器來操縱,另外,他還能擁有其他組件。
從Tcomponent下,分出非可視組件和可視組件。
非可視組件如TOPenDialog,TTimer,TTable等,這些組件因為繼承自Tomponent,所以也就繼承了在設計時可以被操縱的特性。
可視化組件始自TControl,是它引入了可視化屬性和方法,使繼承自它的類都有了這些可視化特性。
TControl又分出兩類組件類型:從TWinControl(窗口控件)自下的控件,和從TGraphicControl(圖形控件)自下的控件。繼承自TWinControl的控件將windows控件進行封裝,所以擁有windows控件的很多特性,比如可以得到焦點,有唯一的句柄,用戶可以通過發送消息與這些控件進行交互等。而繼承自TGraphicControl的控件,也是可見的,但沒有句柄,可以稱之為圖形控件,比如TLabel,TBevel,都是Delphi畫出來的,並不占用系統資源。
二、屬性
先看一個簡單的屬性定義:
TsomeObj=class
Private
FCount:integer;
Protected
Procedure SetCount(value:Integer);
published
Property count:integer read FCount write SetCount default 0;//屬性定義
End;
該屬性從私有成員FCount讀出值,而靠SetCount方法設置值到私有成員FCount。
屬性的優勢在於可以很直觀進行讀寫,而又不同於私有成員。因為屬性可以通過寫訪問方法來保護私有成員:
Procedure TsomeBoj.SetCount(value:Integer);
Begin
If FCount<>value then
FCount:=value;
End;
其中屬性定義中的Default 0並不是默認值(即對象察看器顯示的值),默認值要在組件類的構造函數中設定。而Default有這樣的作用,決定DFM文件中是否要保存該屬性的值,比如上面為Default 0,即當該屬性值為0時,則該屬性不會被保存到DFM中,如果該屬性值不為0,則該屬性會被保存到DFM中。另外屬性定義還有一個關鍵字為
NoDefault,設置了這個關鍵字,比如
Property count:integer read FCount write SetCount NoDefault;
則無論它的值是什麼,都會被寫到DFM文件中。
屬性可以有如下幾種類型,下面只給出簡單介紹,而這些類型的屬性,會在組件制作時詳細的運用:
簡單類型屬性:如上面定義的,加一個例子
Property text:string read Ftext write SetText;
枚舉類型屬性:
TEnumtype=(Enum1,Enum2,Enum3);
FEnumtype:TEnumtype;
Property Enumtype:TEnumtype read FEnumtype write FEnumtype;
在對象察看器中看來就是下拉列框選擇值。
集合類型屬性:
Tset=(set1,set2,set3);
Tsets=set of Tset;
Fsets:Tsets;
Property sets:Tsets read Fsets write Fsets;
在對象察看器中看來,就是列出幾個選項分別設置真假。比如TForm的BorderIcons屬性即是。
對象類型屬性:一個屬性是一個對象,而這個對象必須派生自Tpersistent或者他之下的類,才能在對象察看器中可以展開它,並設置它裡面的屬性。
數組類型屬性:數組屬性如果要在對象察看器中看見,需要有自己的屬性編輯器(如果不想在對象察看器看當然就不用啦),是比較高級的組件,在後來的組件制作再來介紹,會更直觀一些。這裡只給出它的定義形式:
property Selected[Index: Integer]: Boolean read GetSelected write SetSelected;
三、事件
事件其實是一種特殊的屬性,他是指針類型,指向一個事件方法類型。當有特定的事件發生時,它就會關聯到一段執行代碼。
下面以一個例子來講解事件是怎麼發生的。
我們先定義一個鼠標點下事件的鼠標事件類型,它其實就是方法指針:
type TMouseEvent = procedure (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object;
又定義一個私有成員:鼠標事件類型的,即一個方法指針類型
FonMouseDown:TMouseEvent
最後定義一個屬性:類要通過這個屬性將外部的事件處理函數和FonMouseDown關聯在一起:
onMouseDown:TMouseEvent read FonMouseDown write FonMouseDown;
當有鼠標左鍵點擊的,系統會向窗口會發送WM_LBUTTONDOWN;消息
Delphi可以截獲這個消息,如下定義消息函數:
procedure WMLButtonDown(var Message: TWMLButtonDown);
message WM_LBUTTONDOWN;
在這個消息處理函數中調DOMouseDown,DoMouseDown又調 用了MouseDown
在這個函數裡面才到了最重要的部分
該函數是這樣的:
procedure TControl.MouseDown(Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
begin
if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y);
end;
而我們先來看看用戶外部是怎麼操作的,
他自己定義一個SomeobjMouseDown; 是一個事件處理函數,必須和TMouseEvent的形式一樣:
Procedure SomeobjMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
然後他這樣賦值:
someobj.onMouseDown:=SomeobjMouseDown;
當賦值以後,類內部其實是通過onMouseDown屬性,將SomeobjMouseDown;與FonMouseDwon關聯在一起,也就是說,MouseDown方法中調用了FOnMouseDown(Self, Button, Shift, X, Y);其實就等於調用了Procedure SomeobjMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
所以用戶就可以在這個自定義的事件方法中寫自己的代碼,當事件發生時,該類的調度機制就會自動調用這個事件方法啦
也許有人會問,為什麼從消息處理函數要調用DoMouseDown,DoMouseDown又調用MouseDown,然後再調用事件方法呢。為什麼不直接在消息處理函數WMLButtonDown中調用呢,其實它這樣的做的目的是要進行一些保護判斷,以及一些消息附加值的轉換,使這些值看起來更加直觀。
好了,事件就講了這裡,不知道你們明白了沒有,可能是我的表達能力不行,但沒有關系,到真正做的時候,大家應該能明白了。
四、組件制作步驟
有了上面的基本概念,其實制作簡單組件已經不是什麼問題了,而要做真正的組件,還需要有一個正確過程,我們以後學做組件,也會順著這個過程來做。主要如下:
1, 確定一個祖先類。怎麼確定,可以根據上面組件的簡要層次結構來確定。如果你想做非可視化組件,可以從繼承TComponent開始。如果想做可視要可視化組件,可以從TControl的子類開始。
2 創建組件的單元,這個在制作組件時再說,不過是在IDE裡面做幾個操作而已。
3 給組件寫屬性,方法,事件,成員,等。這些在上面己有詳細說明,是寫組件的核心部分,事實上也是後面實踐的主要內容。
4 測試,安裝組件和寫幫助,這個內容比較次要,後面的例子會講怎麼樣安裝,包括單個單元,或用包的形式安裝。而寫幫助,己超出范圍,這裡就不說了。
關於組件基本概念就到這裡講完了,接下來就是實踐了,有了上面的知識,實踐起來也不是很難,很多東西都在上面了,而一些高級的特性,會在以後慢慢說的。