熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

通過實例看VCL組件開發全過程(一)

2022-06-13   來源: Delphi編程 
    這篇文章算是對我前段時間學習的一個學習總結以及對自己學習過程的一個回顧本文通過一個簡單的例子來盡可能的展示VCL組件開發的各個方面本文針對即將學習組件開發的初學者如果你已經熟悉組件開發或認為本文內容過於基礎簡單那麼本文對你毫無用處閱讀本文假設你已經熟悉delphi的普通程序設計以及vcl的結構層次還有一些重要的關鍵字publishedproperty等(注本文內容建立在delphi及以上版本)


  在這篇文章中我們將建立一個和時間有關的組件這個組件通過設置它的不同狀態有以下基本功能顯示系統的當前時間(包括設置鬧鐘)跑表倒計時

   這是一個簡單的例子然而我們將在這個例子中盡可能多的用到delphi在組件開發中的多種特性你可以通過以下列舉出的本文涉及特性有選擇的閱讀

  ·組件和組件包

  ·組件的屬性類別

  ·組件的屬性編輯器

  ·組件編輯器


  組件和組件包以及一些你應該知道的文件類型

  組件和組件包的關系就如同普通工程中unit和工程文件的關系一樣通常你所安裝的組件都是以組件包的形式發布的一個組件包中可以有很多個組件在組件開發中組件包就是項目的工程文件為了開始開發我們的組件(我們把他叫做TClock)並將它包括在我們自己的組件包(ClockPackage)中我們選擇Fileànewàother在彈出的窗口中的New頁選擇Package新建一個組件包得到一個組件包窗口查看這個組件包的原文件(dpk)得到以下代碼

package ClockPackage;


{$R *res}

{$ALIGN }

{$ASSERTIONS ON}

……

……

{$DESCRIPTION Our Clock Pack}

{$IMPLICITBUILD OFF}


requires

rtl;


end

  這個文件其實就是組件開發中的工程文件requires關鍵字指示了組件包所需組件包的列表隨著向組件包中加入組件(類似於單元文件)你還會看到contains關鍵字指示了組件包所包含的組件你可以通過組件包窗口中的add和remove按紐來添加新的組件和刪除已有的組件另外這個代碼中所包含的大量的編譯器開關大多都可以在組件包窗體上的Options中設置這裡需要補充說明的是組件包的種重要屬性(都在Options中)Designtime OnlyRuntime OnlyDesigntime and runtime(這個詞的意思有英語基礎的朋友應該都知道吧)對於大多數的組件包我們只要選擇最後一個就可以了然而有些組件包設計為只運行時(這樣你用這套組件開發的程序不能脫離組件而單獨運行組件包也不能被安裝)有些組件包被設計為只設計時(這將在後文有更詳細的說明)

  了解了組件和組件包我們對組件開發中可能出現的一些你沒有見過的文件做一些說明dpk文件既組件包的原代碼bpl文件組件包編譯後的結果在沒有發布dpk的情況下可以通過bpl來安裝組件包到delphi(ProjectàOptionsàPackagesàadd)pas在這裡就是組件包中組件的原代碼了dcu為pas編譯後的結果在你選擇將組件包含進組件包時(contains關鍵字)你可以選擇發布原代碼或是不發布(dcu文件)dcp如果你將組件作為運行時組件連接器將使用該文件


  開始開發組件

  了解了上面的知識後我們就可以開始開發組件了!在組件窗體中單擊add選擇NewComponent頁在第一個組合框中選擇我們的組件將要繼承自哪個類(通常新的組件是通過繼承已有的組件來開發的)由於這個組件的主要作用是要顯示時間跑表倒計時種的文字信息所以我們選擇繼承自TCustomLabel(由於我們並不需要Tlabel的全部功能我們選擇了能夠隱藏Tlabel屬性並有選擇的發布它的屬性的TcustomLabel類)接下來為我們的新組件取一個名字Tclock然後指定我們想把組件安裝到哪一個頁中這裡我們自己鍵入一個ClockAndTime頁這將出現在RegisterComponents過程中(後面會詳細說明)選擇好文件保存的路徑後(最好把它和組件dpk包放在同一目錄)確認這是組件包窗體中的contains下已經多了我們剛才建立的組件的文件雙擊它開始編寫代碼

  在代碼中我們需要注意在interface部分的一個新的過程procedure Register;(注意delphi規定Register的R必須大寫這是一個保留字)這個過程是作為每一個組件所必須有的它完成組件的注冊包括組件本身以及如屬性編輯器等多種組件特性的注冊)

