[轉貼]Writing VCL Component in BCB |
|
axsoft
版主 發表:681 回覆:1056 積分:969 註冊:2002-03-13 發送簡訊給我 |
這是我很久以前在網路上收集的文章,現在網路上已經找不到這一篇文章了
至於作者也不詳(我忘了)
只有作者的e-mail:s8427154@cycs03.ice.cycu.edu.tw
該作者應該也重學校畢業了!e-mail可能無效了
如作者見此文,請發表一下!謝謝!
=========================================
VCL元件DIY Writing VCL Component in Borland C Builder 1.0
前言
Borland出的Delphi在市場上耕耘了數年了,會用Delphi的人,大都會自己有自己寫的元件,幾年下來,VCL的資源豐富,大部份各位想要的元件大都已有人寫出來了.然而萬一(真的萬一)有一天,你要元件但是找不到.....嘿嘿嘿...就只有DIY了.今天不是給各位魚吃,而是教各位釣魚的方法.那麼就將小弟嘗試(不敢說研究)的結果,供大家參考,也許哪天各位放棄M$派的語言,轉而投向Borland陣營時,這一篇就會有小小的參考價值..... 建立一個新的元件
咱們廢話少說,先New一個新的元件出來吧.在上方選單上有一個"Component",選擇後再選"New",這時會開一個Component Wizard要你定義Class Name(你所要寫的元件的名字),Ancestor type(這個元件的父類別)和Palette Page(你要將元件放於哪一個元件Page).假設我們定Class Name = TMycomponent , Ancestor type = TCustomControl按OK之後出現一個畫面(如圖一). 咱們先儲存起來吧,再把這個CPP檔和H檔打開.這時C Builder已經建立了一個最基本的元件(就是什麼功能都沒有的那一種).想裝裝看嗎??找上方選單的"Component",選擇後選"Install",按"Add"把你的元件加進去,按"OK"等它Compile完就可以了.想知道你的元件在哪出現嗎??到你元件中設定的Palette Page中找找看吧... 繪出元件的外形.
其實對windows程式有點了解的人都知道,攔截WM_PAINT可以重繪外形,做Repaint()的動作.所以我們勢必要Handle這個函數,幸運的是WM_PAINT自動地被C Builder所攔截,我們只要在類別中implement這個Paint()函數就可以了.然後在Paint()中利用Canvas去畫出你的元件的外型. 例如將元件繪成一個黑心實心的矩形:
在H檔的protected中
void __fastcall Paint();
在CPP檔中
void __fastcall TMycomponent :: Paint()
{ Canvas->Brush->Color=clBlack;
Canvas->Brush->Style=bsSolid;
Canvas->Rectangle(0,0,Width,Height);
} 不難,對吧?? 增加元件的函數.
平常我們在寫類別時,多少都會帶上一些Method.同樣的VCL也可以,因為VCL本身就是一個類別.那也就是說,我們只要寫類似一般C 類別的Method就可以了.夠簡單吧?? 如何在元件中再加入另一個元件?
這一點我舉一個例子來說明好了.
例如在我們寫的元件(TMycomponent)中加入一個Timer.
請在H檔中的private中加入
TTimer *Timer1;
void __fastcall Timer1Timer(TObject *Sender);
請在CPP檔的建構子中加入
Timer1 = new TTimer (this);
Timer1->Interval=1000; // 1秒觸發一次
Timer1->Enabled=true; // 允許Timer Enable
Timer1->OnTimer= Timer1Timer;
// 設定Timer的Event為Timer1Timer函數
然後寫Timer1Timer這個函數.
void __fastcall TMycomponent::Timer1Timer(TObject *Sender)
{
// 你想做的事.........
} 不錯吧.當然這只是個例子,你可以加其他的元件進來. 增加Properties
用過Delphi或C Builder的人都該知道每次編寫一個Form時,會有一個視窗(Object Inspector)列出各個元件的Properties,大家都可以在上面改變元件其中幾個Properties的值,那你就會問我,這個東西應該如何做呢?? 其實這個東西不難,但是該注意的地方很多,請君耐心待我說來: 在H檔中的__published或public部份(一般來說是__published)寫下(我們假設要多一個property為Test ,Data type為int )
__property int Test = {read = MyTest ,write = MyTest ,nodefault};
在H檔中的private部份加入
int MyTest;
這時一個property就加進去了(興奮嗎??Rebuild Library,然後new一個新的Form,去拉你所寫的元件到這個Form,仔細看一下Object Inspector,看到沒??沒關係,你可以靠近一點.沒看到??沒關係,你可以再靠近一點.....^_^)
說明一下:
__property int Test = {read = MyTest ,write = MyTest ,nodefault};
在大括號中
read=MyTest //當有人要讀Test的值時,就傳MyTest的值回去
//這也可以給Function的名稱 .
write=MyTest//當有人要寫Test的值時,就寫到MyTest
//這也可以給Function的名稱. !![注意]
若要給Function名稱,則此Function宣告必須在private中,
而在function前加入__fastcall. 例如:
在H檔的private中
int MyTest;
void __fastcall SetMyTest(int Value); 在H檔的__published中
__property int Test = {read = MyTest,write=SetMyTest,nodefault};
在CPP檔中
void __fastcall TMycomponent :: SetMyTest(int Value)
{
MyTest=Value;
} 不錯吧.也許你會問我,這個的效果不是和剛剛那個一樣嗎??那我們幹嗎要換??? 這一點請看下一個小主題 < Event > 你就會明白了.一些Event如OnChange等的Event,就是在這裡被觸發的!! 增加新的Event
這是另一個重點,因應需要,很多情形下我們必需要觸發一些自訂的Event.這就會讓人想起了一些問題,我何時才要觸發Event??我如何觸發Event??我的Event的作用是啥??這些想清楚了之後,再來寫會比較好.再回到上一個小主題的最後一部份,我們談到了可以用Function存取Properties時,順帶著觸發Event.那就舉上一個例子做為其之延伸吧!! 例: 我們希望當使用者修改Test的值時會觸發OnChange
的Event.
在H檔的private中加入
TNotifyEvent FOnChange;
int MyTest;
void __fastcall SetMyTest(int Value);
在H檔的__published中
__property int Test = {read = MyTest,write=SetMyTest,nodefault};
__property TNotifyEvent OnChange = {read=FOnChange, write=FOnChange};
在CPP檔中
void __fastcall TMycomponent :: SetMyTest(int Value)
{
MyTest=Value;
if ( FOnChange!=NULL)
FOnChange(this);
}
如此當有人修改了Test的值時,而且Event所指向的Function
有被你所設過的話,就會去執行該Function的程式碼.
如此完成了自訂Event的觸發.帥氣,不是嗎?? 攔截訊息.
在Windows中漫布著Message的流動,我想這是每一個了解Windows程式設計的人都會知道的.因應著某些需要,我們必須去攔截某一些我們要的訊息.例如當元件的大小改變時,我們必須做出相對應的動作,如重繪元件等,那就必須去攔截WM_SIZE.如果我們需要在某個地方設定Focus,那麼我們就必須去攔截WM_SETFOCUS來達到我們的要求. 重點是:我們該如何去攔截我們要的Message呢??我們舉例子說明:以攔截WM_SIZE來說. 在H檔的private中寫入
MESSAGE void __fastcall WMSize(TWMSize &Message);
在__published中寫入
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_SIZE, TWMSize, WMSize);
//第一個參數:攔截的訊息名稱
//第二個參數:對應到C Builder中的結構宣告
//第三個參數:攔入訊息的Function名稱.
END_MESSAGE_MAP(TCustomControl);
^^^^^^^^^^^^^^^這是父類別
在你的CPP檔中寫入
void __fastcall TMycomponent::WMSize(TWMSize &Message)
{
//你要的處理
} 大功告成.你已攔截了你要的訊息了.簡單吧,以各位的聰明才智舉一反三,甚至是反十,我想都不會有什麼問題,只是改一些小地方而已. 到此,我們已把最基本的元件寫法,談了一遍,大家可以喝杯茶輕鬆一下,後面的幾個小題將提到一些觀念和一些各位可能會問的問題.先去看個電視再來看其他的吧.....*^_^* 我應該從哪一個類別來繼承呢??
這是一個好問題,每個人第一次寫元件都會不知道該從哪一個類別繼承..在VCL中有一些所謂的"自訂"類別,許多許多的控制項就直接從這些"自訂類別"裡繼承下來.例如說:TMemo是繼承自TCustomMemo類別 , TRichEdit則是從TCustomRichEdit繼承下來的,如果你寫的元件是寫類似這些功能的話,那你可以直接繼承相對應的自訂類別. 如果你要寫視覺元件,可以繼承自TGraphicControl,不過這有個缺點,就是沒有Window Handle,亦不能接受輸入焦點. 如果你要寫類似Timer元件(拉至Form上還是一樣一個小圖)的話,可以繼承TComponent 如果你要寫有關視窗元件的話,那你可以繼承自TWinControl因為它將已存在的視窗控制項包裝起來,如Windows標準控制項或VBX元件. 為何我的元件不能處理方向鍵??
這一點你就必須去攔截一個叫WM_GETDLGCODE的訊息才能處理方向鍵的訊息,如果你不這樣做,那方向鍵的功能就只能用來移動視窗的焦點而已. 在H檔案中的private加入
MESSAGE void __fastcall WMGetDlgCode ( TWMGetDlgCode &Message);
在__published的部份
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_GETDLGCODE,TWMGetDlgCode,WMGetDlgCode);
END_MESSAGE_MAP(TCustomControl);
^^^^^^^^^^^^^^^
注意這是你元件的Ancestor type
在CPP檔中
void __fastcall TMycomponent::WMGetDlgCode(TWMGetDlgCode &Message)
{
Message.Result=DLGC_WANTARROWS|DLGC_WANTCHARS;
} [另一種做法]
攔截CM_WANTSPECIALKEY這個元件訊息!!
這一個元件訊息提供給你比攔截WM_GETDLGCODE這個
視窗訊息更容易且靈活的判斸方法來決定是否需要某些特殊
鍵的訊息.特殊鍵抱括:VK_TAB,VK_LEFT,VK_RIGHT,
VK_UP,VK_DOWN,VK_RET,VK_ESCAPE和VK_CANCEL.
如果訊息傳回值是非零值,這個鍵就會被送至KeyPress方法
以供處理,否則這個鍵的訊息會被送至元件的父控制項,以預
設方式來處理.
// 在H檔的private:
Message void __fastcall CMWantSpecialKey(TCMWantSpecialKey &Message);
// 在H檔__published:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CM_WANTSPECIALKEY,
TCMWantSpecialKey,CMWantSpecialKey);
END_MESSAGE_MAP(TCustomControl);
// 在CPP檔中
void __fastcall CMWantSpecialKey(TCMWantSpecialKey &Message)
{
switch(Message.CharCode)
{ case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
Message.Result=1;
break;
}
} 為何我的元件重繪時的會有閃動的情形??
引起閃動的原因很多,主要是WM_PAINT和WM_ERASEBKGND這兩個訊息的處理.當VCL控制收到WM_ERASEBKGND這個訊息時,它會將元件背景擦掉,然後設定成背景預設的顏色.也就是說,再整個元件重繪之前,會先被清成背景的顏色,然成再動WM_PAINT予以重繪.如此就是造成閃動的最主要原因了.這個情形尤以繼承自TWinControl的元件較多此類之情形. 如何解決???就是告訴Windows你要自行解決"所有的"繪圖動作,如此Windows就不會做清成背景色的動作.但是有一個很重要的前提,就是你一定要確定你的Paint Method的確把整個元件重繪過,如果漏了什麼地方忘了畫,那麼那個地方的資料會好像透明的元件一樣,有視窗在元件上方就會留下殘影,可以想見其情況否?? 這個方法可以加速元件的繪圖動作,這可以想見的,因為少了填背景色的動作. // 在H檔的private:
Message void __fastcall WMEraseBkgnd(TWMEraseBkgnd &Message);
// 在H檔__published:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_ERASEBKGND,
TWMEraseBkgnd,WMEraseBkgnd);
END_MESSAGE_MAP(TCustomControl);
// 在CPP檔中
void __fastcall WMEraseBkgnd(TWMEraseBkgnd &Message)
{
//不要重繪背景,會造成元件閃動地喔!!
Message.Result=1;
} [另外一種可能]
就是在你寫Paint Method時,在裡面用了類似以下的函數來
繪元件,如Canvas中的FillRect(),這個函數在每次畫繪時會做
類似WM_ERASEBKGND訊息的動作,所以要避開這些動作. VCL在C Builder 1.0和C Builder 3.0的不同點:
C Builder1.0 繼承了Delphi 2.0的傳統,C Builder3.0則相似於Delphi 3.0,其主要的不同點在於Package.Borland在推出Delphi3. 0加入Package的觀念,就是將各個類似的元件置於同一個Package中,某天覺得這個Package的元件,暫時不適合留著用,你可以先Disable這個package,而不用把它從Library中移除,若是以後有用到的一天,就把它Enable回來就好了.Package也就提昇了管理上的方便. 然而為了迎合這個目標,在VCL的撰寫上,就有些小地方不一樣,但是大部份是相同的.例如:<圖二> 結論:
林林總總談了一堆,其實還有一些沒有談到的地方,礙於篇幅,只好待以後有機會在談談吧. 有問題的話,可以mail給我:s8427154@cycs03.ice.cycu.edu.tw
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
|
axsoft
版主 發表:681 回覆:1056 積分:969 註冊:2002-03-13 發送簡訊給我 |
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: 希望您也能將您的製作元件心得說給大家參考一下喔!這樣大家就多一個學習的機會嘍!說真的,我用 BCB 近 5 年了,直到最近才開始學寫 VCL 元件。 因為我對於 OO 的感覺還是太少... 在沒 OO 的 DOS 時代實在是奮鬥太久了,用 BCB 只因懶得再學 Object Pascal... 對於之前開發的程式,大多沿用沒有 OO 的架構,會寫一些 functions 給 VCL 元件的某事件去叫用,都沒有想過要把 VCL 改寫或增強。 之前有想過要寫 VCL 元件,但在網路中可下載的 VCL 元件含原始碼的,大多都是 Delphi 的原始碼,好像很少人用 BCB 在寫 VCL 元件~~ 大部分賣 VCL 元件的軟體公司也都是使用 Delphi 在開發,其商用完整版所附的都是 Delphi 的原始碼... 如果我是軟體商,而且要賣 VCL 元件,那我也會要求使用 Delphi 來開發,這樣才能同時賣給 Delphi 使用者及 BCB 的使用者。 ... 如果當完兵時 BCB 還沒出,那我可能就會去學 Delphi 了 ... 會開始學寫 VCL 元件,是因為公司的同事開始寫 VCL 元件了,如果再不自學就來不及了...
------
http://www.ViewMove.com |
GGump
一般會員 發表:2 回覆:16 積分:8 註冊:2006-08-04 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |