視覺化控制項(Visual Component)其實就是一個類別(class),要寫一個類,可以直接在*.pas檔中寫。但是要編寫控件,則必須使用套件(package)。從File選單中選擇New,新建一個Package,這就是存放和安裝控制項用的套件。然後點選Package視窗中的Add按鈕,新增一個元件(Unit)。
在彈出的對話框最上方選擇New Component。因為一個控制項的所有屬性、方法、事件不可能都由自己編,所以就需要選擇祖先類別(或稱為"父類"或"基類"),然後再在其上方加入自己的屬性、方法、事件。在Ancestor type後的下拉框中選擇所需的祖先類別。由於編寫視覺化控制項必須要畫圖,所以選擇TGraphicControl作為祖先類別。再在Class Name方塊中輸入新控制項(類別)的名稱,一般以"T"開頭。 Palette Page是用來選擇新控制項在Delphi的視窗中的控制項頁面名稱,例如"Standard",這個可以自己取。在Unit File Name中添好新控制項檔案的路徑及檔名,點選OK按鈕。新的控制便加入了。現在可以為該控制項編寫程式碼了。
以下以編寫一個可以自訂圖片的捲軸為例,說明編寫視覺化控制項的方法。
依照上面的方法,選擇TGraphicControl為祖先類,新控制項的名稱是TPigHorizontalScroller。選擇好檔案路徑和檔案名稱後,點選OK按鈕,開始編寫程式碼。
每一個控件,都會被建立(Create)和刪除(Destroy),所以必須先寫這兩個過程。對於控制項中的每一個過程,都必須在前面先定義,然後再在後面寫。定義的過程或屬性有三種:一、在PRivate後定義的是屬於控制項內部使用的,使用該控制項的人無法看到;二、在protected後定義的一般是看不到的,只在別人使用該控制項作為祖先類別編寫其它控制項時才可見;三、在public後定義的只允許別人在程式中呼叫;四、在published後定義的可以在屬性視窗(Object Inspector)中看到。由於建立和刪除過程除了在程式設計過程中建立控制項時自動執行外,還可能在程式執行過程中動態建立控制項時被調用,所以把它定義在public後⑴。 (該序號表示次步驟在所附原始程式中的程式碼的位置,下同)現在也許還不知道應該在這兩個過程中要寫什麼,如何去編。我們在下面將會講到。
我們首先為這個控制項添加一些屬性。我們定義一個Max屬性用於設定或讀取捲軸的最大值。因為程式中一般不直接使用屬性,所以要定義一個變量,和該屬性對應起來,一邊修改或讀取其值。因為它只在控制項內部使用,所以我們把它定義在private後⑵。 (一般與屬性相關聯的變數都以"F"開頭,例如FMax)定義好變數後,再定義屬性。這個屬性需要再Object Inspector視窗中可見,所以把它定義再published後⑶。定義的語法是:
property <屬性名稱>:<類型> read <讀取該屬性時對應的變數> write <寫入該屬性時對應的變數或流程>
其它的變數和屬性也類似的定義(例如Min最小值、Value當前值等)。下面我們定義幾個屬性和變量,用來設定滾動條的圖片(因為圖片變數比較特殊,所以單獨講一下)。我們把LeftButtonUpPicture(向左按鈕圖片)、LeftButtonDownPicture(向左按鈕按下圖片)等定義為TBitmap類型(一定要定義相對應的變數)。
大家一定注意到了,在所附的來源程式中,定義這幾個屬性時,read後所指定的讀取屬性時對應的變數是F…,而write後面指定的寫入該屬性時對應的不是一個變量,而是一個Set…之類的東西,這是一個自訂的過程。作為此功能的過程的定義為:
procedure <過程名稱>(Value: <被設定的屬性的值的型別>)
因為執行寫入該類別屬性的時候需要做其它的事情,所以不能光用一個變數來處理,應該用一個流程來處理。這中過程一般定義在protected後。在該類別過程中,使用一個在⑷處這樣一個語句來給TBitmap類型的變數來賦值,這是由於該類型的變數不能直接賦值而採用的。
定義完這些TBitmap類型的變數的屬性後,上面講的create過程和destroy過程中就需要寫程式了。因為TBitmap也是一個類,所以在create過程中必須創建⑸,在destroy過程中必須釋放(free)⑹。這裡⑺所指的inherited語句是用來指明該過程是從祖先類別繼承來的。 (這個一定不能掉)。
因為我們寫的是可視化控件,所以必須在控件上畫圖。我們這個控制項的祖先類別TGraphicControl中封裝有一個Canvas(畫布)對象,我們可以直接用它來畫圖。如果你對畫布的使用還不太熟悉,最好去找一本書來看看。
下面要做的工作就是畫圖了。如何在控制項上畫圖呢?祖先類別TGraphicControl中有一個Paint事件,當控制項需要重畫時會自動觸發。我們現在要做的就是要為這個事件寫一段程式。首先在protected後面定義一個Canvas物件。由於它是祖先類別中已有的,所以不需要加任何說明⑻。我們將使用這個物件來畫圖。接著,就要定義一個Paint過程,寫出繪製控制項的程式碼。先在public後定義Paint過程。由於它是由祖先類別觸發的,而不是由使用者呼叫的,所以後面必須加上override,否則,該控制項將會因為Paint過程永遠不會被呼叫而不成為可視化控制項⑼。下面我們就來寫Paint過程的程式碼⑽。
該文章所附的來源程式的Paint過程中的T_Height等變數是用來保存捲軸中按鈕、滑桿等的大小的,這部分程式和普通的application中的程式差別不大,大部分都是對畫布進行操作,相信大家一看就明白。值得注意的是下面對FAutoSize變數的判斷⑾,FAutoSize是和該控制項的屬性AutoSize相關聯的布林型變量,是用來設定這個控制項的大小是否隨圖片的大小而變化的。請注意,在控制項的程式碼中,一般都不直接呼叫屬性,而是使用與其相對應的的變數。
程式編到這裡,就算是終於為自己的新控製做了一個外型了,不過它還不能滾動。現在我們來編寫滑鼠事件,讓我們能夠操縱它。滑鼠事件的過程的定義和Paint過程很相似,只是後面要加上參數說明⑿,滑鼠事件分為MouseDown、MouseMove和MouseUp三個,在定義後面都要加上override。接下來在後面編寫它的程式碼。注意:這裡的滑鼠事件是Mouse…,而不是通常的OnMouse…。可是在⒀處的定義是做什麼用的呢?這裡的事件定義,都是給使用者使用的,也就是說,當使用該控制項時,會在Object Inspector中的Event頁面中顯示出來。
這些滑鼠事件的程式碼也非常簡單,判斷滑鼠的座標,在畫布上畫出對應的圖片等,並同時觸發對應的事件。值得注意的是,在呼叫自訂事件時,都要先用⒁處的這樣一個語句來判斷使用者是否已經為該事件編寫程式碼。這一點非常重要,否則會呼叫出錯。
大家注意到了,剛才所呼叫的事件都是自訂的,定義的方法也很簡單,跟定義屬性差不多,只是類型時TNotifyEvent罷了。
TNotifyEvent是預設事件,其定義為:
TNotifyEvent = procedure(Sender: TObject)
如果你要定義另外形式的事件,就必須這樣:先在type後再寫
<事件類型名稱> = procedure(<參數>:<類型>)
例如:
TCustomEvent = procedure(a: Integer; b:String);
然後在public後定義:
<事件名稱>:<事件類型名稱>
例如:
AnEvent: TCustomEvent;
看完這些,這整個程式你應該要理解了吧。如果編譯或執行出錯,請注意檢查以下幾點:
1、create和destroy過程中是否有inherited語句;
2、TBitmap類型的變數create和free了沒有;
3、流程前有沒有控制項名,例如:TPigHorizontalScroller.MoseMove
判斷滑鼠是否進入或離開控制項的方法:
定義如下的過程:
procedure MouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
procedure MouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;