procedure Register;

begin

RegisterComponents(ClockAndTime [TClock]);

//這個過程注冊組件本身注意到前面定義的ClockAndTime頁了嗎?

//這裡在後面還會出現一些新的過程包括注冊組件的屬性類別等等

end;

  組件的代碼由於假設你已經熟悉delphi開發(它和一般開發沒什麼不同)我們就直接貼出來並加上適當的注釋


unit Clock;


interface


uses

SysUtils Classes Controls StdCtrlsExtCtrls;


type

TState=(StClockStRunClockStBackClock);//定義枚舉類表示控件的種狀態時鐘跑表倒計時鐘


TClock = class(TCustomLabel)

private

fState:TState;

fTimer:TTimer;//為什麼使用這個組件作為我們組件的私有成員就不用說了吧

RCD:array[] of integer;//跑表中的各個數位

fBeginTime:string;//到計時時的開始時鐘之所以沒用TTime類型是為了在後面演示屬性編輯器

fWakeTime:string;//鬧鐘時間出於和上面同樣的理由

fAllowWake:boolean;//是否開啟鬧鐘功能

fOnWakeUp:TNotifyEvent;//為了使組件更加完美我們允許組件用戶能夠響應鬧鐘到來時的時件

fOnTimeUp:TNotifyEvent;//同上能夠響應倒計時種完成時的事件我們將發布這兩個事件

function GetActive:boolean;//控制Timer是否工作以控制種狀態的鐘是否工作

procedure SetActive(Value:boolean);

procedure SetState(Value:TState);

procedure SetBeginTime(Value:string);

procedure SetWakeTime(Value:string);

protected

procedure WalkClock(sender:TObject);//作為時鐘時走種的事件

procedure RunClock(sender:TObject); //跑表

procedure BackClock(sender:TObject);//倒計時

public

constructor Create(AOwner:TComponent);override;//完成一些初始化工作

procedure ReSetRunClock; //跑表和倒計時都需要一個復位方法給組件使用者調用

procedure ReSetBackClock;

published

property State:TState read fState write SetState default StClock;//默認為時鐘狀態

property Active:boolean read GetActive write SetActive;//控制種狀態的鐘是否工作

property BeginTime:string read fBeginTime write SetBeginTime;

property WakeTime:string read fWakeTime write SetWakeTime;

property AllowWake:boolean read fAllowWake write fAllowWake;

property OnWakeUp:TNotifyEvent read fOnWakeUp write fOnWakeUp;

property OnTimeUp:TNotifyEvent read fOnTimeUp write fOnTimeUp;

//最後我們再發布一些被TCustomLabel所隱藏而我們又需要的屬性

property Align;

property Alignment;

property Color;

property Font;

property ParentColor;

property ParentFont;

property ParentShowHint;

property PopupMenu;

property ShowHint;

property Visible;

property Transparent;

property OnClick;

end;


procedure Register;


implementation


procedure Register;

begin

RegisterComponents(ClockAndTime [TClock]);

end;


{ TClock }


constructor TClockCreate(AOwner: TComponent);

begin

inherited Create(AOwner);

//設置默認值

fTimer:=TTimerCreate(self);

//將它屬於我們的組件這樣便不用編寫析構函數而可以自動在釋放本組件時釋放Timer

Active:=false;

AllowWake:=false;

State:=StClock;

BeginTime:=::;

WakeTime:=::;

end;


function TClockGetActive: boolean;

begin

result:=fTimerEnabled;

end;


procedure TClockSetActive(Value: boolean);

