Delphi自定义控件开发

合集下载

Delphi自定义控件设计

Delphi自定义控件设计

Delphi 自定义控件设计邓清闯,张丹,姬希娜,李国杰(河南许昌许继电气有限公司中试部,许昌461000)摘要:通过一个实例程序,系统、详尽地讲解了如何设计Delphi 的自定义控件。

该控件代码技术难度不高,旨在说明Delphi 控件的设计方法,包括自定义控件创建向导、代码设计、图标设计及控件发布。

关键词:控件;自定义控件;Delphi 控件Delphi 语言受众多程序员追捧,主要原因之一就是它有很多第三方的控件可供使用。

很多资深的Delphi 程序员都把自己积累的函数、过程等设计成控件,以方便使用,提高开发效率。

本文通过一个只允许输入数字、并且可以设置输入值范围和小数点位数的编辑框控件的设计,详细介绍了控件的实现方法。

该控件继承自edit 控件,控件单元名称为NumEdit ,控件类名称为TNumEdit 。

控件的实现主要分为4个阶段:(1)建立控件原型。

(2)设计控件功能代码。

(3)设计控件图标。

(4)安装发布控件。

下面对这四个阶段进行详细讲解。

1建立控件原型可以通过Delphi 向导建立控件原型。

通过Delphi 菜单“File-New-Other ”打开“New Items ”对话框,然后在New 属性页中选择“Component ”,点击“OK ”,弹出“New Compo -nent ”对话框,在该对话框中设置控件的基本信息,如图1。

Ancestor type :选择控件要继承的类,选择“TEdit (StdC -trls )”。

Class Name :要创建的控件的类名称,我们设置为“TNumEdit ”。

Palette Page :该自定义控件发布后将要停靠的控件面板。

Unit file name :该控件代码存放路径。

设置好上面信息后,点“OK ”按钮,向导自动生成最原始的控件代码,如下:unit NumEdit;//单元名称Interfaceuses //该控件需要调用的单元SysUtils,Classes,Controls,StdCtrls;typeTNumEdit =class (TEdit)//控件类,继承自TEdit private //私有成员{Private declarations }protected //保护成员{Protected declarations }public //公有成员{Public declarations }published //发布成员{Published declarations }end;procedure Register;//过程声明implementation//下面函数功能是将控件TNumEdit 的图标显示在Samples //面板上procedure Register;beginRegister Components ('Samples',[TNumEdit]);end;end.到目前为止,控件TNumEdit 已经具备了Tedit 编辑框的所有功能,下面就可以在这个基础上设计需求的功能了。

Delphi开发简单ActiveX控件

Delphi开发简单ActiveX控件

1.打开Delphi2.File->New->Other->ActiveX->Active Form3.弹出Active Form Wizard窗口,采用默认配置,单击“OK”4.出现Form设计界面,这里添加一个Button和一个Edit控件5.双击Button,添加事件处理代码procedure TActiveForm X.Button1Click(Sender: TObject);beginEdit1.Text :='Hello World!';end;6.保存并编译程序7.发布程序,Project->Web Deployment Options,弹出Web Deployment Options窗口,填写说明如下:Target dir:C:\Inetpub\wwwroot(IIS目录,OCX发布的目录)Target URL:http://localhost/(OCX发布的URL)HTML dir:C:\Inetpub\wwwroot(IIS目录,HTML发布的目录)8.调试Active Form,Run->Param eters,弹出Run Parameters窗口,填写说明如下:Host Application:点击“Browser”找到IEParameters:填入自动生成的HTML文件地址,这里为“C:\Inetpub\wwwroot\ActiveForm Proj1.ht m”9.注册ActiveX,Run->Register ActiveX Server10.运行程序注意:如果看不到控件,可能是IE限制了下载未签名的ActiveX控件,你需要修改IE的安全设置,我用的是IE6;另外FireFox需要安装插件才支持ActiveX。

Delphi VCL 高级组件编程 之 设置自定义子组件的属性

Delphi VCL 高级组件编程 之 设置自定义子组件的属性

Delphi VCL 高级组件编程之设置自定义子组件的属性作者:金海龙(天主教)Google搜索:金海龙天主教,delphi VCL components,writecomponent,programming,setsubcomponent(true),TTestPanel今天打开软件开发经验记录本,发现一个以前没有解决的组件编程问题。

现在,我是开发Delphi 第三方组件的高手了,对于刚入门时的问题,解决一下,不留遗憾。

当时对这个问题的描述如下:“TestPanel组件有一个P子控件,通过下面的代码,只能够在设计时,改变P的属性,但是运行之后,你就会发现,P没有任何变化,即:在设计时所作的修改,全部无效。

”当时的努力写了下面的代码:TestPanel.pasunit TestPanel;interfaceusesWindows, Messages, graphics,SysUtils, Classes, Controls, ExtCtrls, ComCtrls;typeTTestPanel = class(TPanel)p:TPanel;private{ Private declarations }Function Getp:TPanel;Procedure Setp(Value:TPanel);protected{ Protected declarations }procedure CreateWindowHandle(const Params: TCreateParams); override; public{ Public declarations }constructor Create(AOwner: TComponent); override;destructor Destroy; override;published{ Published declarations }Property panel : TPanel Read Getp Write Setp;end;procedure Register;implementationFunction TTestPanel.Getp:TPanel;beginresult:=p;end;Procedure TTestPanel.Setp(Value:TPanel);beginif p<>Value thenp:=Value;end;procedure TTestPanel.CreateWindowHandle(const Params: TCreateParams); begininherited CreateWindowHandle(Params);with p dobeginwidth:=50; height:=50;left:=10; top:=10;color:=clred;end;end;constructor TTestPanel.Create(AOwner: TComponent);begininherited Create(AOwner);p:=tpanel.Create(self);p.parent:=self;width:=100; height:=100;end;destructor TTestPanel.Destroy;beginp.Free;inherited destroy;end;procedure Register;beginRegisterComponents('Jin Hailong', [TTestPanel]);end;end.看了上面的描述,明白了我当初的意图:设计一个包含子组件的Panel, 这个组件叫“TTestPanel”,它的子组件叫:P,它们都继承于TPanel,在设计时,使用属性编辑器可以改变子组件p的属性,但是按下F9调试程序,却发现所做的设置无效。

Delphi一个简单的DELPHI自定义事件的例子

Delphi一个简单的DELPHI自定义事件的例子

Delphi⼀个简单的DELPHI⾃定义事件的例⼦unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;type//定义⼀个事件格式,要传参数的格式后⾯必须要加上of objectTeacherArgnyEvent = procedure(ErrorCount: Integer) of object;typeTTeacher = class(TObject)privateFStudentErrorCount: Integer;FOnTeacherArngy: TeacherArgnyEvent; //定义⼀个内部事件,private⾥的只能在TTeacher类内部调⽤procedure SetStudentErrorCount(Value: Integer);publicconstructor Create;destructor Destroy; override;property StudentErrorCount: Integer read FStudentErrorCount write SetStudentErrorCount ;property OnTeacherArngy: TeacherArgnyEvent read FOnTeacherArngy write FOnTeacherArngy ; //定义⼀个外部的事件,在其它的对象⾥⾯可以通过FTeacher.ONTeacherArngy这样调⽤。

读写是通过内部的FOnTeacherArngyend;typeTForm1 = class(TForm)Button1: TButton;procedure FormCreate(Sender: TObject);procedure FormClose(Sender: TObject; var Action: TCloseAction);procedure Button1Click(Sender: TObject);procedure IfTeacherArngy(EC: Integer); //⾃⼰定义事件触发时要执⾏的函数,和Button控件在事件列表中双击时出来的那段代码⼀样,只是这⾥是你⾃⼰定义的,⽽不是DELPHI给你⽣成的privateFTeacher: TTeacher ;public{ Public declarations }end;varForm1: TForm1;implementation{$R *.dfm}{ TTeacher }constructor TTeacher.Create;beginFStudentErrorCount := 0;end;destructor TTeacher.Destroy;begininherited;end;procedure TTeacher.SetStudentErrorCount(Value: Integer);beginFStudentErrorCount := Value;if FStudentErrorCount > 3 then //给StudentErrorCount赋值的时候,判断是否触发FOnTeacherArngy事件if Assigned(FOnTeacherArngy) then //看FOnTeacherArngy是否为空,不为空则执⾏该事件,如果不判断是否为空,则执⾏下⾯⼀句的时候可能会有错误FOnTeacherArngy(FStudentErrorCount);end;{ TForm }procedure TForm1.FormCreate(Sender: TObject);beginFTeacher := TTeacher.Create ; //创建⼀个TTeacher的实例FTeacher.OnTeacherArngy := IfTeacherArngy; //把FOrm1⾥的IfTeacherArngy过程赋值给FTeacher的OnTeacherArngy,这样它就不为空了end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);beginFTeacher.Free ; //窗体关闭时释放end;procedure TForm1.Button1Click(Sender: TObject);beginFTeacher.StudentErrorCount := FTeacher.StudentErrorCount + 1; //给StudentErrorCount赋值,我只是给他赋值,就会触发那个事件// 有了这个类,就不⽤下⾯这⼀段了,给 StudentErrorCount属性赋值就能达到下⾯这段代码的效果。

delphi常用技巧

delphi常用技巧

delphi常用技巧
Delphi是一种强大的编程语言,有许多有用的技巧和功能可以帮助提高开发效率。

以下是一些Delphi常用的技巧:
1. 使用快捷键:Delphi支持许多快捷键,使用这些快捷键可以快速完成常见的任务,如Ctrl+C用于复制,Ctrl+V用于粘贴等。

2. 快速查找和替换:使用Ctrl+Shift+F可以打开查找和替换对话框,输入要查找的文本,然后输入要替换的文本,最后点击“全部替换”即可。

3. 调试技巧:在调试时,可以使用断点、观察表达式、单步执行等技巧来检查程序的运行情况。

4. 自定义控件:Delphi支持自定义控件,可以创建自己的控件来满足特定的需求。

5. 使用第三方控件:Delphi有许多第三方控件可用,这些控件可以提供更多的功能和更好的用户体验。

6. 优化代码:使用一些技巧可以优化代码的运行速度和可读性,例如使用局部变量、避免不必要的对象创建等。

7. 使用异常处理:在Delphi中,可以使用异常处理来处理运行时错误和异常情况。

8. 使用多线程:Delphi支持多线程编程,可以使用多线程来提高程序的性能和响应速度。

9. 使用数据库:Delphi支持多种数据库连接方式,可以使用数据库来存储和检索数据。

10. 使用第三方库:Delphi有许多第三方库可用,这些库可以提供更多的功能和更好的性能。

希望这些技巧可以帮助你更好地使用Delphi进行开发。

delphi编写自定义组件

delphi编写自定义组件

第2 1章编写自定义组件本章内容:• 组件设计基础• 组件的示例• TddgButtonEdit—一个容器组件• 组件包• 附加包在D e l p h i5中,可以轻松地编写自定义组件,这一点优于其他的编程环境。

正因为可以将自定义组件运用到程序设计中,所以你可以随心所欲地设计应用程序的用户界面。

如果你的工作主要是编写组件,那肯定对本章所提供的内容感到非常满意。

你将学到设计组件的基本概念,以及将组件集成到D e l p h i开发环境中的方法。

并且,你将了解到设计组件的一些缺陷,以及关于开发高级功能和可扩展组件的一些提示与技巧。

即使你主要从事的是应用程序开发,而不是编写组件,也能从本章学到很多东西。

将自定义组件应用到程序中是很有意思的。

在编写应用程序时,现有的组件往往不能满足特殊要求。

这时,就需要自己设计一个合适的组件,使之满足程序设计时的特殊需要,并且设计的组件应当灵活巧妙,以便在其他应用程序中也能使用。

