知识大全 通过实例看VCL组件开发全过程
Posted 属性
篇首语:一年好景君须记,最是橙黄橘绿时。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 通过实例看VCL组件开发全过程相关的知识,希望对你有一定的参考价值。
通过实例看VCL组件开发全过程 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
在这篇文章中我们将建立一个和时间有关的组件 这个组件通过设置它的不同状态有以下基本功能 显示系统的当前时间(包括设置闹钟) 跑表 倒计时 这是一个简单的例子 然而我们将在这个例子中尽可能多的用到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 Only Runtime Only Designtime 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 StdCtrls ExtCtrls; type TState=(StClock StRunClock StBackClock);//定义枚举类表示控件的 种状态 时钟 跑表 倒计时钟 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 TClock Create(AOwner: TComponent); begin inherited Create(AOwner); //设置默认值 fTimer:=TTimer Create(self); //将它属于我们的组件 这样便不用编写析构函数 而可以自动在释放本组件时释放Timer Active:=false; AllowWake:=false; State:=StClock; BeginTime:= : : ; WakeTime:= : : ; end; function TClock GetActive: boolean; begin result:=fTimer Enabled; end; procedure TClock SetActive(Value: boolean); begin fTimer Enabled:=Value; end; procedure TClock SetState(Value: TState); var i:integer; begin case Value of StClock: begin Active:=false; fTimer Interval:= ; fTimer OnTimer:=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[ ]); fTimer Interval:= ; //经过测试 这个秒表的效果很好 然而这只是一个技术上的演示 //实际上这么频繁( / 秒)的不断执行RunClock会使CPU的占用一直达到 % //这并不是一个好注意 事实上要想在跑表中显示微秒级别并做到合理的占用CPU //这需要更加灵活和复杂的编程 fTimer OnTimer:=RunClock; end; StBackClock: begin Active:=false; Caption:=BeginTime; fTimer Interval:= ; fTimer OnTimer:=BackClock; end; end; fState:=Value; end; procedure TClock SetBeginTime(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 TClock SetWakeTime(Value: string); begin try StrToTime(Value); fWakeTime:=Value; except on Exception do begin fWakeTime:= : : ; end; end; end; procedure TClock WalkClock(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 TClock RunClock(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 TClock BackClock(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 TClock ReSetBackClock; var i:integer;
三 添加组件图标 注册组件的属性类别 在前面的文章中我们已经完成了组件的基本功能的开发 但是遗憾的是一但你安装了组件包 你会发现组件显示在delphi组件页中的图标并不能清楚的说明我们组件的功能(由于我们的组件继承自TcustomLabel 图标是一个默认的delphiVCL的图标 如果组件继承自其它已经出现在组件面板中的组件 图标还会和已有组件一样!) 显然一个好的组件特别是一个要发布的商业化组件需要一个有自己特色的目标 下面我们便来完成这一工作 打开delphi自带的Image Editor(ToolsàImage Editor) 新建一个组件资源(fileànewàComponent Resource File ( dcr)) 在弹出的窗口中右键单击new新建一个bitmap位图资源调整好位图的大小(我们用 * )和色深后确定 双击建立好的位图名字还是做图(做图工具的使用基本和windows自带的画图程序差不多 这里略过) 完成后我们需要为位图文件另取一个名字(右键点击bitmap) 因为delphi强制要求这个位图的名字要和组件的名字一样 并且要全部大写 这里我们就取为 TCLOCK 最后保存这个资源文件到我们的组件包(dpk文件)目录 命名为ClockDcr dcr 最后在Clock的代码中的interface部分加入一个编译器开关 $R ClockDcr dcr然后重新编译更新组件(还记得怎么更新吗?) 这时的组件图标已经变成我们刚才做的位图了! 接下来我们将为我们开发的组件的属性进行分类并介绍一个组件开发中重要的特性 属性类别 为了让我们组件的一些和时钟有关的属性注册成一个新的类别把它们和label的属性分开开来 让组件用户能够更容易的发现组件的新特性 我们继承了属性类别的基类TpropertyCategory(在delphi 中这需要引用单元DsgnIntf 不过应该特别注意在delphi 中已经没有了这个基类 也没有这个单元文件 注册新的属性类别可以通过直接使用RegisterPropertyInCategory这种简单的办法完成 在下面的代码中会在相应的地方同时给出两种方法并说明他们的不同 )并覆蓋它的两个类方法 最后在Register过程中用RegisterPropertyInCategory(在delphi 中在DsgnIntf单元 在delphi 中在DesignIntf单元 注意 delphi的一些单元并没有被安装 包括我们这里指出的这两个单元和将要在后文中指出的单元 这些单元属于delphi的open tools api是用来方便我们 特别是组件开发者用来扩展delphi 如果你的delphi没有这些单元 请将delphi安装目录下的source文件夹里ToolsAPI文件夹中的pas文件拷贝到lib目录下 在你第一个需要用到这些单元的程序编译时delphi会自动编译这些单元)方法注册属性类别 我们把以下的部分代码补充进我们开发的组件的原代码中 uses DesignIntf;//delphi //delphi 用DsgnIntf ///////////这部分代码如果是delphi 就不需要了/////////////// type TClockGategory=class(TpropertyCategory)//建立一个新的属性类别 Class function Name:string;override;//属性类别的名称 Class function Description:string;override;//属性类别的描述 End; …… Class function TClockGategory Name:string; Begin Result:= ClockPro ; End; Class function TClockGategory Description:string; Begin Result:= Our Component Clock Description ; End; //////////////////////////////////////////////////////////////////////////////////// 接下来我们要做的就是修改register过程 procedure Register; begin RegisterComponents( ClockAndTime [TClock]); ////////////这是delphi 的代码///////////////////////////// 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 ); ////////////////////////////////////////////////////////// ///////////////这是delphi 的代码///////////////////////// 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; 重新编译后 做一个测试程序 这时只要组件使用者右键单击Object Inspector选择ArrangeàBy Category就可以看到属性已经被清楚的分类了 然而 应该清楚的是属性类别绝对不能被滥用 因为过多的使用该技术会使组件使用者为了找到某一个属性变的更加麻烦和摸不著头脑 在接下来的文章里 我们将继续研究两个很有用的组件特性
四 组件属性编辑器和组件编辑器 通过上面的努力我们的组件似乎已经比较完美了 可我们也忽略了一些重要的细节和一些有趣的事情 这一篇我们将研究两个很有用的组件特性 在之前开发组件核心功能时我们曾设置了两个属性BeginTime和WakeTime 他们都是字符串型的属性 然而他们所要表示的却是时间类型 这样就很有可能使组件使用者错误的编辑属性并导致转化字符串到时间时出错(当然这里只是为了文章的讲解 我们故意把它设置为了字符串类型) 虽然通过浏览原代码你知道我们也做了一些代码级别的防出错处理 使当输入错误时属性自动变成 : : 然而这对组件使用者来讲仍然显的很不友好 所以我们需要为这两个属性定制编辑器 我们的编辑器将弹出一个窗口里面有一个TdateTimePicker用来选择时间 在delphi中有许多这样的例子 例如大家都知道的lines属性 当你单击它右放的省略号时为自动弹出一个文本编辑器来编辑lines 这大大降低了组件使用者范错误的可能性 在定制完属性编辑器以后 我们将为组件本身加入一写有趣的元素——组件编辑器 这也是在delphi中经常出现的 例如有些组件当你双击它时 它并不会进入代码编写状态 而是弹出它自己的编辑器 虽然我们的组件似乎并不需要这种特性 但为了演示它 我们也将它考虑近来 我们给我们的组件编写了一个版权信息和一个关于对话框 当组件使用者双击它时弹出关于信息(当然 这仅仅是种演示) 上面提到的两种特性由于它们只是会在设计时起作用 所以你完全可以在新的组件包中编写并注册它们 并将这个组件包设置为Designtime Only 为了方便起见我们就直接把它们和组件的单元编写在一起 注意 以下出现的一些类和方法都需要引用单元DesignEditors(delphi )或DsgnIntf(delphi ) 与前面说的一样 它们都属于delphi的open tools api所以 如果你没有这写单元请按照前文的方法安装它们 首先来编写属性编辑器 由于BeginTime和WakeTime是字符串类型 所以我们必须从默认的字符串属性编辑器类TstringProperty继承并覆蓋它的一写方法(这里只介绍几个重要的方法 事实上所有的属性编辑器都从TpropertyEditor继承而来 然而我们不用直接继承这个基类) 其中一个重要的方法是GetAttributes 他将返回一些代表编辑器功能的值 这些值将会在代码的注释中说明(如果你的属性编辑器还需要一个下拉列表 你还需要另外一个重要的方法GetValues具体请查看delphi帮助)另外为了使属性编辑器为弹出的对话框我们需要覆蓋Edit方法 为了可以以可视化的方式设计对话框 我们可以建立一个普通工程 在设计好后将窗体的类声明复制到我们的组件单元 并将窗体的dfm文件拷贝到我们的组件包目录 并在代码中加入编译器开关$R * dfm 以下是窗体的类声明 这个窗体没有任何的代码需要编写 TTimeEditFrm = class(TForm) DateTimePicker : TDateTimePicker; Button : TButton; Button : TButton; private Private declarations public Public declarations end; 以下是属性编辑器的代码 TClockProperty=class(TStringProperty) public function GetAttributes:TPropertyAttributes;override; procedure Edit;override; end; 实现部分 procedure TClockProperty Edit; var TimeEditFrm:TTimeEditFrm; begin TimeEditFrm:=TTimeEditFrm Create(Application); try TimeEditFrm DateTimePicker Time:=StrToTime(GetValue); if TimeEditFrm ShowModal=mrOK then SetValue(TimeToStr(TimeEditFrm DateTimePicker Time)); //GetValue和SetValue是TStringProperty的基类方法 他直接读取和设置字符串的值 finally TimeEditFrm Free; end; end; function TClockProperty GetAttributes: TPropertyAttributes; begin result:=[paDialog paMultiselect]; //paDialog表示属性编辑器将显示一个对话框 paMulitiselect允许多个组件选择属性 //除此之外如果你想让属性编辑器显示下拉列表 你还需要paValueList具体请查看帮助 end; 最后我们用RegisterPropertyEditor方法注册属性编辑器: procedure Register; begin …… RegisterPropertyEditor(TypeInfo(string) TClock BeginTime TClockProperty); RegisterPropertyEditor(TypeInfo(string) TClock WakeTime TClockProperty); end; 重新编译更新组件后我们就可以测试了 接下来我们来实现组件编辑器 组件编辑器需要继承TponentEditor并覆蓋一些重要的方法 GetVerbCount返回设计时组件右键自定义菜单的数目 GetVerb为每一个自定义菜单添加文字 ExecuteVerb为每一个菜单项添加事件 Edit为组件的缺省操作指定事件(即在设计时双击组件) 以下是代码 TClockEditor=class(TComponentEditor) public function GetVerbCount:integer;override; function GetVerb(index:integer):string;override; procedure ExecuteVerb(index:integer);override; procedure Edit;override; end; 实现部分 procedure TClockEditor Edit; begin ExecuteVerb( ); //默认显示关于 end; procedure TClockEditor ExecuteVerb(index: integer); begin case index of //第一个显示名字的菜单什么都不做显示 :showmessage( barton@ ); end; end; function TClockEditor GetVerb(index: integer): string; begin case index of :result:= barton ; :result:= About Clock ; end; end; function TClockEditor GetVerbCount: integer; begin result:= ;//我们显示两条菜单 一个我的名字 一个关于 end; 同样最后我们注册组件编辑器 procedure Register; begin …… RegisterComponentEditor(TClock TClockEditor); end; cha138/Article/program/Delphi/201311/8400相关参考
通过实例看VCL组件开发全过程(二) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &n
通过实例看VCL组件开发全过程(一) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &n
怎样在Java实例开发的过程中使用进度条 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在读取大
基于Delphi的组件设计之简单实例 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!这是一个可以计数
Extjs单独定义各组件的实例代码 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 网上看到的一个
在你的struts配置文件strutsconfigxml中加入下面的配置 <plugin
新建类库MyTestDLL 右击项目“MyTestDLL”》属性》生成》勾选“为互操作注册” 打开 AssemblyInfocs&nb
在laszlo应用开发过程中大部分编码过程集中在对系统组件的熟练应用比如取值传值刷新组件状态等等但是很多情况下尤其是项目的初始编码阶段需要定义一些满足项目特定要求的界面组件这就需要自定义组件了&n
VB.NET中的组件开发 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!简介组件其实是一段可以重用的
dotnet下开发COM+组件 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 一问题的提出 最