begin

fTimerEnabled:=Value;

end;


procedure TClockSetState(Value: TState);

var

i:integer;

begin

case Value of

StClock:

begin

Active:=false;

fTimerInterval:=;

fTimerOnTimer:=WalkClock;

Active:=true;

end;

StRunClock://由於Time類型不好處理微秒操作我們只有手工模仿這個操作代碼會稍微煩瑣

begin

Active:=false;

for i:= to do RCD[i]:=;

Caption:=IntToStr(RCD[])+IntToStr(RCD[])+:+IntToStr(RCD[])+IntToStr(RCD[])+:+IntToStr(RCD[]);

Caption:=Caption+IntToStr(RCD[])+:+IntToStr(RCD[])+IntToStr(RCD[]);

fTimerInterval:=;

//經過測試這個秒表的效果很好然而這只是一個技術上的演示

//實際上這麼頻繁(/秒)的不斷執行RunClock會使CPU的占用一直達到%

//這並不是一個好注意事實上要想在跑表中顯示微秒級別並做到合理的占用CPU

//這需要更加靈活和復雜的編程

fTimerOnTimer:=RunClock;

end;

StBackClock:

begin

Active:=false;

Caption:=BeginTime;

fTimerInterval:=;

fTimerOnTimer:=BackClock;

end;

end;

fState:=Value;

end;

procedure TClockSetBeginTime(Value: string);

begin

try

StrToTime(Value);

fBeginTime:=Value;

if State=StBackClock then

begin

Active:=false;

Caption:=Value;

end;

except

on Exception do

begin

fBeginTime:=::;

if State=StBackClock then Caption:=::;

end;

end;

end;


procedure TClockSetWakeTime(Value: string);

begin

try

StrToTime(Value);

fWakeTime:=Value;

except

on Exception do

begin

fWakeTime:=::;

end;

end;

end;


procedure TClockWalkClock(sender: TObject);

begin

Caption:=TimeToStr(Time);

if AllowWake and (StrToTime(Caption)=StrToTime(WakeTime)) then

begin

Beep;//蜂鳴器

if Assigned(fOnWakeUp) then

fOnWakeUp(self);

end;

end;


procedure TClockRunClock(sender: TObject);

begin

RCD[]:=RCD[]+;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then begin RCD[]:=RCD[]+;RCD[]:=; end;

if RCD[]= then RCD[]:=; //我們的跑表最多可計個小時;

Caption:=IntToStr(RCD[])+IntToStr(RCD[])+:+IntToStr(RCD[])+IntToStr(RCD[])+:+IntToStr(RCD[]);

Caption:=Caption+IntToStr(RCD[])+:+IntToStr(RCD[])+IntToStr(RCD[]);

end;

procedure TClockBackClock(sender: TObject);//可以在一天之類的時間倒計時

begin

if StrToTime(Caption)<>StrToTime(::) then

Caption:=TimeToStr(StrToTime(Caption))

else

begin

Active:=false;

Beep;

if Assigned(fOnTimeUp) then

fOnTimeUp(self);

end;

end;

procedure TClockReSetBackClock;

var

i:integer;

begin

if State=StRunClock then

begin

Active:=false;

for i:= to do RCD[i]:=;

Caption:=:::;

end;

end;

procedure TClockReSetRunClock;

begin

if State=StBackClock then

begin

Active:=false;Caption:=BeginTime;

end;

end;

end

  為了測試我們的組件現在你就可以安裝這個組件包並建立一個應用測試它了點擊組件包窗體中的install即可(注意一但你安裝了組件包當你想對組件修改時在修改了原代碼以後只用點擊組件窗體的compile就可以了更新組件了)這時delphi的組件頁的最後多出了我們定義的頁其中有了我們的組件!

  然而這個組件到目前為止仍然不夠完善還不能正式發布給用戶在下一篇中我們將解決兩個重要的問題給我們的組件添加一個默認的圖標將這個組件雜亂的屬性歸類


From:http://tw.wingwit.com/Article/program/Delphi/201311/24840.html
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.