21.1 组件设计基础下面介绍编写组件所要求的基本技巧,并且通过范例演示如何应用技巧来设计组件。

21.1.1 确定是否需要编写组件其实,有些情况下没有必要编写自定义组件,只是在下列情况下才考虑编写自定义组件:• 希望设计一个可以被其他应用程序共享的组件。

• 希望将应用程序变成逻辑上独立的对象。

• 现有的D e l p h i组件和A c t i v e X组件不能满足某种特殊的需要。

• 你了解到一个特殊组件的市场,想设计一个组件作为商品出售。

• 你想更深入地了解D e l p h i、V C L和Win32 API。

学习创建自定义组件的最好途径就是剖析V C L的源代码。

对组件编写者而言,V C L的源代码是无价资源,在它的注释部分对自定义组件进行了详细介绍。

V C L源代码包括在客户服务器版本和专业版本里。

编写自定义组件看上去有点难,其实并非如此。

编写自定义组件是难是易取决于自己,当然按照这里所介绍的步骤和方法,你自己也可以非常轻松地设计出很有用的组件。

DELPHI基础教程:Delphi自定义部件开发(四)[2]

DELPHI基础教程:Delphi自定义部件开发(四)[2]

DELPHI基础教程:Delphi自定义部件开发(四)[2]⑵ 声明属性当声明一个属性时通常需要声明私有域来保存属性值然后描述读写属性值的方法对于Shape控制将声明一个域保存当前形状然后声明一个属性通过方法调用来读写域值typeTSampleShape=class(TGrahpicControl)privateFShape: TSampleShapeType;procedure SetShape(value: TSampleShapeType)publishedproperty Shape: TSampleShapeType read FShape write SetShape;end;现在只剩下SetShape的实现部分了⑶ 编写实现方法下面是SetShape的实现procedure TSampleShape SetShape(value: TSampleShapeType)beginif FShape<>value thenbeginFShape := value;Invalidate(True) { 强制新形状的重画 }end;end;覆蓋constructor和destructor为了改变缺省属性值和初始化部件拥有的对象需要覆蓋继承的constructor和destructor方法图形控制的缺省大小是相同的因此需要改变Width和Height属性本例中Shape控制的大小的初始设置为边长个象素点⑴ 在部件声明中增加覆蓋constructortypeTSampleShape=class(TGraphicControl)publicconstructor Create(Aowner: TComponent) override;end;⑵ 用新的缺省值重新声明属性Height和widthtypeTSampleShape=class(TGrahicControl)publishedproperty Height default ;property Width default ;end;⑶ 在库单元的实现部分编写新的constructorconstructor TSampleShape Create(Aowner: TComponent)begininherited Create(AOwner)width := ;Height := ;end;公布Pen和Brush在缺省情况下一个Canvas具有一个细的黑笔和实心的白刷为了使用户在使用Shape控制时能改变Canvas的这些性质必须能在设计时提供这些对象然后在画时使用这些对象这样附属的Pen或Brush被称为Owned对象管理Owned对象需要下列三步● 声明对象域● 声明访问属性● 初始化Owned对象⑴ 声明Owned对象域拥有的每一个对象必须有对象域的声明该域在部件存在时总指向Owned对象通常部件在constructor中创建它在destructor中撤消它Owned对象的域总是定义为私有的如果要使用户或其它部件访问该域通常要提供访问属性lishixinzhi/Article/program/Delphi/201311/25110。

Delphi 培训(二)控件的开发

Delphi 培训(二)控件的开发

组件的结构
• • 可见性:Private、Protected、 Published(属性窗口可见)、Public 属性(Property),属性类型包括 – 简单类型(字符串、浮点数、整数、 字符等)、枚举、集合 – 对象,必须有自己的属性编辑器 – 数组,必须有自己的属性编辑器 方法(Method) 事件(Event):OnXXXX 流属性:持续化 拥有关系(Owner):Owner释放时,被 Owner者会被同时Free 父子关系(Parent)
创建控件
• 利用Delphi生成框架代码
– Component New VCL Component – 提高生产力,提高正确性,规范化
• 增加方法或编写需要重载的方法
– Override – Wndproc – Message
VCL框架说明
• TObject——万类之源 • TPersistent完成控件的持续化处理,即能够重新加载和 保存控件、控件之间相互赋值的能力 • TComponent,VCL所有组件的父类,只要从此继承,就 可以被“摆放”到窗口上,可以通过它封装一些数据操 作的对象给用户“可视化”编程使用 • TControl,可视控件的基类,这类控件都是可以在窗口 上被用户看到 • TGraphControl,图形控件,无用户输入,不占用 Windows句柄资源,如Label控件、SpeedButton等控件 • TWinControl,可视化句柄控件,有焦点,用户可输入, 占用Windows句柄资源,封装的Windows标准控件都继 承自该类,如Button、Edit、ListView等
控件 2010-11
Delphi 培训(二)
通过本课程,你将掌握——
• Windows是如何通过消息机制来运作的, Delphi是如何完成消息分发,程序运作机制是 怎样的,知道如何利用消息编写控件 • 了解Delphi VCL的框架,知道该用什么控件来 完成什么样的功能,如果自己要编写控件,该 从哪里去继承 • 能够通过Delphi编写一个简单的控件,能够使 用属性编辑器,知道怎么样去封装控件,以便 达到重用目的 • 了解一些简单的OTA机制,能够简单地控制 Delphi开发环境
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

