下载此文档

VCL组件开发实例.pdf


文档分类:IT计算机 | 页数:约19页 举报非法文档有奖
1/19
下载提示
  • 1.该资料是网友上传的,本站提供全文预览,预览什么样,下载就什么样。
  • 2.下载该文档所得收入归上传者、原创者。
  • 3.下载的文档,不会出现我们的网址水印。
1/19 下载此文档
文档列表 文档介绍
该【VCL组件开发实例 】是由【小s】上传分享,文档一共【19】页,该文档可以免费在线阅读,需要了解更多关于【VCL组件开发实例 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。:.
通过实例看VCL组件开发全过程
这篇文章算是对我前段时间学****的一个学****总结,以及对自己学****过程的一个回顾。本
文通过一个简单的例子来尽可能的展示VCL组件开发的各个方面,本文针对即将学****组件
开发的初学者,如果你已经熟悉组件开发或认为本文内容过于基础简单,那么本文对你毫无
用处。阅读本文,假设你已经熟悉delphi的普通程序设计以及vcl的结构层次,还有一些重
要的关键字:published、property等。(注:)
在这篇文章中我们将建立一个和时间有关的组件,这个组件通过设置它的不同状态有以
下基本功能:1、显示系统的当前时间(包括设置闹钟)。2、跑表。3、倒计时。这是一个简
单的例子,然而我们将在这个例子中尽可能多的用到delphi在组件开发中的多种特性,你可
以通过以下列举出的本文涉及特性有选择的阅读:
·组件和组件包
·组件的属性类别
·组件的属性编辑器
·组件编辑器
一、组件和组件包,以及一些你应该知道的文件类型:
组件和组件包的关系就如同普通工程中unit和工程文件的关系一样,通常你所安装的
组件都是以组件包的形式发布的,一个组件包中可以有很多个组件,在组件开发中,组件包
(我们把他叫做TClock)并将它包括在我们
自己的组件包(ClockPackage)中,我们选择Filenewother在弹出的窗口中的New页选
择Package新建一个组件包,得到一个组件包窗口,查看这个组件包的原文件(.dpk),得
到以下代码:
packageClockPackage;
{$R*.res}
{$ALIGN8}
{$ASSERTIONSON}:.
…….
…….
{$DESCRIPTION'OurClockPack'}
{$IMPLICITBUILDOFF}
requires
rtl;
end.
这个文件其实就是组件开发中的工程文件,requires关键字指示了组件包所需组件包的
列表,随着向组件包中加入组件(类似于单元文件),你还会看到contains关键字,指示了
组件包所包含的组件,你可以通过组件包窗口中的add和remove按纽来添加新的组件和删
除已有的组件。另外这个代码中所包含的大量的编译器开关大多都可以在组件包窗体上的
Options中设置。这里需要补充说明的是组件包的3种重要属性(都在Options中):Designtime
Only、RuntimeOnly、Designtimeandruntime(这3个词的意思有英语基础的朋友应该都知
道吧),对于大多数的组件包我们只要选择最后一个就可以了,然而有些组件包设计为只运
行时(这样你用这套组件开发的程序不能脱离组件而单独运行,组件包也不能被安装),有
些组件包被设计为只设计时(这将在后文有更详细的说明)。
了解了组件和组件包,我们对组件开发中可能出现的一些你没有见过的文件做一些说
明: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部分的一个新的过程:procedureRegister;(注意:
delphi规定Register的R必须大写,这是一个保留字),这个过程是作为每一个组件所必须
有的,它完成组件的注册,包括组件本身以及如属性编辑器等多种组件特性的注册):
procedureRegister;
begin
RegisterComponents('ClockAndTime',[TClock]);
//这个过程注册组件本身,注意到前面定义的ClockAndTime页了吗?
//这里在后面还会出现一些新的过程,包括注册组件的属性类别等等。
end;
在下一篇中我们将给出这个组件的全部原代码
组件的代码由于假设你已经熟悉delphi开发(它和一般开发没什么不同),我们就直接
贴出来并加上适当的注释:
unitClock;
interface
uses
SysUtils,Classes,Controls,StdCtrls,ExtCtrls;
type
TState=(StClock,StRunClock,StBackClock);//定义枚举类表示控件的3种状态:时钟、
跑表、倒计时钟
TClock=class(TCustomLabel):.
private
fState:TState;
fTimer:TTimer;//为什么使用这个组件作为我们组件的私有成员就不用说了吧
RCD:array[1..8]ofinteger;//跑表中的各个数位。
fBeginTime:string;//到计时时的开始时钟,之所以没用TTime类型是为了在后面演示
属性编辑器
fWakeTime:string;//闹钟时间,出于和上面同样的理由
fAllowWake:boolean;//是否开启闹钟功能
fOnWakeUp:TNotifyEvent;//为了使组件更加完美,我们允许组件用户能够响应闹钟
到来时的时件
fOnTimeUp:TNotifyEvent;//同上能够响应倒计时种完成时的事件,我们将发布这两
个事件
functionGetActive:boolean;//控制Timer是否工作以控制3种状态的钟是否工作
procedureSetActive(Value:boolean);
procedureSetState(Value:TState);
procedureSetBeginTime(Value:string);
procedureSetWakeTime(Value:string);
protected
procedureWalkClock(sender:TObject);//作为时钟时走种的事件
procedureRunClock(sender:TObject);//跑表
procedureBackClock(sender:TObject);//倒计时
public
constructorCreate(AOwner:TComponent);override;//完成一些初始化工作
procedureReSetRunClock;//跑表和倒计时都需要一个复位方法给组件使用者调用
procedureReSetBackClock;
published
propertyState:TStatereadfStatewriteSetStatedefaultStClock;//默认为时钟状态
propertyActive:booleanreadGetActivewriteSetActive;//控制3种状态的钟是否工作
propertyBeginTime:stringreadfBeginTimewriteSetBeginTime;
propertyWakeTime:stringreadfWakeTimewriteSetWakeTime;:.
propertyAllowWake:booleanreadfAllowWakewritefAllowWake;
propertyOnWakeUp:TNotifyEventreadfOnWakeUpwritefOnWakeUp;
propertyOnTimeUp:TNotifyEventreadfOnTimeUpwritefOnTimeUp;
//最后我们再发布一些被TCustomLabel所隐藏而我们又需要的属性
propertyAlign;
propertyAlignment;
propertyColor;
propertyFont;
propertyParentColor;
propertyParentFont;
propertyParentShowHint;
propertyPopupMenu;
propertyShowHint;
propertyVisible;
propertyTransparent;
propertyOnClick;
end;
procedureRegister;
implementation
procedureRegister;
begin
RegisterComponents('ClockAndTime',[TClock]);
end;
{TClock}
(AOwner:TComponent);:.
begin
inheritedCreate(AOwner);
//设置默认值
fTimer:=(self);
//将它属于我们的组件,这样便不用编写析构函数,而可以自动在释放本组件时释放
Timer
Active:=false;
AllowWake:=false;
State:=StClock;
BeginTime:='00:00:00';
WakeTime:='00:00:00';
end;
:boolean;
begin
result:=;
end;
(Value:boolean);
begin
:=Value;
end;
(Value:TState);
var
i:integer;
begin
caseValueof
StClock:
begin:.
Active:=false;
:=1000;
:=WalkClock;
Active:=true;
end;
StRunClock://由于Time类型不好处理微秒操作,我们只有手工模仿这个操作,代码
会稍微烦琐
begin
Active:=false;
fori:=1to8doRCD[i]:=0;
Caption:=IntToStr(RCD[8])+IntToStr(RCD[7])+':'+IntToStr(RCD[6])+IntToStr(RCD[5])+':'+IntT
oStr(RCD[4]);
Caption:=Caption+IntToStr(RCD[3])+':'+IntToStr(RCD[2])+IntToStr(RCD[1]);
:=10;
//经过测试,这个秒表的效果很好,然而这只是一个技术上的演示,
//实际上这么频繁(1/100秒)的不断执行RunClock会使CPU的占用一直达到100%
//这并不是一个好注意。事实上要想在跑表中显示微秒级别并做到合理的占用CPU
//这需要更加灵活和复杂的编程
:=RunClock;
end;
StBackClock:
begin
Active:=false;
Caption:=BeginTime;
:=1000;
:=BackClock;
end;
end;
fState:=Value;:.
end;
(Value:string);
begin
try
StrToTime(Value);
fBeginTime:=Value;
ifState=StBackClockthen
begin
Active:=false;
Caption:=Value;
end;
except
onExceptiondo
begin
fBeginTime:='00:00:00';
ifState=StBackClockthenCaption:='00:00:00';
end;
end;
end;
(Value:string);
begin
try
StrToTime(Value);
fWakeTime:=Value;
except
onExceptiondo
begin
fWakeTime:='00:00:00';:.
end;
end;
end;
(sender:TObject);
begin
Caption:=TimeToStr(Time);
ifAllowWakeand(StrToTime(Caption)=StrToTime(WakeTime))then
begin
Beep;//蜂鸣器
ifAssigned(fOnWakeUp)then
fOnWakeUp(self);
end;
end;
(sender:TObject);
begin
RCD[1]:=RCD[1]+1;
ifRCD[1]=10thenbeginRCD[2]:=RCD[2]+1;RCD[1]:=0;end;
ifRCD[2]=10thenbeginRCD[3]:=RCD[3]+1;RCD[2]:=0;end;
ifRCD[3]=10thenbeginRCD[4]:=RCD[4]+1;RCD[3]:=0;end;
ifRCD[4]=6thenbeginRCD[5]:=RCD[5]+1;RCD[4]:=0;end;
ifRCD[5]=10thenbeginRCD[6]:=RCD[6]+1;RCD[5]:=0;end;
ifRCD[6]=6thenbeginRCD[7]:=RCD[7]+1;RCD[6]:=0;end;
ifRCD[7]=10thenbeginRCD[8]:=RCD[8]+1;RCD[7]:=0;end;
ifRCD[8]=10thenRCD[8]:=0;//我们的跑表最多可计99个小时;
Caption:=IntToStr(RCD[8])+IntToStr(RCD[7])+':'+IntToStr(RCD[6])+IntToStr(RCD[5])+':'+IntT
oStr(RCD[4]);
Caption:=Caption+IntToStr(RCD[3])+':'+IntToStr(RCD[2])+IntToStr(RCD[1]);:.
end;
(sender:TObject);//可以在一天之类的时间倒计时
begin
ifStrToTime(Caption)<>StrToTime('00:00:00')then
Caption:=TimeToStr(StrToTime(Caption)-)
else
begin
Active:=false;
Beep;
ifAssigned(fOnTimeUp)then
fOnTimeUp(self);
end;
end;
;
var
i:integer;
begin
ifState=StRunClockthen
begin
Active:=false;
fori:=1to8doRCD[i]:=0;
Caption:='00:00:00:00';
end;
end;
;
begin
ifState=StBackClockthen:.
begin
Active:=false;
Caption:=BeginTime;
end;
end;
end.
为了测试我们的组件,现在你就可以安装这个组件包并建立一个应用测试它了,点击
组件包窗体中的install即可(注意:一但你安装了组件包,当你想对组件修改时,在修改了
原代码以后只用点击组件窗体的compile就可以了更新组件了),这时delphi的组件页的最
后多出了我们定义的页,其中有了我们的组件!
然而这个组件到目前为止仍然不够完善,还不能正式发布给用户,在下一篇中我们将
解决两个重要的问题:1、给我们的组件添加一个默认的图标。2、将这个组件杂乱的属性归
类。
三、添加组件图标、注册组件的属性类别:
在前面的文章中我们已经完成了组件的基本功能的开发。但是遗憾的是一但你安装了
组件包,你会发现组件显示在delphi组件页中的图标并不能清楚的说明我们组件的功能(由
于我们的组件继承自TcustomLabel,图标是一个默认的delphiVCL的图标,如果组件继承自
其它已经出现在组件面板中的组件,图标还会和已有组件一样!)。显然一个好的组件特别是
一个要发布的商业化组件需要一个有自己特色的目标,下面我们便来完成这一工作:
打开delphi自带的ImageEditor(ToolsImageEditor),新建一个组件资源
(filenewComponentResourceFile(.dcr)),在弹出的窗口中右键单击new新建一个
bitmap位图资源调整好位图的大小(我们用24*24)和色深后确定,双击建立好的位图名字
还是做图(做图工具的使用基本和windows自带的画图程序差不多,这里略过),完成后我
们需要为位图文件另取一个名字(右键点击bitmap),因为delphi强制要求这个位图的名字
要和组件的名字一样,并且要全部大写,这里我们就取为:TCLOCK。最后保存这个资源文
件到我们的组件包(dpk文件)目录,。最后在Clock的代码中的interface
部分加入一个编译器开关:{$}然后重新编译更新组件(还记得怎么更新
吗?),这时的组件图标已经变成我们刚才做的位图了!:.
接下来我们将为我们开发的组件的属性进行分类并介绍一个组件开发中重要的特性:
属性类别。
为了让我们组件的一些和时钟有关的属性注册成一个新的类别把它们和label的属性
分开开来,让组件用户能够更容易的发现组件的新特性,我们继承了属性类别的基类
TpropertyCategory(在delphi5中这需要引用单元DsgnIntf,不过应该特别注意在delphi7中
已经没有了这个基类,也没有这个单元文件,注册新的属性类别可以通过直接使用
RegisterPropertyInCategory这种简单的办法完成,在下面的代码中会在相应的地方同时给出
两种方法并说明他们的不同。)并覆盖它的两个类方法,最后在Register过程中用
RegisterPropertyInCategory(在delphi5中在DsgnIntf单元,在delphi7中在DesignIntf单元,
注意:delphi的一些单元并没有被安装,包括我们这里指出的这两个单元和将要在后文中指
出的单元,这些单元属于delphi的opentoolsapi是用来方便我们,特别是组件开发者用来
扩展delphi。如果你的delphi没有这些单元,请将delphi安装目录下的source文件夹里
ToolsAPI文件夹中的pas文件拷贝到lib目录下,在你第一个需要用到这些单元的程序编译
时delphi会自动编译这些单元)方法注册属性类别。我们把以下的部分代码补充进我们开发
的组件的原代码中:
uses
DesignIntf;//delphi7//delphi5用DsgnIntf
///////////这部分代码如果是delphi7就不需要了///////////////
type
TClockGategory=class(TpropertyCategory)//建立一个新的属性类别
ClassfunctionName:string;override;//属性类别的名称
ClassfunctionDescription:string;override;//属性类别的描述
End;
……
:string;
Begin
Result:=’ClockPro’;
End;:.
:string;
Begin
Result:=’OurComponentClockDescription’;
End;
////////////////////////////////////////////////////////////////////////////////////
接下来我们要做的就是修改register过程:
procedureRegister;
begin
RegisterComponents('ClockAndTime',[TClock]);
////////////这是delphi7的代码/////////////////////////////
RegisterPropertyInCategory('ClockPro',TClock,'State');
RegisterPropertyInCategory('ClockPro',TClock,'Active');
RegisterPropertyInCategory('ClockPro',TClock,'BeginTime');
RegisterPropertyInCategory('ClockPro',TClock,'WakeTime');
RegisterPropertyInCategory('ClockPro',TClock,'AllowWake');
RegisterPropertyInCategory('ClockPro',TClock,'OnWakeUp');
RegisterPropertyInCategory('ClockPro',TClock,'OnTimeUp');
//////////////////////////////////////////////////////////
///////////////这是delphi5的代码/////////////////////////
{
RegisterPropertyInCategory(TClockGategory,TClock,'State');
RegisterPropertyInCategory(TClockGategory,TClock,'Active');
RegisterPropertyInCategory(TClockGategory,TClock,'BeginTime');
RegisterPropertyInCategory(TClockGategory,TClock,'WakeTime');
RegisterPropertyInCategory(TClockGategory,TClock,'AllowWake');
RegisterPropertyInCategory(TClockGategory,TClock,'OnWakeUp');
RegisterPropertyInCategory(TClockGategory,TClock,'OnTimeUp');
}:.
////////////////////////////////////////////////////////
end;
重新编译后,做一个测试程序,这时只要组件使用者右键单击ObjectInspector选择
ArrangeByCategory就可以看到属性已经被清楚的分类了,如下图:
然而,应该清楚的是属性类别绝对不能被滥用,因为过多的使用该技术会使组件使用
者为了找到某一个属性变的更加麻烦和摸不着头脑。
在接下来的文章里,我们将继续研究两个很有用的组件特性
四、组件属性编辑器和组件编辑器:
通过上面的努力我们的组件似乎已经比较完美了,可我们也忽略了一些重要的细节和一些有趣的事情,这
一篇我们将研究两个很有用的组件特性:
在之前开发组件核心功能时我们曾设置了两个属性BeginTime和WakeTime,他们都
是字符串型的属性,然而他们所要表示的却是时间类型,这样就很有可能使组件使用者错误
的编辑属性并导致转化字符串到时间时出错(当然这里只是为了文章的讲解,我们故意把它
设置为了字符串类型),虽然通过浏览原代码你知道我们也做了一些代码级别的防出错处理,
使当输入错误时属性自动变成‘00:00:00’,然而这对组件使用者来讲仍然显的很不友好,
所以我们需要为这两个属性定制编辑器,我们的编辑器将弹出一个窗口里面有一个
TdateTimePicker用来选择时间。在delphi中有许多这样的例子,例如大家都知道的lines属
性,当你单击它右放的省略号时为自动弹出一个文本编辑器来编辑lines,这大大降低了组
件使用者范错误的可能性。
在定制完属性编辑器

VCL组件开发实例 来自淘豆网www.taodocs.com转载请标明出处.

相关文档 更多>>
非法内容举报中心
文档信息
  • 页数19
  • 收藏数0 收藏
  • 顶次数0
  • 上传人小s
  • 文件大小691 KB
  • 时间2022-12-06