以后會(huì)把我的一些經(jīng)驗(yàn)&一些想法發(fā)上來(lái)的,共同學(xué)習(xí)共同進(jìn)步。主要是java&Delphi方面的吧,Delphi用了有將近三年了,用它做了兩個(gè)項(xiàng)目(都是呼叫中心),做了N多的小軟件,以后會(huì)陸續(xù)把他們貼上來(lái)的?,F(xiàn)在在深圳一家比較有名的軟件工作做開(kāi)發(fā),用的是java,公司的自主研發(fā)的MDA平臺(tái)確實(shí)不錯(cuò),自己偷偷的學(xué)了不少東西,也模仿著做了一些東西,其中的各種感想也會(huì)及時(shí)與大家交流的。
以后會(huì)把我的一些經(jīng)驗(yàn)&一些想法發(fā)上來(lái)的,共同學(xué)習(xí)共同進(jìn)步。主要是java&Delphi方面的吧,Delphi用了有將近三年了,用它做了兩個(gè)項(xiàng)目(都是呼叫中心),做了N多的小軟件,以后會(huì)陸續(xù)把他們貼上來(lái)的?,F(xiàn)在在深圳一家比較有名的軟件工作做開(kāi)發(fā),用的是java,公司的自主研發(fā)的MDA平臺(tái)確實(shí)不錯(cuò),自己偷偷的學(xué)了不少東西,也模仿著做了一些東西,其中的各種感想也會(huì)及時(shí)與大家交流的。
問(wèn)世間情為何“物”
―――情人節(jié)的禮物
發(fā)表于《電腦愛(ài)好者2003年2月刊》
又一個(gè)情人節(jié)到了,阿當(dāng)?shù)?/SPAN>MM非要阿當(dāng)寫(xiě)封情書(shū)送給她,說(shuō)什么阿當(dāng)平常工作忙,不浪漫,忽視她………阿當(dāng)在強(qiáng)大的“武力”壓迫之下只有屈服!可是已經(jīng)“封筆”多年的阿當(dāng)寫(xiě)情書(shū)真是個(gè)不小的難題呀,不過(guò)阿當(dāng)就是阿當(dāng),他又打起了電腦的注意……
專(zhuān)為不同階段,量身定做――唯美情書(shū)
初戀時(shí)期,情書(shū)是愛(ài)的鼓動(dòng)力;熱戀時(shí)期,情書(shū)是愛(ài)的奠基石;狂戀時(shí)期,情書(shū)是愛(ài)的醒腦劑;失戀時(shí)期,情書(shū)是起死回生的救命劑;成功時(shí)期,情書(shū)是愛(ài)的贊美詩(shī)(特別披露阿當(dāng)之三“狗”階段:先是“癩皮狗”,再來(lái)是“哈巴狗”,最終進(jìn)入“狼狗”,目前阿當(dāng)正在向著“狼狗”這一偉大目標(biāo)前進(jìn)^O^)。情書(shū)在不同時(shí)期自然內(nèi)容也不大相同啦,軟件內(nèi)的上百封情書(shū)就是根據(jù)處于不同戀愛(ài)時(shí)期的人士而設(shè)計(jì)的。本軟件可將寫(xiě)好的情書(shū)打包成EXE或html,阿當(dāng)覺(jué)得這個(gè)功能真的不錯(cuò),阿當(dāng)?shù)?/SPAN>MM對(duì)電腦勉強(qiáng)來(lái)說(shuō)是個(gè)菜鳥(niǎo),直接生長(zhǎng)EXE文檔,對(duì)她可方便多了,阿當(dāng)也省去了很多解釋時(shí)的口水了^O^。
海量情書(shū)庫(kù)――魔術(shù)情書(shū)
一萬(wàn)封以上的情書(shū),還可隨意擴(kuò)充,真是海量呀,就憑這一點(diǎn),阿當(dāng)當(dāng)然要重點(diǎn)推薦一下啦。軟件更精選了一百多封中外經(jīng)典情書(shū),可以任您使用。比較有趣的,軟件提供了發(fā)送匿名情書(shū)的功能,怎么樣,給你暗戀中的MM一個(gè)驚喜吧,如果還搞不定的話(huà),那……(阿當(dāng)也沒(méi)辦法啦^O^)
讓你的情書(shū)“開(kāi)口說(shuō)話(huà)”――浪漫情書(shū)(LoveLetter)
現(xiàn)在情書(shū)也可以“有聲有色”了,怎么樣?把“I LOVE YOU”“說(shuō)”出來(lái),MM不高興才怪,來(lái)試試這個(gè)軟件吧,它可是帶有一個(gè)強(qiáng)大的情話(huà)庫(kù)呀。
“情書(shū)精靈”是軟件的一大特色,在第一次使用這個(gè)軟件的時(shí)候,您可對(duì)它進(jìn)行設(shè)置,對(duì)于到底有什么用,您快去試試吧!
它能及時(shí)地提醒您使用的方法。當(dāng)您第一次使用“浪漫情書(shū)”時(shí)會(huì)出現(xiàn)一個(gè)初始化界面,您可以在其中輸入對(duì)她的稱(chēng)呼、您的落款等信息。您還可以對(duì)它選擇你喜歡的情話(huà)庫(kù)!
兩個(gè)人的世界,加密你的情書(shū)――心跳情書(shū)
該軟件的情書(shū)書(shū)寫(xiě)方法包括:組合式書(shū)寫(xiě),模板式書(shū)寫(xiě),英文情書(shū)書(shū)寫(xiě),腳本編程書(shū)寫(xiě),電腦自動(dòng)書(shū)寫(xiě)。如何?連英文的都可以寫(xiě)呀,國(guó)外的MM……它還能對(duì)情書(shū)加密,這個(gè)功能不錯(cuò)吧。這種東東除了心愛(ài)的MM外怎么可以給別人看呢。這個(gè)軟件的另一個(gè)“神奇”之處大家猜一猜在哪兒?去看看幫助文件吧,里面的東東保證你非常受用!
戀愛(ài)物語(yǔ)之情書(shū)殺手BOY版:內(nèi)附“戀愛(ài)密技”,阿當(dāng)很是受用,不可不看。
情書(shū).net:網(wǎng)戀必備之利器,能夠讓人歡喜讓人憂(yōu),不可不備。
情書(shū)生成器:阿當(dāng)使用的第一個(gè)情書(shū)軟件,全windows界面,一看就懂,寫(xiě)出的情書(shū)可以說(shuō)是至幽至默。
1 光宗耀祖
小菜同志近日迷戀上了編程,整天泡在電腦前忙的不亦樂(lè)乎。不過(guò)我發(fā)現(xiàn)昨天他好像一個(gè)晚上沒(méi)有睡好,今天又神神秘秘的,還要我陪他去他祖父的墓地。到了墓地,他沖著他祖父的墓碑磕了三個(gè)響頭,最里邊不知在嘟囔著什么,好像在說(shuō)“光宗耀祖”、“請(qǐng)爺爺在天之靈為我保佑”之類(lèi)的話(huà),而且還流著眼淚。我怕他出事,就問(wèn)他怎么了?他想了半天,才說(shuō):“兄弟,咱哥倆從小長(zhǎng)到大,這次小弟我求大哥您一件事。”?!笆裁词卵剑磕阏f(shuō)吧!”,我說(shuō)。“你編程水平不錯(cuò),我又有商業(yè)頭腦,咱們開(kāi)個(gè)軟件公司吧!”?!昂醚剑?dāng)然好了!不過(guò)咱們做什么東西呢?”,我問(wèn)。他又一言不發(fā),顯得很激動(dòng),過(guò)了好久才說(shuō):“咱們回家再說(shuō)吧!”。
到了他家,他急忙打開(kāi)電腦,啟動(dòng)Delphi,打開(kāi)了一個(gè)他做的Delphi源碼,上面放著一個(gè)webbrowser控件和一個(gè)Edit輸入框。他很激動(dòng)地說(shuō):“大哥,我昨天才發(fā)現(xiàn)做一個(gè)瀏覽器也并不是很難,只用一個(gè)控件就能辦到!所以,我想……我想成立一個(gè)軟件公司,專(zhuān)門(mén)做網(wǎng)頁(yè)瀏覽器!打敗微軟!為網(wǎng)景報(bào)仇!微軟呀微軟,我做的瀏覽器不比你的差!你用了這么多年開(kāi)發(fā)IE,而我利用Delphi就能很快很好的做出來(lái)!我要挑戰(zhàn)微軟!祖輩呀!列祖列宗呀!我要光耀門(mén)楣了!我要打敗微軟了!”。這時(shí)他明顯已經(jīng)處于癲狂狀態(tài)。我聽(tīng)到這個(gè),差點(diǎn)把肚皮笑破,不過(guò)我還是忍住了笑,假裝一本正經(jīng)地說(shuō):“好呀!我也有個(gè)點(diǎn)子!”。說(shuō)著,我把MediaPlayer控件裝到了Delphi地組件面板上,然后用了不到一分鐘,就“做”出了一個(gè)播放器,這時(shí)我發(fā)現(xiàn)他地眼都綠了。他狂叫著:“微軟呀,微軟!我也要在播放器領(lǐng)域與你分一倍羹!一分鐘做一個(gè)播放器,你們的開(kāi)發(fā)人員能做到嘛?兄弟,你任我們公司多媒體開(kāi)發(fā)部的經(jīng)理!”,我聽(tīng)了之后就暈了過(guò)去。過(guò)了半晌才醒過(guò)來(lái)。我剛睜開(kāi)眼睛,就發(fā)現(xiàn)他正在擺弄著RealPlayer控件,并且用近乎神經(jīng)質(zhì)的聲音大喊著:“RealNetworks公司!你們也完了!列祖列宗呀!……”,還沒(méi)聽(tīng)完,我又暈了過(guò)去。
2 遞歸
小菜同志經(jīng)過(guò)兩個(gè)月的治療,終于回到了正常人的狀態(tài)。這兩天又開(kāi)始學(xué)習(xí)編程。有一天他向我請(qǐng)教怎么求一個(gè)數(shù)的階乘,我隨口答道:“用遞歸!”,然后我就出去做別的事情去了。過(guò)了好半天我才回來(lái),發(fā)現(xiàn)他正在電腦前忙乎著。見(jiàn)我進(jìn)來(lái),他狂嘯到:“什么遞歸呀!根本找不到遞歸這個(gè)控件!”,我再暈。
3 大徹大悟
小菜同志這兩天功力見(jiàn)長(zhǎng),目光已經(jīng)不局限于一門(mén)語(yǔ)言。受“聰明的程序員用C++理解Delphi”思想的指導(dǎo),開(kāi)始研習(xí)C++。一日,見(jiàn)小菜同志在電腦前冥思苦想,他見(jiàn)我進(jìn)來(lái)了,馬上裝出大徹大悟狀:“噢!這段C++代碼寫(xiě)得不錯(cuò)呀!又規(guī)范,效率又高!不錯(cuò)!不錯(cuò)!”。我走上前去,看見(jiàn)屏幕上顯示著:
“package mypic
import java.awt.*;
......”
八講的內(nèi)容這么快就講完了,通過(guò)這八講相信大家對(duì)于Delphi控件開(kāi)發(fā)有了大體的了解。但是僅僅了解還不夠,要自己去發(fā)現(xiàn):我做的程序中有沒(méi)有可以提煉成控件的東西?一旦發(fā)現(xiàn)有可以提煉成控件的東西,就要盡力將它抽象化成控件。這樣將能成倍甚至成十倍的提高系統(tǒng)的開(kāi)發(fā)進(jìn)度。
這篇文章其實(shí)是我去年寫(xiě)的,當(dāng)時(shí)發(fā)在了我的個(gè)人網(wǎng)站上,但是由于空間到期了,我就一直沒(méi)有理他。昨天注冊(cè)了一個(gè)blog,所以想把這些東西再放出來(lái),希望能給需要它的朋友帶來(lái)一點(diǎn)幫助。
我在實(shí)際的開(kāi)發(fā)系統(tǒng)中開(kāi)發(fā)了很多實(shí)用的控件,這些組件列表如下:
1、BigStringContainer:可以在程序中以資源的形式嵌入大尺寸的字符串,比如一篇文章,并且通過(guò)一個(gè)屬性就讀取出來(lái)。
2、BrowseComputerDlg:選擇局域網(wǎng)中的所有計(jì)算機(jī)的對(duì)話(huà)框。
3、CheckTreeView:節(jié)點(diǎn)前帶復(fù)選框的樹(shù)控件。
4、DataSet2Excel:將DataSet中的數(shù)據(jù)導(dǎo)出為Excel文件。
5、DataSetHTMLProducer:將DataSet中的數(shù)據(jù)導(dǎo)出為HTML。
6、DBDateTimepk:可以做數(shù)據(jù)綁定的日期、時(shí)間選擇控件。
7、DBLabelEdit:帶Label標(biāo)簽的文本框,可以做數(shù)據(jù)綁定。
8、DBOperatTool:帶有"添加""刪除""修改"“確認(rèn)”“取消”的按鈕的數(shù)據(jù)庫(kù)操縱工具面板。其中【刪除】按鈕點(diǎn)擊的時(shí)候還會(huì)彈出一個(gè)帶“下次不再顯示此對(duì)話(huà)框”的確認(rèn)對(duì)話(huà)框。
9、DBSelectDlgEdit:此控件可以用來(lái)選擇數(shù)據(jù)庫(kù)中已有的數(shù)據(jù),是彈出對(duì)話(huà)框選擇。
10、ExcelCreator:可以生成Excel文件,在沒(méi)有安裝Excel的機(jī)器上也無(wú)可以運(yùn)行。
11、IconContainer:可以在程序中以資源的形式嵌入圖標(biāo),并且通過(guò)一個(gè)屬性就讀取出來(lái)。
12、LincoClock:表盤(pán)時(shí)鐘控件。
13、PopupMsg:像QQ一樣可以在右下角彈出消息框,并且會(huì)超時(shí)自動(dòng)縮回。
14、qqpanel:QQ風(fēng)格的面板。
15、selectdirdlg:選擇文件夾目錄的對(duì)話(huà)框控件。
16、switch:長(zhǎng)得像按鈕開(kāi)關(guān)一樣的控件。
17、sysMenu:可以將一個(gè)PopMenu設(shè)置到窗口的系統(tǒng)菜單中顯示,和“最大化”、“最小化”那些菜單在一起。
18、trayicon:原創(chuàng)的系統(tǒng)拖盤(pán)圖標(biāo)控件。比市面上流行的TrayIcon更小巧,功能一點(diǎn)都不弱,安裝也更加簡(jiǎn)單。
19、urllabel:長(zhǎng)得像超鏈接的Label,用戶(hù)點(diǎn)擊label的時(shí)候自動(dòng)打開(kāi)網(wǎng)址。
20、URLRichEdit:自動(dòng)識(shí)別超鏈接并添加下劃線的URLRichEdit控件。
21、窗口設(shè)計(jì)器,可以實(shí)現(xiàn)類(lèi)似于Delphi那樣的控件拖放效果。
以上控件、源碼每份30元(不帶售后服務(wù),只提供源碼),如果需要提供售后服務(wù)每份再加30元,售后服務(wù)只包括安裝、使用指導(dǎo)、修改Bug等售后服務(wù),不包括增加新功能、修改功能、講解代碼等,如果需要安裝、使用指導(dǎo)、修改Bug等售后服務(wù),費(fèi)用面議。
以上源碼打包購(gòu)買(mǎi)只需要300元,另加200元提供售后服務(wù)(不包括增加新功能、修改功能、講解代碼等)。
以上代碼是個(gè)人開(kāi)發(fā),從未上傳到網(wǎng)上,因此不用費(fèi)勁去網(wǎng)上找泄露版,找不到的。這么長(zhǎng)的《Delphi控件開(kāi)發(fā)淺入深出》系列文章已經(jīng)免費(fèi)提供了,希望大家能夠諒解。上面的源碼、控件都是我的心血之作,我免費(fèi)提供是我的無(wú)私之心,收費(fèi)提供是我的本分,因此請(qǐng)不要說(shuō)黑不黑的問(wèn)題,花幾百元買(mǎi)一件衣服怎么從來(lái)沒(méi)有罵過(guò)服裝商黑呢?我也要養(yǎng)家。
購(gòu)買(mǎi)請(qǐng)到CowNew的淘寶網(wǎng)店: http://item.taobao.com/auction/item_detail.jhtml?x_id=0db2&item_id=2ee8dc9c5459bd770f598989568ce745
比如需要購(gòu)買(mǎi)DataSet2Excel和PopupMsg的源碼,并且需要購(gòu)買(mǎi)PopupMsg的售后,只要拍下3件“CowNew技術(shù)服務(wù)”即可,拍下后我們將盡快向你提供源碼和售后服務(wù).我們還支持手機(jī)充值卡支付,詳見(jiàn)http://www.rupeng.com/index.php/action-channel-name-pengbishop
八、數(shù)據(jù)敏感控件的制作。
Delphi的一大亮點(diǎn)就是它的數(shù)據(jù)庫(kù)開(kāi)發(fā)能力。而數(shù)據(jù)敏感組件則在這中間起著很重要的作用。在Delphi的Data Control頁(yè)面下的控件都是用于顯示和編輯數(shù)據(jù)庫(kù)中的數(shù)據(jù)的。相信大家已經(jīng)體會(huì)到數(shù)據(jù)敏感控件的好處了。我們這一節(jié)就給大家演示一下數(shù)據(jù)敏感控件的開(kāi)發(fā)方法。
需要提醒大家的是,不像其他體系的控件,數(shù)據(jù)敏感控件并沒(méi)有一個(gè)統(tǒng)一的基類(lèi),只要是從TwinControl類(lèi)或其子類(lèi)派生就可以,數(shù)據(jù)敏感控件的特殊之處就在于我們下面提到的數(shù)據(jù)連接。
相信用Delphi開(kāi)發(fā)過(guò)數(shù)據(jù)庫(kù)的人一定對(duì)delphi中沒(méi)有一個(gè)日期數(shù)據(jù)敏感控件而惱火。每次都要我們自己處理數(shù)據(jù)的更新與顯示。所以我們就來(lái)開(kāi)發(fā)一個(gè)DBDateTimePicker控件。
新建一個(gè)控件,從TdateTimePicker派生,源代碼如下:
{*******************************************************}
{?????? Linco TDBDateTimePicker
{?????? mail me: about521@163.com?????????????????????? }
{*******************************************************}
unit DBDateTimePicker;
interface
uses
? SysUtils, Classes, Controls, ComCtrls, DBCtrls, Messages, DB;
type
? TDBDateTimePicker = class(TDateTimePicker)
? private
??? FDataLink: TFieldDataLink;
??? procedure CMGetDataLink(var Msg: TMessage);message CM_GETDATALINK;
??? procedure DataChange(Sender: TObject);
??? procedure EditingChange(Sender: TObject);
??? procedure FSetDataField(AValue: string);
??? procedure FSetDataSource(AValue: TDataSource);
??? procedure FSetReadOnly(AValue: Boolean);
??? procedure ShowData;
??? procedure UpdateData(Sender: TObject);
??? function FGetDataField: string;
??? function FGetDataSource: TDataSource;
??? function FGetField: TField;
??? function FGetReadOnly: Boolean;
? protected
??? procedure Change;override;
??? procedure Notification(AComponent: TComponent;Operation: TOperation);override;
? public
??? constructor Create(AOwner: TComponent); override;
??? destructor Destroy; override;
??? property Field: TField read FGetField;
? published
??? property DataField: string read FGetDataField write FSetDataField;
??? property DataSource: TDataSource read FGetDataSource write FSetDataSource;
??? property ReadOnly: Boolean read? FGetReadOnly write FSetReadOnly;
? end;
procedure Register;
implementation
uses? Variants;
constructor TDBDateTimePicker.Create(AOwner: TComponent);
begin
? inherited Create(AOwner);
? FDataLink := TFieldDataLink.Create;
? FDataLink.OnDataChange := DataChange;
? FDataLink.Control := self;
? FDataLink.OnEditingChange := EditingChange;
? FDataLink.OnUpdateData := UpdateData;
? self.DateTime := Now();
end;
?
destructor TDBDateTimePicker.Destroy;
begin
? FDataLink.Free;
? inherited;
end;
?
procedure TDBDateTimePicker.CMGetDataLink(var Msg: TMessage);
begin
? Msg.Result := Integer(FDataLink);
end;
?
procedure TDBDateTimePicker.DataChange(Sender: TObject);
begin
? if Field<>nil then
??? if Field.Value = null then
????? if (DataSource.DataSet.State = dsEdit)
??????? or (DataSource.DataSet.State = dsInsert) then
??????? Field.AsDateTime := Now();
? ShowData;
end;
?
procedure TDBDateTimePicker.EditingChange(Sender: TObject);
begin
? if (DataSource <> nil) and (DataField <> '') then
??? FDataLink.Edit;
end;
?
procedure TDBDateTimePicker.FSetDataField(AValue: string);
begin
? FDataLink.FieldName := AValue;
end;
?
procedure TDBDateTimePicker.FSetReadOnly(AValue: Boolean);
begin
? FDataLink.ReadOnly := AValue;
end;
?
procedure TDBDateTimePicker.ShowData;
begin
? if (DataSource <> nil) and (DataField <> '') and(Field<>nil)then
? begin
??? case Kind of
??? dtkDate: if Field.AsString <> '' then
?????????????? self.Date := Field.AsDateTime
???????????? else
?????????????? self.Date := Now();
??? dtkTime: if Field.AsString <> '' then
??????? ???????self.Time := Field.AsDateTime
???????????? else
?????????????? self.Time := Now();?
??? else
????? self.DateTime := Now();
??? end;
? end;
end;
?
procedure TDBDateTimePicker.FSetDataSource(AValue: TDataSource);
begin
? FDataLink.DataSource := AValue;
? if AValue <> nil then
??? AValue.FreeNotification(self);
end;
?
procedure TDBDateTimePicker.Change;
begin
? if (DataSource <> nil) and (DataField <> '') then
? begin
??? FDataLink.Edit;
??? Field.Value := self.Text;
? end;
? inherited Change;
end;
?
procedure TDBDateTimePicker.Notification(AComponent: TComponent;Operation: TOperation);
begin
? if (Operation = opRemove) and (FDataLink <> nil) and
????? (AComponent = DataSource) then
??? DataSource := nil;
end;
?
procedure TDBDateTimePicker.UpdateData(Sender: TObject);
var
? t: TFieldType;
begin
? if (DataSource <> nil) and (DataField <> '') then
? begin
??? t := FDataLink.Field.DataType;
??? case t of
??? ftTime: FDataLink.Field.AsDateTime := self.Time;
??? ftDate: FDataLink.Field.AsDateTime := self.Date;
??? ftDateTime: FDataLink.Field.AsDateTime := self.DateTime;
??? end;
? end;
end;
?
function TDBDateTimePicker.FGetDataField: string;
begin
? result := FDataLink.FieldName;
end;
?
function TDBDateTimePicker.FGetDataSource: TDataSource;
begin
? result := FDataLink.DataSource;
end;
?
function TDBDateTimePicker.FGetField: TField;
begin
? result := FDataLink.Field;
end;
?
function TDBDateTimePicker.FGetReadOnly: Boolean;
begin
? result := FDataLink.ReadOnly;
end;
procedure Register;
begin
? RegisterComponents('Linco', [TDBDateTimePicker]);
end;
end.
?
談到開(kāi)發(fā)數(shù)據(jù)敏感控件就不得不說(shuō)數(shù)據(jù)連接(DataLink),數(shù)據(jù)連接有很多種,開(kāi)發(fā)數(shù)據(jù)敏感控件最常用到的就是字段數(shù)據(jù)連接(TFieldDataLink)。數(shù)據(jù)連接是聯(lián)系數(shù)據(jù)敏感控件和數(shù)據(jù)庫(kù)的通道。在數(shù)據(jù)敏感控件中就是憑借著數(shù)據(jù)連接來(lái)處理數(shù)據(jù)的更新和顯示的。從后邊我們的描述中您將更加能體會(huì)到,正是數(shù)據(jù)連接把數(shù)據(jù)在數(shù)據(jù)庫(kù)中的表示反映到用戶(hù)界面中,也是數(shù)據(jù)連接把數(shù)據(jù)從用戶(hù)界面更新到數(shù)據(jù)庫(kù)中。數(shù)據(jù)連接就是一個(gè)“大媒人”(這其實(shí)是設(shè)計(jì)模式中Mediator中介者模式的典型應(yīng)用)。
既然字段數(shù)據(jù)連接這么重要,我們就先來(lái)系統(tǒng)的介紹一下它吧!TfieldDataLink閃亮登場(chǎng)?。。?/SPAN>
TfieldDataLink的屬性:
(1)、property CanModify: Boolean;表示這個(gè)字段是不是只讀的。
(2)、property Control: TComponent;指定這個(gè)字段數(shù)據(jù)連接被連接到哪個(gè)數(shù)據(jù)敏感控件。因?yàn)樽侄螖?shù)據(jù)連接要把它的狀態(tài)改變通知包含它的數(shù)據(jù)敏感控件。
(3)、property Editing: Boolean; 表示這個(gè)字段是不是可以被編輯。
(4)、property Field: TField;表示這個(gè)字段數(shù)據(jù)連接連接的字段。
(5)、property Active: Boolean;表示字段數(shù)據(jù)連接連接的數(shù)據(jù)集是否處于激活狀態(tài)。
(6)、property FieldName: String;字段名。
(7)、property DataSource: TDataSource;表示它連接的數(shù)據(jù)源。
(8)、property DataSet: TDataSet;表示它負(fù)責(zé)維護(hù)的數(shù)據(jù)集。
方法:
(1)、function Edit: Boolean;嘗試設(shè)置字段為編輯狀態(tài)。如果設(shè)置成功則返回True,反之返回False;
事件:
(1)、property OnActiveChange: TNotifyEvent;當(dāng)Active屬性變化的時(shí)候發(fā)生此事件。
(2)、property OnDataChange: TNotifyEvent;當(dāng)數(shù)據(jù)集發(fā)生變化的時(shí)候發(fā)生。
(3)、property OnEditingChange: TNotifyEvent;當(dāng)數(shù)據(jù)源從編輯狀態(tài)變?yōu)槠渌麪顟B(tài)或從其他狀態(tài)變?yōu)榫庉嫚顟B(tài)的時(shí)候發(fā)生。
(4)、property OnUpdateData: TNotifyEvent;當(dāng)向數(shù)據(jù)庫(kù)提交對(duì)數(shù)據(jù)庫(kù)的修改時(shí)發(fā)生此事件。
代碼分析:
(1)、做為一個(gè)數(shù)據(jù)敏感控件,它首先要實(shí)現(xiàn)的功能就是允許用戶(hù)將此控件連接到一個(gè)數(shù)據(jù)源(DataSource)。我們還要用戶(hù)能選擇這個(gè)控件綁定到哪個(gè)字段。
將控件連接到一個(gè)數(shù)據(jù)源,而數(shù)據(jù)源又是一個(gè)控件,所以這就是一個(gè)關(guān)聯(lián)控件屬性方法的應(yīng)用。FsetDataSource中FDataLink.DataSource := AValue;這句代碼是最重要的。就像我們前面講到的數(shù)據(jù)連接就是一個(gè)在數(shù)據(jù)源和數(shù)據(jù)敏感控件之間的媒人,所以數(shù)據(jù)源(DataSource)要告訴媒人是它要被連接到數(shù)據(jù)敏感控件,而不是別人,告訴媒人的唯一方法就是設(shè)定媒人的DataSource為自己(即要綁定的數(shù)據(jù)源)。因?yàn)槲覀兊娘@示日期的控件只能顯示一個(gè)字段,還要告訴媒人自己的哪個(gè)字段要綁定到數(shù)據(jù)敏感控件,這個(gè)通過(guò)數(shù)據(jù)敏感控件的FieldName屬性來(lái)進(jìn)行。即:
procedure TDBDateTimePicker.FSetDataField(AValue: string);
begin
? FDataLink.FieldName := AValue;
end;
(2)、我們還可以為控件增加一個(gè)Field屬性,這樣用戶(hù)就可以通過(guò)DBDateTimePicker.Field.AsString = ‘ok’;這樣的方式對(duì)字段進(jìn)行操作了。當(dāng)然了,這最終還是通過(guò)數(shù)據(jù)連接的Field屬性來(lái)進(jìn)行的。
(3)、由于VCL內(nèi)部通信機(jī)制的要求,數(shù)據(jù)敏感控件要響應(yīng)CM_GETDATALINK事件。只要在事件相應(yīng)函數(shù)里邊把消息的Result域賦值為DataLink的地址就可以了。也就是:
procedure TDBDateTimePicker.CMGetDataLink(var Msg: TMessage);
begin
? Msg.Result := Integer(FDataLink);
end;
(4)、就像DBEdit一樣,在用戶(hù)通過(guò)改變控件中的日期時(shí),應(yīng)該能將改動(dòng)保存到數(shù)據(jù)庫(kù)字段中。我們覆蓋控件的調(diào)度方法Change(在顯示的數(shù)據(jù)變化時(shí)被調(diào)用)以將變化保存到數(shù)據(jù)庫(kù)中。
procedure TDBDateTimePicker.Change;
begin
? if (DataSource <> nil) and (DataField <> '') then
? begin
??? FDataLink.Edit;//設(shè)置數(shù)據(jù)連接為編輯狀態(tài),由這個(gè)媒人將數(shù)據(jù)庫(kù)綁定的字//段設(shè)置為編輯狀態(tài)
??? Field.Value := self.Text;//設(shè)定數(shù)據(jù)字段的值
? end;
? inherited Change;
end;
(5)、回頭再來(lái)看看構(gòu)造函數(shù)吧!
?? ?FDataLink.OnDataChange := DataChange;
?? FDataLink.OnEditingChange := EditingChange;
? FDataLink.OnUpdateData := UpdateData;
? FDataLink.Control := self;
前三句是設(shè)定響應(yīng)數(shù)據(jù)連接事件處理句柄,正是這三句把數(shù)據(jù)庫(kù)中的數(shù)據(jù)與用戶(hù)界面聯(lián)系了起來(lái)。關(guān)于這三個(gè)事件處理句柄的實(shí)現(xiàn)請(qǐng)參加源代碼,這里就不多說(shuō)了。
思考題:
1、做一個(gè)顯示是/否的數(shù)據(jù)敏感控件,當(dāng)這個(gè)控件與一個(gè)布爾類(lèi)型的字段連接的時(shí)候,如果字段的值是0則顯示“否”,如果字段的值是1則顯示“是”;同時(shí)可以接受用戶(hù)的修改,當(dāng)用戶(hù)在控件上單擊一次鼠標(biāo),布爾值就翻轉(zhuǎn)一次。
對(duì)話(huà)框控件的制作
Delphi中有很多對(duì)話(huà)框組件,例如TopenDialog、TfontDialog等。這些控件的特點(diǎn)就是雖然是不可視控件,但是在運(yùn)行時(shí)都有一個(gè)可視化的效果,比如TopenDialog的可視化效果就是一個(gè)打開(kāi)對(duì)話(huà)框。我們這次將開(kāi)發(fā)一個(gè)日期對(duì)話(huà)框控件,當(dāng)我們調(diào)用控件的Execute方法(不一定非要使用Execute方法,不過(guò)大部分對(duì)話(huà)框控件都是使用這個(gè)方法,我們也就按照慣例來(lái)了)時(shí),就會(huì)彈出一個(gè)可以選擇日期的對(duì)話(huà)框,我們選擇一個(gè)日期后,點(diǎn)擊“確定”則Execute返回True,點(diǎn)擊“取消”則Execute返回False。我們可以讀取Date屬性來(lái)得到用戶(hù)選擇的日期,也可以修改此屬性來(lái)改變對(duì)話(huà)框的初始日期。
1、新建一個(gè)對(duì)話(huà)框。在對(duì)話(huà)框窗體上放置一個(gè)TmonthCalendar組件,命名為Cal,窗體名稱(chēng)改為FormDate。在窗體上放置兩個(gè)按鈕,一個(gè)按鈕的Caption為“確定(&O)”,ModalResult為mrOk,一個(gè)按鈕的Caption為“取消(&C)”,ModalResult為mrCancel。設(shè)計(jì)好的窗體如下圖所示:
2、為窗體添加兩個(gè)Public訪問(wèn)級(jí)的方法:
??? function GetSelDate: TDate;
??? procedure SetInitDate(AValue: TDate);
代碼如下:
function TFormDate.GetSelDate: TDate;
begin
? result := cal.Date;
end;
?
procedure TFormDate.SetInitDate(AValue: TDate);
begin
? cal.Date := AValue;
end;
3、新建一個(gè)控件,派生自Tcomponent。
代碼如下:
unit DateDialog;
?
interface
?
uses
? SysUtils, Classes, Controls, frmDlg;
?
type
? TDateDialog = class(TComponent)
? private
??? FDlg: TFormDate;
??? function GetDate: TDate;
??? procedure SetDate(AValue: TDate);
? protected
? public
??? constructor Create(AOwner: TComponent);override;
??? function Execute: Boolean;
? published
??? property Date: TDate read GetDate write SetDate;
? end;
?
procedure Register;
?
implementation
procedure Register;
begin
? RegisterComponents('Linco', [TDateDialog]);
end;
constructor TDateDialog.Create(AOwner: TComponent);
begin
? inherited Create(AOwner);
? FDlg := TFormDate.Create(self);
end;
?
function TDateDialog.Execute: Boolean;
begin
? result := (FDlg.ShowModal = mrOK);
end;
?
function TDateDialog.GetDate: TDate;
begin
? result := FDlg.GetSelDate;
end;
?
procedure TDateDialog.SetDate(AValue: TDate);
begin
? FDlg.SetInitDate(AValue);
end;
end.
代碼比較簡(jiǎn)單就不多解釋了。
思考題:
1、做一個(gè)模仿TcolorDialog的對(duì)話(huà)框控件。
六、控件手拉手――控件關(guān)聯(lián)的實(shí)現(xiàn)
控件的關(guān)聯(lián)在Delphi中也是很常見(jiàn)的,我們可以設(shè)定一個(gè)控件的某個(gè)屬性指向另一個(gè)控件。比如我們?cè)诖绑w上放上Tedit,TpopupMenu兩個(gè)控件,然后設(shè)定Tedit的PopupMenu屬性為TpopupMenu控件,運(yùn)行后在Tedit點(diǎn)擊右鍵就會(huì)彈出剛才設(shè)定的那個(gè)TpopupMenu菜單,也就是說(shuō)Tedit,TpopupMenu聯(lián)手完成了任務(wù)。再比如TDBEdit控件的DataSource屬性就可以指向一個(gè)TdataSource控件,這樣就可以在TDBEdit控件中顯示TdataSource輸出的某個(gè)字段的值了。
? 下面我們將寫(xiě)一個(gè)簡(jiǎn)單的實(shí)現(xiàn)控件關(guān)聯(lián)的控件。這個(gè)控件派生于Tedit,它可以與一個(gè)Tlabel控件關(guān)聯(lián),在控件的編輯框中輸入文字時(shí),與它關(guān)聯(lián)的Tlabel控件的文字將隨著它而變化。代碼如下:
unit MyEdit;
interface
uses
? SysUtils, Classes, Controls, StdCtrls;
type
? TMyEdit = class(TEdit)
? private
??? FLinkLabel: TLabel;
??? procedure FSetLinkLabel(AValue: TLabel);
? protected
??? procedure Notification(AComponent: TComponent;Operation: TOperation);
????????? override;
??? procedure Change;override;
? public
? published
??? property LinkLabel: TLabel read FLinkLabel write FSetLinkLabel;
? end;
?
procedure Register;
?
implementation
procedure Register;
begin
? RegisterComponents('Linco', [TMyEdit]);
end;
procedure TMyEdit.Change;
begin
? inherited;
? if LinkLabel <> nil then
??? LinkLabel.Caption := Text;
end;
?
procedure TMyEdit.FSetLinkLabel(AValue: TLabel);
begin
? FLinkLabel := AValue;
? if AValue <> nil then
??? FLinkLabel.FreeNotification(self);
end;
?
procedure TMyEdit.Notification(AComponent: TComponent;
? Operation: TOperation);
begin
? inherited;
? if (Operation = opRemove) and (AComponent = LinkLabel) then
??? LinkLabel := nil;
end;
end.
代碼解釋?zhuān)?/SPAN>
(1)、我們只要將控件的任意一個(gè)屬性的類(lèi)型設(shè)定為另外一個(gè)控件的類(lèi)名稱(chēng),那么我們就可以在控件的Object Inspector中將這個(gè)屬性指向那個(gè)控件(或那個(gè)控件的派生控件)的一個(gè)實(shí)例。比如本例中我們?cè)黾恿?/SPAN>LinkLabel屬性,它的類(lèi)型為 Tlabel,所以我們就可以把LinkLabel屬性指向一個(gè)標(biāo)簽控件。
(2)、請(qǐng)注意FsetLinkLabel中的這段代碼:
?if AValue <> nil then
FLinkLabel.FreeNotification(self);
如果我們將控件關(guān)聯(lián)屬性指向了一個(gè)控件,可是后來(lái)又將被指向的控件刪除了,那么我們的控件關(guān)聯(lián)屬性是不會(huì)自動(dòng)刪除的,這樣就會(huì)造成控件關(guān)聯(lián)屬性指向的控件不存在的現(xiàn)象。我們必須自動(dòng)感知被關(guān)聯(lián)控件的刪除并重新設(shè)定控件關(guān)聯(lián)屬性為不指向任何控件,這樣就避免了錯(cuò)誤的發(fā)生。
FLinkLabel.FreeNotification(self);的作用就是這樣的。它調(diào)用控件的FreeNotification方法(在Tcomponent中定義)向被指向的控件注冊(cè)一個(gè)“消息”,當(dāng)被指向控件被刪除時(shí),會(huì)向所有向他注冊(cè)的控件發(fā)送一個(gè)它被刪除的消息,此時(shí)向他注冊(cè)的控件就會(huì)觸發(fā)Notification方法,這樣我們就可以自動(dòng)感知被指向控件的狀態(tài)了。這是設(shè)計(jì)模式中Observer(觀察者)模式的典型應(yīng)用。
既然向他注冊(cè)的控件就會(huì)觸發(fā)Notification方法,我們就覆蓋父類(lèi)的Notification方法,寫(xiě)出如下的代碼:
? if (Operation = opRemove) and (AComponent = LinkLabel) then
??? LinkLabel := nil;
這句話(huà)的意思是:如果控件被刪除并且被刪除的控件(因?yàn)槲覀兊目丶赡芟蚨鄠€(gè)控件注冊(cè)了消息)是LinkLabel,那么我們就設(shè)定LinkLabel屬性不指向任何控件。
(3)覆蓋父類(lèi)的Change調(diào)度方法。在此方法里為連接的LinkLabel的Caption賦值就達(dá)到我們的目的了。
思考題:
1、做一個(gè)Label控件,給它增加一個(gè)DataSource屬性,該屬性可以指向一個(gè)TdataSource類(lèi)型的控件,它有一個(gè)GetRecordCount方法。當(dāng)調(diào)用此方法時(shí),就在Label控件中顯示這個(gè)DataSource對(duì)應(yīng)的數(shù)據(jù)集中的記錄的條數(shù)。
五、復(fù)合控件
復(fù)合控件是Delphi控件中非常重要的一種控件,復(fù)合控件就是將兩個(gè)或兩個(gè)以上的控件重新組合成一個(gè)新的控件。例如TspinEdit、TlabeledEdit、TDBNavigator等就是復(fù)合控件,TDBNavigator其實(shí)就是在一個(gè)Panel放上若干個(gè)Button而已。制作一個(gè)復(fù)合控件時(shí),我們一般從TwinControl派生控件。
我們這次做的控件是擁有一個(gè)Edit編輯框和一個(gè)Button按鈕的復(fù)合控件,在用戶(hù)在編輯框中輸入文字的過(guò)程中,Button將隨時(shí)顯示編輯框中文字的長(zhǎng)度。我們把控件的源碼先展示給大家。
unit EditButton;
interface
uses
? SysUtils, Classes, Controls, StdCtrls, Messages;
type
? TEditButton = class(TWinControl)
? private
??? FEdit: TEdit;
??? FButton: TButton;
??? FText: string;
??? procedure FSetText(AValue: string);
??? procedure OnEditChange(Sender: TObject);
? protected
??? procedure WMSize(var Msg: TMessage);message WM_SIZE;
? public
??? constructor Create(AOwner: TComponent);override;
??? destructor Destroy;override;
? published
??? property Text: string read FText write FSetText;
? end;
?
procedure Register;
implementation
?
procedure Register;
begin
? RegisterComponents('Linco', [TEditButton]);
end;
?
constructor TEditButton.Create(AOwner: TComponent);
begin
? inherited;
? FEdit := TEdit.Create(nil);
? FEdit.Parent := self;
? FEdit.Top := 0;
? FEdit.Left := 0;
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
? FEdit.OnChange := OnEditChange;
? FButton := TButton.Create(nil);
? FButton.Parent := self;
? FButton.Top := 0;
? FButton.Left := Width div 2;
? FButton.Height := Height;
? FButton.Width := Width div 2;
end;
?
destructor TEditButton.Destroy;
begin
? FEdit.Free;
? FButton.Free;
? inherited;
end;
?
procedure TEditButton.FSetText(AValue: string);
begin
? FEdit.Text := AValue;
end;
?
procedure TEditButton.OnEditChange(Sender: TObject);
begin
? FButton.Caption := IntToStr(Length(FEdit.Text));
end;
?
procedure TEditButton.WMSize(var Msg: TMessage);
begin
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
? FButton.Left := Width div 2;
? FButton.Height := Height;
? FButton.Width := Width div 2;
end;
end.
代碼解釋?zhuān)?/SPAN>
(1)、我們首先定義了兩個(gè)變量??
?? ?FEdit: TEdit;
??? FButton: TButton;
? 分別代表復(fù)合控件中的文字編輯框和按鈕。
(2)所謂復(fù)合控件說(shuō)簡(jiǎn)單一點(diǎn)就是在一個(gè)共同的基板上將組成復(fù)合控件的各個(gè)控件(可以叫做子控件)畫(huà)出來(lái)。所以我們?cè)跇?gòu)造函數(shù)中建立各個(gè)子控件,然后分別設(shè)定它們的位置等屬性。
以文字編輯框?yàn)槔?/SPAN>
FEdit := TEdit.Create(nil);
的作用是建立編輯框控件。如果Create的參數(shù)指定為nil,則子控件在設(shè)計(jì)狀態(tài)是可以響應(yīng)用戶(hù)的操作的;而如果設(shè)定為self(即設(shè)定子控件的父控件為基板),則子控件在設(shè)計(jì)時(shí)時(shí)不可響應(yīng)用戶(hù)操作的,如果設(shè)定為self則析構(gòu)函數(shù)中就不用Fedit.Free來(lái)銷(xiāo)毀對(duì)象了,對(duì)象會(huì)自動(dòng)銷(xiāo)毀。
? FEdit.Parent := self;的作用是設(shè)定子控件的父控件,如果沒(méi)有這一句則控件是無(wú)法顯示的。
? FEdit.Top := 0;
? FEdit.Left := 0;
? FEdit.Height := Height;
? FEdit.Width := Width div 2;
這四句是設(shè)定控件在基板上的相對(duì)位置的,這里的Top,Left不是相對(duì)于窗體的,而是相對(duì)于基板的。
? FEdit.OnChange := OnEditChange;
則是設(shè)定編輯框控件的OnChange(文字改變事件)的處理句柄為OnEditChange;
(1)??? 用戶(hù)有可能在設(shè)計(jì)時(shí)或運(yùn)行時(shí)通過(guò)代碼改變控件的大小,這時(shí)控件中子控件的順序就會(huì)變得亂七八糟,所以需要相應(yīng)控件的WM_SIZE事件(控件大小發(fā)生變化的事件)重新設(shè)定子控件的位置,大小等。函數(shù)WMSize的作用就是這樣的。
安裝控件后發(fā)現(xiàn)控件已經(jīng)可以正確運(yùn)行了,但是還有一個(gè)問(wèn)題,就是這個(gè)控件沒(méi)有了Onclick,Onchange等必須的屬性。我們只要為控件增加事件處理句柄屬性,然后把事件處理句柄屬性的讀寫(xiě)方法都指向子控件的事件處理句柄屬性即可。例如我們?yōu)榭丶黾?/SPAN>OnClick事件,這個(gè)事件發(fā)生在用戶(hù)單擊按鈕時(shí),我么只要在Pulished部分增加如下代碼:
property? OnClick:? TnotifyEvent read GetOnClick write SetOnClick
在Private中增加如下方法聲明:
function GetOnclick: TnotifyEvent;
procedure SetOnclick(AValue: TnotifyEvent);
這兩個(gè)方法的實(shí)現(xiàn)分別為:
function? TeditButton. GetOnclick: TnotifyEvent;
begin
? result := Fbutton.Onclick;
end;
procedure TeditButton. SetOnclick(AValue: TnotifyEvent);
begin
? Fbutton.OnClick := Avalue;
end;
思考題:
1、做一個(gè)模仿播放器中的操作按鈕的復(fù)合控件,控件由三個(gè)按鈕組成,分別是“播放”、“暫停”、“停止”,請(qǐng)按照正常的邏輯關(guān)系,處理這三個(gè)按鈕的可用/不可用關(guān)系。(提示:可以參考TDBNavigator的源代碼)
四、對(duì)特定字符串敏感的Edit控件
我們這個(gè)控件將演示控件的自定義事件的書(shū)寫(xiě)。這個(gè)控件有一個(gè)類(lèi)型為string的SensitiveText屬性,當(dāng)用戶(hù)在輸入框中輸入的文字為InvalidText時(shí)就會(huì)觸發(fā)OnSensitiveText事件。按照慣例,我先把源碼展示給大家:
unit TextSenseEdit;
?
interface
?
uses
? SysUtils, Classes, Controls, StdCtrls;
type
? TSensitiveTextEvent = procedure(AText: string) of object;//方法指針
? TTextSenseEdit = class(TEdit)
? private
??? FSensitiveText: string;
??? FOnSensitiveText: TSensitiveTextEvent;
??? procedure SetSensitiveText(AValue: string);
? protected
??? procedure Change;override;
? public
? published
??? property SensitiveText: string read FSensitiveText write SetSensitiveText;
??? property OnSensitiveText: TSensitiveTextEvent read FOnSensitiveText write FOnSensitiveText;
? end;
?
procedure Register;
?
implementation
?
procedure Register;
begin
? RegisterComponents('Linco', [TTextSenseEdit]);
end;
procedure TTextSenseEdit.Change;
begin
? inherited;
? if Text = SensitiveText then
??? if Assigned(OnSensitiveText) then
????? OnSensitiveText(Text);
end;
?
procedure TTextSenseEdit.SetSensitiveText(AValue: string);
begin
? FSensitiveText := AValue;
end;
end.
代碼解釋?zhuān)?/SPAN>
(1)、SensitiveText屬性的添加方法大家已經(jīng)熟悉了,這里不多解釋。
(2)、正如大家猜測(cè)的,Change方法正是編輯框文字發(fā)生變化時(shí)的調(diào)度方法,它將引起OnChange事件。我們可以在這個(gè)方法中監(jiān)控編輯框文字發(fā)生的變化,當(dāng)文字等于SensitiveText就觸發(fā)OnSensitiveText事件(具體的實(shí)現(xiàn)方法在后邊解釋?zhuān)?/SPAN>
(3)、Delphi中的控件的事件機(jī)制是通過(guò)方法指針來(lái)實(shí)現(xiàn)的。聲明方法指針的格式為:
方法指針名稱(chēng) = procedure(參數(shù)列表) of object;
聲明事件屬性的方法與聲明普通屬性的方法相同。在我們這個(gè)例子中,我們首先聲明一個(gè)FOnSensitiveText: TSensitiveTextEvent;私有變量,然后property OnSensitiveText: TSensitiveTextEvent read FOnSensitiveText write FOnSensitiveText; 聲明事件屬性。這樣注冊(cè)控件后,當(dāng)用戶(hù)把控件放到窗體中后,就會(huì)在Object Inspector中Evnets頁(yè)中出現(xiàn)OnSensitiveText事件,我們就可以像使用其他事件一樣使用這個(gè)事件了。
? 但是我們現(xiàn)在只是聲明了一個(gè)事件屬性,并沒(méi)有書(shū)寫(xiě)任何代碼來(lái)激發(fā)這個(gè)事件。我們應(yīng)該在合適的時(shí)候激發(fā)此事件,顯而易見(jiàn)我們應(yīng)該在Change方法中激發(fā)此事件:
procedure TTextSenseEdit.Change;
begin
? inherited;
? if Text = SensitiveText then
??? if Assigned(OnSensitiveText) then
????? OnSensitiveText(Text);
end;
當(dāng)if Text = SensitiveText時(shí)就判斷控件使用者是否為OnSetSensitiveText寫(xiě)代碼了(準(zhǔn)確的說(shuō)是是否為OnSetSensitiveText事件句柄賦值了),如果寫(xiě)代碼了則調(diào)用OnSetSensitiveText(Text);來(lái)激發(fā)OnSetSensitiveText事件,并把控件的Text傳遞給方法的Avalue參數(shù)。正如“方法指針”這個(gè)名字一樣,被聲明為方法指針類(lèi)型的變量可以當(dāng)作方法使用,用來(lái)激發(fā)事件。VCL已經(jīng)為我們預(yù)定義了一些常用的事件句柄,我們直接拿來(lái)使用:TnotifyEvent,TmouseEvent,TmouseMoveEvent,TkeyPressEvent等,具體可以參考VCL源碼。
思考題:
1、做一個(gè)支持累加運(yùn)算的文本編輯框控件,用戶(hù)可以在編輯框中輸入正整數(shù)。當(dāng)用戶(hù)按回車(chē)時(shí),如果編輯框中輸入的不是正整數(shù)(為負(fù)數(shù)、小數(shù)或一般字符串)則觸發(fā)控件的OnError事件;如果輸入的是正整數(shù),則開(kāi)始計(jì)算從1到用戶(hù)輸入的那個(gè)正整數(shù)中所有整數(shù)的和(用1+2+3+……這種累加的辦法實(shí)現(xiàn),不要用(1+n)*n/2這種直接計(jì)算的方法),并且在計(jì)算工程中如果發(fā)現(xiàn)計(jì)算的中間結(jié)果位數(shù)是5,則觸發(fā)OnTailFive事件。
三、開(kāi)關(guān)控件TlincoSwitch
用過(guò)Delphi1(好古老的東東呀?。┑娜讼嘈哦加浀眠@個(gè)開(kāi)關(guān)控件
1、建立位圖資源文件:
用Image Editor建立一個(gè)Res文件,并在文件中分別建立下面兩個(gè)位圖,并分別命名為SWITCHON、SWITCHOFF。保存此Res到控件單元所在目錄下。
2、寫(xiě)控件代碼。
unit LincoSwitch;
interface
uses
? SysUtils, Classes, Controls, Graphics, Windows;
?
type
? TLincoSwitch = class(TCustomControl)
? private
??? FIsOn: Boolean;
??? FPicOn: Graphics.TBitmap;
??? FPicOff: Graphics.TBitmap;
??? procedure FSetIsOn(AValue: Boolean);
? protected
??? procedure Click;override;
??? procedure Paint;override;
? public
??? constructor Create(AOwner: TComponent);override;
??? destructor Destroy;override;
? published
??? property IsOn: Boolean read FIsOn write FSetIsOn;
??? property OnClick;
??? property OnKeyDown;
??? property OnKeyPress;
??? property OnKeyUp;
??? property OnCanResize;
??? property OnDblClick;
??? property OnMouseDown;
??? property OnMouseMove;
??? property OnMouseUp;
??? property OnMouseWheel;
??? property OnResize;
? end;
?
procedure Register;
implementation
{$R *.res}
procedure LoadBitmapFromRes(ABitmapId: string; ABitmap: Graphics.TBitmap);
begin
? ABitmap.LoadFromResourceName(hInstance, ABitmapId);//從資源文件中讀取位圖
end;
?
constructor TLincoSwitch.Create(AOwner: TComponent);
begin
? inherited Create(AOwner);
? FPicOn := Graphics.TBitmap.Create;
? FPicOff := Graphics.TBitmap.Create;
? LoadBitmapFromRes('SWITCHON', FPicOn);
? LoadBitmapFromRes('SWITCHOFF', FPicOff);
? Invalidate;
end;
?
destructor TLincoSwitch.Destroy;
begin
? FPicOn.Free;
? FPicOff.Free;
? inherited;
end;
?
procedure TLincoSwitch.Click;
begin
? IsOn := not IsOn;//改變按鈕的狀態(tài)
? Invalidate;
? inherited;
end;
?
procedure TLincoSwitch.Paint;
begin
//畫(huà)開(kāi)關(guān)圖案?
if IsOn then
???
????? StretchBlt(Canvas.Handle, 0, 0, self.Width, self.Height, FPicOn.Canvas.Handle,
???????????? 0, 0, FPicOn.Width, FPicOn.Height,SRCCOPY)
? else
????? StretchBlt(Canvas.Handle, 0, 0, self.Width, self.Height, FPicOff.Canvas.Handle,
???????????? 0, 0, FPicOff.Width, FPicOff.Height,SRCCOPY);
end;
?
?
procedure TLincoSwitch.FSetIsOn(AValue: Boolean);
begin
? FIson := AValue;
? Invalidate;
end;
?
procedure Register;
begin
? RegisterComponents('Linco', [TLincoSwitch]);
end;
end.
3、代碼分析
? (1)、因?yàn)槲覀円诳丶砻嫔蠈粹o的圖案畫(huà)出來(lái),所以我們選擇TcustomControl做為父類(lèi)控件,因?yàn)樗袀€(gè)Canvas屬性,我們可以利用Canvas在控件表面作圖。不選用Tcontrol的原因是因?yàn)樗泻芏辔覀儾恍枰膶傩浴?/SPAN>
(2)、ABitmap.LoadFromResourceName(hInstance, ABitmapId);是從資源文件中讀取Id為AbitmapId的位圖,關(guān)于資源文件的使用請(qǐng)參考其他相關(guān)資料。注意代碼中的“{$R *.res}”,它的作用是將資源文件編譯到程序文件中,如果沒(méi)有這個(gè)預(yù)編譯條件,程序?qū)?huì)出現(xiàn)錯(cuò)誤。
(3)、StretchBlt是將位圖畫(huà)到畫(huà)板上,使用方法請(qǐng)參考MSDN。
(4)、我們?yōu)榭丶黾恿?/SPAN>IsOn屬性。這個(gè)布爾屬性用來(lái)表示開(kāi)關(guān)的狀態(tài)(開(kāi)/關(guān))。
從property IsOn: Boolean read FIsOn write FSetIsOn;我們可以看出這個(gè)屬性是個(gè)可讀可寫(xiě)的屬性。當(dāng)讀這個(gè)屬性時(shí)會(huì)將FisOn的值返回給調(diào)用者,而寫(xiě)屬性時(shí)則會(huì)調(diào)用FsetIsOn方法,并將賦給屬性的值做為參數(shù)傳遞給FsetIsOn。在FsetIsOn方法中,有如下實(shí)現(xiàn)代碼:
? FIson := AValue;
? Invalidate;
首先將Fison設(shè)置為參數(shù)傳遞來(lái)的值,然后調(diào)用? Invalidate;要求重畫(huà)控件,以告訴用戶(hù)控件的狀態(tài)已經(jīng)改變,這一點(diǎn)是使用寫(xiě)字段無(wú)法做到的。
(5)???
??? FPicOn: Graphics.TBitmap;
FPicOff: Graphics.TBitmap;
是聲明兩個(gè).Tbitmap類(lèi)型變量以保存控件的開(kāi)關(guān)兩種狀態(tài)的圖案。
(6)
??? procedure Click;override;
procedure Paint;override;
分別是覆蓋父類(lèi)中相應(yīng)的調(diào)度方法。當(dāng)控件被鼠標(biāo)單擊時(shí),Click方法會(huì)被調(diào)用,我們將在Click中改變控件的開(kāi)關(guān)狀態(tài);Paint方法則在用戶(hù)調(diào)用? Invalidate方法或控件發(fā)生重畫(huà)時(shí)調(diào)用,我們一般在這個(gè)方法繪制控件的圖案。
(7)、TcustomControl中又很多事件處理句柄。比如OnClick、OnKeyDown等,但是它把他們聲明成了Protected保護(hù)級(jí)別,所以我們?cè)?/SPAN>Object Inspector中看不到他們,如果我們要他們可以在Object Inspector中被用戶(hù)編輯的話(huà),只要在Published中重新聲明他們即可,不用寫(xiě)他們的讀寫(xiě)方法,只要使用:Property? 屬性名;
這樣的方法就可以。比如這個(gè)例子中的:Property Onclick;
思考題:
1、??????? 做一個(gè)有特效的按鈕控件,當(dāng)鼠標(biāo)按下時(shí)按鈕是一個(gè)紅色邊框的空心圓,當(dāng)鼠標(biāo)松開(kāi)時(shí)按鈕是一個(gè)淡綠色邊框的空心圓。
2、??????? 對(duì)于父類(lèi)控件中為protected的屬性,如果想將它在子類(lèi)控件中公布,應(yīng)該怎么做?請(qǐng)思考Delphi為什么要將一些屬性設(shè)為protected級(jí)別?