有人说过“不会开发控件的Delphi程序员不是真正的程序员”.Delphi正是由于高度的可扩展性和大量的第三方控件的支持才能吸引无数程序员挑剔的目光.即使是由于工作需要使用其他开发工具的开发者也常常怀念和Delphi度过的日日夜夜.接触Delphi已经一年多了,从当初对着Delphi组件面板上上百个控件不知所措,到现在已经可以根据需要开发一些有一定难度的控件,其中走过的路是十分艰辛的,所以特此写下这篇文章,将自己的经验留给后来者,也算是献给“同门师弟”的一份厚礼吧需要说明的一点是:在写这篇文章之前我假设读者已经对面向对象的基本知识有一定的了解,所以对于文章中面向对象相关的概念将不再展开讲述.一、牛刀小试-TURLLabel控件我们从一个能够添加超链接的标签控件开始我们的控件开发之旅吧既然是Label我们就从Tlabel派生这个控件吧其实从TcustomLabel派生最好,不过出于简单的目的我们这里先从Tlabel派生1、选择“File”->“New”->“Component”,将弹出如下的对话框:在Ancestor type中选择Tlabel,ClassName中填入TURLLabel名字可以任意取,但是要以T开头,否则的话会出现注册控件时候的问题.这里Ancestor type代表控件的基类,TURLLabel代表控件的名称.2、超链接的视觉效果是带下划线的文字,所以我们覆盖父类的构造函数,在构造函数里修改控件的字体属性. constructor AOwner:TComponent;begininherited CreateAOwner;Cursor:=crHandPoint;:= fsUnderline;end;代码解释:1 inherited CreateAOwner;这句的意思是执行父类的构造函数.我们制作控件的时候,如果覆盖了父类的构造函数,那么在新的构造函数中一定要首先调用父类的构造函数,否则会引起错误.这是很多初学控件开发的人常常遇到的问题.当您把自己开发的控件从面板上拖放到窗体时,如果跳出一个“Access Violent”的错误对话框的话,那么十有八九是因为您忘了调用父类的构造函数.2 Cursor:=crHandPoint;:= fsUnderline;这三句的意思是修改标签的视觉效果.Cursor:=crHandPoint;是设定当鼠标移动到控件上时鼠标的形状为“手型”;:= fsUnderline; 是设定文字的下划线效果.3、既然是超链接控件,那么我们肯定要能使用户在使用控件的时候能在“Object Inspector”中对超链接的URL进行修改,所以我们应该为控件增加一个Url属性.属性是访问控件字段的接口.通过属性,控件使用者可以间接读或者写控件的内部字段改变控件的状态.组件属性的声明需要以下几部分:属性名、属性类型、读方法或读字段、写方法或写字段.如果没有写方法或写字段,则该属性为只读属性.属性在控件类声明的Published部分声明.在Published 中声明的属性可以在设计期通过“Object Inspector”对属性值进行修改.如果声明在Public部分则不可以在设计期通过“Object Inspector”对属性值进行修改,但是可以在运行时通过代码进行读写.在类声明的Private访问区域中添加如下字段声明:FUrl: String;在类声明中添加Published访问区域,并添加如下代码property Url: String read FUrl write FUrl;这段声明的意思是为控件添加一个Url属性,属性的类型是string,在读Url属性时返回Furl的值,在写 Url属性时设定Furl的值.4、超链接的视觉效果有了,下面使它点击时调用浏览器打开Url指定的网址.在Delphi控件的事件处理中很多事件都有对应的一个调度方法这是设计模式中模板模式的典型应用.比如在鼠标点击控件时,控件会首先调用Click方法,由Click方法进行相应的处理,而绝大多数调度方法都会引发一个事件句柄事件句柄我们后边有深入的介绍.比如Tlabel控件中在用户用点击Label时会首先调用控件的Click方法被声明为Protected级别,Click方法再触发OnClick事件.所以我们只要覆盖Tlabel的Click方法进行我们自己的处理就可以了.在Protected部分添加如下的声明:procedure Click;override;在实现部分为Click方法写如下的代码:procedure ;beginShellExecute, nil, PCharUrl, nil, nil,SW_NORMAL; inherited;end;代码解释:1 ShellExecute的作用是用默认的程序打开第三个参数指定的文件.所以当第三个参数为一个URL时,则用浏览器打开这个网址.ShellExecute其他参数的使用方法可以查阅MSDN或其他相关资料.2 Inherited;的作用是调用父类的Click方法来由父类来对鼠标单击事件做其他的处理.5、源代码.下面给出这个控件的全部源代码:unit UrlLabel;interfaceusesWindows, Messages, SysUtils, Classes, Controls, StdCtrls,Shellapi,Graphics, Forms;typeTUrlLabel = classTLabelprivateFUrl:AnsiString;protectedprocedure Click;override;publicconstructor CreateAOwner:TComponent;override; publishedproperty Url:AnsiString read FUrl write FUrl; end;procedure Register;implementationconstructor AOwner:TComponent;begininherited CreateAOwner;Cursor:=crHandPoint;:=fsUnderline;end;procedure ;beginShellExecute, nil, PCharUrl, nil, nil,SW_NORMAL; inherited;end;procedure Register;beginRegisterComponents'Linco', TUrlLabel;6、为控件添加图标.如果没有给自定义的控件定义图标,出现在控件面板上的自定义图标的图标是默认的图标,很没有“个性”,也不容易被用户与其他控件区别开来,所以我们需要给组件指定一个图标.首先利用Delphi的Image Editor创建一个2424的位图,并把它保存到一个DCR文件中.创建了一个位图后,就需要给位图命名了.位图的名称必须和控件的类名相同,且为大写,而DCR文件的名字则必须与控件所在单元的单元名相同.如我们上边定义的控件,位图的名字应该为TURLLABEL,DCR文件的名字应该是,此DCR文件应该与组件的单元文件放在同一个目录下.打开Image Editor,选择”File”->“New”->“Component Resource File.dcr”,如下图:在“Contents”上单击鼠标右键,选择“New”->“BitMap”:在Width,Height中都填入24,点“OK”即可.可以在Bitmap1上点右键选择“Rename”为位图重命名为TURLLABEL,然后双击TURLLABEL,就可以像使用“画图”一样为您的控件设计图标了.7、注册组件.点击Componet->Install Componet进行自定义组件安装,此时将出现组件安装对话框.在Unit FilName 中输入控件单元文件的文件名包括路径,点击“OK”,在弹出的Package Editor 中按下Install按钮.如果安装成功系统就会提示安装成功.关闭Package Editor 时,会提示您是否保存修改,点击Yes即可.安装成功,建立一个测试程序.将URLLabel控件放到窗体上,设定Url属性为运行程序,点击此Label,就会弹出浏览器打开这个网址.思考题:1、如何为控件添加一个图标2、Delphi中的控件的共同基类是哪个类3、请做一个编辑框控件,当控件中输入的字符串是网址以,用浏览器打开此网址.二、控件开发纵览通过开发上边这个控件,我们已经对Delphi控件开发有了基本的认识.下面我们将系统的讲述一下控件开发的知识.制作控件第一件事就是选择适当的 Delphi对象类型作为父对象,以派生新的对象.子对象可以继承父对象的全部非private部件,但不能摆脱不需要的部件.因此,所选父对象应尽可能多地包含子对象所需的属性、事件和方法,但不应包含子对象不需要的东西.Delphi必须从Tcomponent或Tcomponent的子类派生.TComponent是所有 Delphi控件的基点,但若直接从 TComponent 派生新控件,很多东西就需要自己从头做起.一般只有非可视控件才直接从TComponent派生. Delphi提供了若干专门用于制作控件可视控件的对象类型,都是从 TControl和 TWinControl派生而来.TControl的子类型用于非窗口式控件, TWinControl的子类型则用于窗口式控件.除非特殊需要,一般不直接从TControl和TWinControl派生新控件,而是从其子类型派生.这样可以充分利用原有的属性、事件和方法,减少很多工作量.在这些控件类型中,非通用的属性、事件和方法都声明为protected.这样可以禁止控件用户访问,又能被子类型继承和修改.在新控件中,可以简单地把继承来的属性和事件重新声明为 published,使控件用户能在设计期通过对象编辑窗口访问,也可以进而修改属性的默认值和读写方式,或是重载 override事件处理子过程和其他控件方法,以修改其中的程序代码.重声明可以放宽访问权限,但不能相反,例如,不可能把 published属性重声明为 private或 protected. Delphi控件也是Delphi的类,所有的控件都有特定的结构.一般控件包括三大组成部分:属性、方法和事件,下面先介绍初学控件开发的最难懂的属性部分,其他部分我们将在以后章节为大家介绍.属性主要部分就是属性的读写方法或读写字段.前面的例子用的是读写字段,也就是对属性的读写都通过对字段的读写来完成.下面为大家讲解一下读写方法的使用方法:TmyComponent = classTComponentPrivateFcount: Integer;Procedure SetCountAvalue: Integer;PulbishedProperty Count: Integer read Fcount write SetCount;End;这个例子中当执行 := 1;这样的代码时,将会导致SetCount方法执行,并且参数Avalue被指定为1;当执行I := ;方法时,会将 Fcount的值返回给I.属性的声明语法允许属性声明的Read和Write部分用访问方法取代对象私有数据域.属性的读方法是不带参数的函数,返回同属性相同类型的值.通常读方法以Get开头.属性的写方法总是带一个参数的过程.写方法常常以Set开头.思考题:1、如何为控件添加属性2、从TwinControl类派生的控件的特点是什么Delphi控件开发浅入深出三三、开关控件TlincoSwitch用过Delphi1好古老的东东呀的人相信都记得这个开关控件,不知道当初Borland为什么把这么一个在开发普通应用程序中应用不到的工控控件放到Delphi中,而且在Delphi2及其以后的版本中再也没有见过它的身影.让我们怀着怀旧的心情把这位“开国元老”请出来吧1、建立位图资源文件:用Image Editor建立一个Res文件,并在文件中分别建立下面两个位图,并分别命名为SWITCHON、SWITCHOFF.保存此Res到控件单元所在目录下.2、写控件代码.unit LincoSwitch;interfaceusesSysUtils, Classes, Controls, Graphics, Windows;typeTLincoSwitch = classTCustomControlprivateFIsOn: Boolean;FPicOn: ;FPicOff: ;procedure FSetIsOnAValue: Boolean;protectedprocedure Click;override;procedure Paint;override;publicconstructor CreateAOwner: TComponent;override; destructor Destroy;override;publishedproperty 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 LoadBitmapFromResABitmapId: string; ABitmap: ;beginhInstance, ABitmapId;3、代码分析1、因为我们要在控件表面上将按钮的图案画出来,所以我们选择TcustomControl做为父类控件,因为它有个Canvas 属性,我们可以利用Canvas在控件表面作图.不选用Tcontrol的原因是因为它有很多我们不需要的属性.2、hInstance, ABitmapId;是从资源文件中读取Id为AbitmapId的位图,资源文件的使用请参考其他相关资料.注意代码中的“{$R .res}”,它的作用是将资源文件编译到程序文件中,如果没有这个预编译条件,程序将会出现错误.3、StretchBlt是将位图画到画板上,使用方法请参考MSDN.4、我们为控件增加了IsOn属性.这个布尔属性用来表示开关的状态开/关.从property IsOn: Boolean read FIsOn write FSetIsOn;我们可以看出这个属性是个可读可写的属性.当读这个属性时会将FisOn的值返回给调用者,而写属性时则会调用FsetIsOn方法,并将赋给属性的值做为参数传递给FsetIsOn.在FsetIsOn方法中,有如下实现代码:FIson := AValue;Invalidate;首先将Fison设置为参数传递来的值,然后调用Invalidate;要求重画控件,以告诉用户控件的状态已经改变,这一点是使用写字段无法做到的.5FPicOn: ;FPicOff: ;是声明两个.Tbitmap类型变量以保存控件的开关两种状态的图案.6procedure Click;override;procedure Paint;override;分别是覆盖父类中相应的调度方法.当控件被鼠标单击时,Click方法会被调用,我们将在Click中改变控件的开关状态;Paint方法则在用户调用 Invalidate方法或控件发生重画时调用,我们一般在这个方法绘制控件的图案.7、TcustomControl中又很多事件处理句柄.比如OnClick、OnKeyDown等,但是它把他们声明成了Protected保护级别,所以我们在Object Inspector中看不到他们,如果我们要他们可以在Object Inspector中被用户编辑的话,只要在Published中重新声明他们即可,不用写他们的读写方法,只要使用:Property 属性名;这样的方法就可以.比如这个例子中的:PropertyOnclick;思考题:1、做一个有特效的按钮控件,当鼠标按下时按钮是一个红色边框的空心圆,当鼠标松开时按钮是一个淡绿色边框的空心圆.2、对于父类控件中为protected的属性,如果想将它在子类控件中公布,应该怎么做请思考Delphi为什么要将一些属性设为protected级别Delphi控件开发浅入深出四四、对特定字符串敏感的Edit控件我们这个控件将演示控件的自定义事件的书写.这个控件有一个类型为string的SensitiveText属性,当用户在输入框中输入的文字为InvalidText时就会触发OnSensitiveText事件.按照惯例,我先把源码展示给大家:unit TextSenseEdit;interfaceusesSysUtils, Classes, Controls, StdCtrls;typeTSensitiveTextEvent = procedureAText: string of object;代码解释:1、SensitiveText属性的添加方法大家已经熟悉了,这里不多解释.2、正如大家猜测的,Change方法正是编辑框文字发生变化时的调度方法,它将引起OnChange事件.我们可以在这个方法中监控编辑框文字发生的变化,当文字等于SensitiveText就触发OnSensitiveText事件具体的实现方法在后边解释.3、Delphi中的控件的事件机制是通过方法指针来实现的.声明方法指针的格式为:方法指针名称 = procedure参数列表 of object;声明事件属性的方法与声明普通属性的方法相同.在我们这个例子中,我们首先声明一个FOnSensitiveText: TSensitiveTextEvent;私有变量,然后property OnSensitiveText: TSensitiveTextEvent read FOnSensitiveText write FOnSensitiveText; 声明事件属性.这样注册控件后,当用户把控件放到窗体中后,就会在Object Inspector中Evnets页中出现OnSensitiveText事件,我们就可以像使用其他事件一样使用这个事件了.但是我们现在只是声明了一个事件属性,并没有书写任何代码来激发这个事件.我们应该在合适的时候激发此事件,显而易见我们应该在Change方法中激发此事件:procedure ;begininherited;if Text = SensitiveText thenif AssignedOnSensitiveText thenOnSensitiveTextText;end;当if Text = SensitiveText时就判断控件使用者是否为OnSetSensitiveText写代码了准确的说是是否为OnSetSensitiveText事件句柄赋值了,如果写代码了则调用OnSetSensitiveTextText;来激发OnSetSensitiveText事件,并把控件的Text传递给方法的Avalue参数.正如“方法指针”这个名字一样,被声明为方法指针类型的变量可以当作方法使用,用来激发事件.VCL已经为我们预定义了一些常用的事件句柄,我们直接拿来使用:TnotifyEvent,TmouseEvent,TmouseMoveEvent,TkeyPressE vent等,具体可以参考VCL源码.思考题:1、做一个支持累加运算的文本编辑框控件,用户可以在编辑框中输入正整数.当用户按回车时,如果编辑框中输入的不是正整数为负数、小数或一般字符串则触发控件的OnError事件;如果输入的是正整数,则开始计算从1到用户输入的那个正整数中所有整数的和用1+2+3+……这种累加的办法实现,不要用1+nn/2这种直接计算的方法,并且在计算工程中如果发现计算的中间结果位数是5,则触发OnTailFive事件.Delphi控件开发浅入深出五五、复合控件复合控件是Delphi控件中非常重要的一种控件,复合控件就是将两个或两个以上的控件重新组合成一个新的控件.例如TspinEdit、TlabeledEdit、TDBNavigator等就是复合控件,TDBNavigator其实就是在一个Panel放上若干个Button 而已.制作一个复合控件时,我们一般从TwinControl派生控件.我们这次做的控件是拥有一个Edit编辑框和一个Button 按钮的复合控件,在用户在编辑框中输入文字的过程中,Button将随时显示编辑框中文字的长度.我们把控件的源码先展示给大家.unit EditButton;interfaceusesSysUtils, Classes, Controls, StdCtrls, Messages;typeTEditButton = classTWinControlprivateFEdit: TEdit;FButton: TButton;FText: string;procedure FSetTextAValue: string;procedure OnEditChangeSender: TObject;protectedprocedure WMSizevar Msg: TMessage;message WM_SIZE;publicconstructor CreateAOwner: TComponent;override;destructor Destroy;override;publishedproperty Text: string read FText write FSetText;end;procedure Register;implementationprocedure Register;beginRegisterComponents'Linco', TEditButton; end;constructor AOwner: TComponent;begininherited;FEdit := nil;:= self;:= 0;:= 0;:= Height;:= Width div 2;:= OnEditChange;FButton := nil;:= self;:= 0;:= Width div 2;:= Height;:= Width div 2;end;destructor ;begin;;inherited;end;procedure AValue: string; begin:= AValue;end;procedure Sender: TObject; begin:= IntToStrLength;end;procedure var Msg: TMessage; begin:= Height;:= Width div 2;:= Width div 2;:= Height;:= Width div 2;end;end.代码解释:1、我们首先定义了两个变量FEdit: TEdit;FButton: TButton;分别代表复合控件中的文字编辑框和按钮.2所谓复合控件说简单一点就是在一个共同的基板上将组成复合控件的各个控件可以叫做子控件画出来.所以我们在构造函数中建立各个子控件,然后分别设定它们的位置等属性.以文字编辑框为例:FEdit := nil;的作用是建立编辑框控件.如果Create的参数指定为nil,则子控件在设计状态是可以响应用户的操作的;而如果设定为self即设定子控件的父控件为基板,则子控件在设计时时不可响应用户操作的,如果设定为self则析构函数中就不用来销毁对象了,对象会自动销毁.:= self;的作用是设定子控件的父控件,如果没有这一句则控件是无法显示的.:= 0;:= 0;:= Height;:= Width div 2;这四句是设定控件在基板上的相对位置的,这里的Top,Left不是相对于窗体的,而是相对于基板的.:= OnEditChange;则是设定编辑框控件的OnChange文字改变事件的处理句柄为OnEditChange;1 用户有可能在设计时或运行时通过代码改变控件的大小,这时控件中子控件的顺序就会变得乱七八糟,所以需要相应控件的WM_SIZE事件控件大小发生变化的事件重新设定子控件的位置,大小等.函数WMSize的作用就是这样的.安装控件后发现控件已经可以正确运行了,但是还有一个问题,就是这个控件没有了Onclick,Onchange等必须的属性.我们只要为控件增加事件处理句柄属性,然后把事件处理句柄属性的读写方法都指向子控件的事件处理句柄属性即可.例如我们为控件增加OnClick事件,这个事件发生在用户单击按钮时,我么只要在Pulished部分增加如下代码:property OnClick: TnotifyEvent read GetOnClick write SetOnClick在Private中增加如下方法声明:function GetOnclick: TnotifyEvent;procedure SetOnclickAValue: TnotifyEvent;这两个方法的实现分别为:function TeditButton. GetOnclick: TnotifyEvent;beginresult := ;end;procedure TeditButton. SetOnclickAValue: TnotifyEvent;begin:= Avalue;end;思考题:1、做一个模仿播放器中的操作按钮的复合控件,控件由三个按钮组成,分别是“播放”、“暂停”、“停止”,请按照正常的逻辑关系,处理这三个按钮的可用/不可用关系.提示:可以参考TDBNavigator的源代码Delphi控件开发浅入深出六六、控件手拉手――控件关联的实现控件的关联在Delphi中也是很常见的,我们可以设定一个控件的某个属性指向另一个控件.比如我们在窗体上放上Tedit,TpopupMenu两个控件,然后设定Tedit的PopupMenu 属性为TpopupMenu控件,运行后在Tedit点击右键就会弹出刚才设定的那个TpopupMenu菜单,也就是说Tedit,TpopupMenu联手完成了任务.再比如TDBEdit控件的DataSource属性就可以指向一个TdataSource控件,这样就可以在TDBEdit控件中显示TdataSource输出的某个字段的值了.下面我们将写一个简单的实现控件关联的控件.这个控件派生于Tedit,它可以与一个Tlabel控件关联,在控件的编辑框中输入文字时,与它关联的Tlabel控件的文字将随着它而变化.代码如下:unit MyEdit;interfaceusesSysUtils, Classes, Controls, StdCtrls;typeTMyEdit = classTEditprivateFLinkLabel: TLabel;procedure FSetLinkLabelAValue: TLabel;protectedprocedure NotificationAComponent: TComponent;Operation: TOperation;override;procedure Change;override;publicpublishedproperty LinkLabel: TLabel read FLinkLabel write FSetLinkLabel;end;procedure Register;implementationprocedure Register;beginRegisterComponents'Linco', TMyEdit;end;procedure ;begininherited;if LinkLabel <> nil then:= Text;end;procedure AValue: TLabel;beginFLinkLabel := AValue;if AValue <> nil thenself;end;procedure AComponent: TComponent;Operation: TOperation;begininherited;if Operation = opRemove and AComponent = LinkLabel thenLinkLabel := nil;end;end.代码解释:1、我们只要将控件的任意一个属性的类型设定为另外一个控件的类名称,那么我们就可以在控件的Object Inspector中将这个属性指向那个控件或那个控件的派生控件的一个实例.比如本例中我们增加了LinkLabel属性,它的类型为 Tlabel,所以我们就可以把LinkLabel属性指向一个标签控件.2、请注意FsetLinkLabel中的这段代码:if AValue <> nil thenself;如果我们将控件关联属性指向了一个控件,可是后来又将被指向的控件删除了,那么我们的控件关联属性是不会自动删除的,这样就会造成控件关联属性指向的控件不存在的现象.我们必须自动感知被关联控件的删除并重新设定控件关联属性为不指向任何控件,这样就避免了错误的发生.self;的作用就是这样的.它调用控件的FreeNotification方法在Tcomponent中定义向被指向的控件注册一个“消息”,当被指向控件被删除时,会向所有向他注册的控件发送一个它被删除的消息,此时向他注册的控件就会触发Notification方法,这样我们就可以自动感知被指向控件的状态了.这是设计模式中Observer观察者模式的典型应用.既然向他注册的控件就会触发Notification方法,我们就覆盖父类的Notification方法,写出如下的代码: if Operation = opRemove and AComponent = LinkLabel thenLinkLabel := nil;这句话的意思是:如果控件被删除并且被删除的控件因为我们的控件可能向多个控件注册了消息是LinkLabel,那么我们就设定LinkLabel属性不指向任何控件.3覆盖父类的Change调度方法.在此方法里为连接的LinkLabel的Caption赋值就达到我们的目的了.思考题:1、做一个Label控件,给它增加一个DataSource属性,该属性可以指向一个TdataSource类型的控件,它有一个GetRecordCount方法.当调用此方法时,就在Label控件中显示这个DataSource对应的数据集中的记录的条数.Delphi控件开发浅入深出七对话框控件的制作Delphi中有很多对话框组件,例如TopenDialog、TfontDialog等.这些控件的特点就是虽然是不可视控件,但是在运行时都有一个可视化的效果,比如TopenDialog的可视化效果就是一个打开对话框.我们这次将开发一个日期对话框控件,当我们调用控件的Execute方法不一定非要使用Execute方法,不过大部分对话框控件都是使用这个方法,我们也就按照惯例来了时,就会弹出一个可以选择日期的对话框,我们选择一个日期后,点击“确定”则Execute返回True,点击“取消”则Execute返回False.我们可以读取Date属性来得到用户选择的日期,也可以修改此属性来改变对话框的初始日期.1、新建一个对话框.在对话框窗体上放置一个TmonthCalendar组件,命名为Cal,窗体名称改为FormDate.在窗体上放置两个按钮,一个按钮的Caption为“确定&O”,ModalResult为mrOk,一个按钮的Caption为“取消&C”,ModalResult为mrCancel.设计好的窗体如下图所示:2、为窗体添加两个Public访问级的方法:function GetSelDate: TDate;procedure SetInitDateAValue: TDate;代码如下:function : TDate;beginresult := ;end;procedure AValue: TDate;begin:= AValue;end;3、新建一个控件,派生自Tcomponent.代码如下:unit DateDialog;interfaceusesSysUtils, Classes, Controls, frmDlg;typeTDateDialog = classTComponentprivateFDlg: TFormDate;function GetDate: TDate;procedure SetDateAValue: TDate;protectedpublicconstructor CreateAOwner: TComponent;override;function Execute: Boolean;publishedproperty Date: TDate read GetDate write SetDate;end;procedure Register;implementationprocedure Register;beginRegisterComponents'Linco', TDateDialog; end;constructor AOwner: TComponent;begininherited CreateAOwner;FDlg := self;end;function : Boolean;beginresult := = mrOK;end;function : TDate;beginresult := ;end;procedure AValue: TDate;beginAValue;end;end.代码比较简单就不多解释了.思考题:1、做一个模仿TcolorDialog的对话框控件.Delphi控件开发浅入深出八八、数据敏感控件的制作.Delphi的一大亮点就是它的数据库开发能力.而数据敏感组件则在这中间起着很重要的作用.在Delphi的Data Control页面下的控件都是用于显示和编辑数据库中的数据的.相信大家已经体会到数据敏感控件的好处了.我们这一节就给大家演示一下数据敏感控件的开发方法.需要提醒大家的是,不像其他体系的控件,数据敏感控件并没有一个统一的基类,只要是从TwinControl类或其子类派生就可以,数据敏感控件的特殊之处就在于我们下面提到的数据连接.相信用Delphi开发过数据库的人一定对delphi中没有一个日期数据敏感控件而恼火.每次都要我们自己处理数据的更新与显示.所以我们就来开发一个DBDateTimePicker控件.新建一个控件,从TdateTimePicker派生,源代码如下:{}{ Linco TDBDateTimePicker{ mail me: }{}unit DBDateTimePicker;interfaceusesSysUtils, Classes, Controls, ComCtrls, DBCtrls, Messages, DB;typeTDBDateTimePicker = classTDateTimePickerprivateFDataLink: TFieldDataLink;procedure CMGetDataLinkvar Msg:TMessage;message CM_GETDATALINK;procedure DataChangeSender: TObject;procedure EditingChangeSender: TObject;procedure FSetDataFieldAValue: string;procedure FSetDataSourceAValue: TDataSource;procedure FSetReadOnlyAValue: Boolean;procedure ShowData;procedure UpdateDataSender: TObject;function FGetDataField: string;function FGetDataSource: TDataSource;function FGetField: TField;function FGetReadOnly: Boolean;protectedprocedure Change;override;procedure NotificationAComponent: TComponent;Operation: TOperation;override;publicconstructor CreateAOwner: TComponent; override;destructor Destroy; override;property Field: TField read FGetField;publishedproperty DataField: string read FGetDataField write FSetDataField;property DataSource: TDataSource read FGetDataSource write FSetDataSource;property ReadOnly: Boolean read FGetReadOnly write FSetReadOnly;end;procedure Register;implementationuses Variants;constructor AOwner: TComponent; begininherited CreateAOwner;FDataLink := ;:= DataChange;:= self;:= EditingChange;:= UpdateData;:= Now;end;destructor ;begin;inherited;end;procedure var Msg: TMessage; begin:= IntegerFDataLink;end;procedure Sender: TObject;beginif Field<>nil thenif = null thenif = dsEditor = dsInsert then:= Now;ShowData;end;procedure Sender: TObject;beginif DataSource <> nil and DataField <> '' then ;end;procedure AValue: string;begin:= AValue;end;procedure AValue: Boolean;begin:= AValue;end;procedure ;beginif DataSource <> nil and DataField <> '' andField<>nilthenbegincase Kind ofdtkDate: if <> '' then:=else:= Now;dtkTime: if <> '' then:=else:= Now;else:= Now;end;end;end;procedure AValue: TDataSource;begin:= AValue;if AValue <> nil thenself;end;procedure ;beginif DataSource <> nil and DataField <> '' then begin;:= ;end;inherited Change;end;procedure AComponent: TComponent;Operation: TOperation;beginif Operation = opRemove and FDataLink <> nil and AComponent = DataSource thenDataSource := nil;end;procedure Sender: TObject;vart: TFieldType;beginif DataSource <> nil and DataField <> '' then begint := case t offtTime: := ;ftDate: := ;ftDateTime: := ;end;end;end;function : string;beginresult := ;end;function : TDataSource;beginresult := ;end;function : TField;beginresult := ;end;function : Boolean;beginresult := ;end;procedure Register;beginRegisterComponents'Linco', TDBDateTimePicker;end;end.谈到开发数据敏感控件就不得不说数据连接DataLink,数据连接有很多种,开发数据敏感控件最常用到的就是字段数据连接TFieldDataLink.数据连接是联系数据敏感控件和数据库的通道.在数据敏感控件中就是凭借着数据连接来处理数据的更新和显示的.从后边我们的描述中您将更加能体会到,正是数据连接把数据在数据库中的表示反映到用户界面中,也是数据连接把数据从用户界面更新到数据库中.数据连接就是一个“大媒人”这其实是设计模式中Mediator中介者模式的典型应用.。

相关文档
最新文档