热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

组件制作之二(一个简单组件的制作过程)

接下来要动手来做一个组件了,我想了一个计数器组件,功能方面是比较简单的,但这不是本章的重点,这一章的重点是说明一个组件的制作全过程。在其中可以学到很多组件制作的技巧,当然这些也是我从书上学得的。

接下来要动手来做一个组件了,我想了一个计数器组件,功能方面是比较简单的,但这不是本章的重点,这一章的重点是说明一个组件的制作全过程。在其中可以学到很多组件制作的技巧,当然这些也是我从书上学得的。好了,开始制作吧:

 

这是一个可以计数的组件,为了简单,我以秒为单位,当开始时,他就从0开始增加,并显示出来,到3600时,它又回到0,如此循环。当然它也可以停止,暂停,继续。另外,他还有一个时间事件,即可以设定每隔多少时间来触发这个事件,这倒是一个好的功能,我们可以设半个小时触发事件,在事件处理函数中来一个声音,然后睡个觉,半个小时后,就由这个声音来提醒你起床了。

 

我们一开始不要新建组件单元,而是把它当成一个工程中的一个类来应用,这样更易于调试,于是我们新建一个工程,又新建一个空白单元用于放置这个类

 

一,确定父类:

 

接下来给这个类起名叫TTimeCount,那么它的父类应该是什么呢,它要能显示出来,得有一个容量来让他显示,TPanel是个不错的选择,我看了一下源码,发觉TPanel什么也没有做,只是把它的父类TCustomPanel的属性显化出来(这个等一下讲),TCustomPanel把好多的属性声明为Protected,为他的子类提供了选择,他的子类如果要使这些属性能在对象察看器中看到,可以在Published重新声明一下,如果不想,则不用去理会他。而我们的计数器组件正好不用那么多属性,正好合适。于是我决定用TCusomPanel为父类

类建立如下:

TTimeCount=class(TCustomPanel)

  private

  protected

  public

  published

end;

 

二,确定成员,方法和属性和事件

 

FCount; 只读私有成员,保存计数值

FActive:Boolean; /确定是否发生类的时间间隔事件

FInterval:TInterval; 这个可以设置时间事件触发间隔,本来用整型值会好一些,但会了学习起见,这里用了一个枚举形的,在Type中声明如下:

TInterval=(TenM,TwentyM,ThirtyM,FortyM,FiftyM,SixtyM);

分别表示十分钟到六十分钟。

TimeLenTimeNum:integer;这两个用在类的时间事件发生的确定上,与外界隔绝。

FTimeOut:TNotifyEvent;时间间隔事件的方法指针,通过调度方法实现他与外部的处理函数关联。

 

我们要它能以秒为单位来计数,则要涉及到时间的应用,所以就有了这个最重要的成员:

FTimer:TTimer;

这个成员对象要在类构造函数中实例化它,并赋与他的属性值,还要在析构函数在释放它。

如下:

 //构造函数,继承父类的构造函数,并初始化类中的成员。

constructor TTimeCount.Create(AOwner:TComponent);

//创建时间控件并设置相关的参数

  procedure CreateTimer;

  begin

      FTimer:=TTimer.Create(self);

      FTimer.Enabled:=False;

      FTimer.Interval:=1000;

      FTimer.OnTimer:=FTimerTimer;

  end;

begin

  inherited Create(AOwner);

  CreateTimer;

end;

//析构函数,先释放时间控件,再继承父类的析构函数

destructor TTimeCount.Destroy;

begin

  FTimer.Free;

  inherited Destroy;

end;

构造函数中还要设置该组件的外观和默认值,这里把它删去,到源代码再贴出来。

其中

FTimerTimer;是很重要的函数,在该类中有声明:

procedure FTimerTimer(Sender:Tobject);//时间控件的事件处理函数

在这个处理函数中,实现了计数值的递增并显示到容器中,此外,还在判断类的时间事件是否有足够条件触发了,如果有则调用DoTimeOut;过程,这个就是事件的调度函数啦:

//事件调度函数,将外部的事件处理函数和该类的事件方法指针联系起来

procedure TTimeCount.DoTimeOut;

begin

  if Assigned(FTimeOut) then

     FTimeOut(Self);

end;

 

而属性则是根据私有成员来设定了:

public

property Count:Integer read FCount default 0; //计数值的只读属性,这个属性不能声明在Published中,因为它是只读的,只在程序运行时通过它来得到计数值。

published

    property Interval:TInterval read FInterval write SetInterval Default TenM;

    property Active:boolean read FActive write SetActive default false;

    property OnTimeOut:TNotifyEvent read FTimeOut write FTimeOut;

 

此外还有几个自定义方法即

procedure pause; //暂停计数

procedure Resume;//从暂停的计数开始计数。

procedure stop;//停止

procedure start;//开始计数

都比较简单。

 

三,父类属性的显化:

 

TCustomPanel及其父类有好多的属性设为Protected,使其子类可以有更灵活的选择,是否把这些属性显示到对象察看器中,如果想,则到Published中重新声明这些属性就可以,我参考了一下TPanel的源码,并按需要选择了其中的一些属性声明到Published中,注意哦,事件也是属性,只要你把它显化出来,就可以设置处理事件了。

 

四.以下是计数组件的源码,相信有了上面的讲解,应该不会很难了:

 

unit CountUnit;

 

interface

 

uses

  SysUtils,Classes,Graphics,Controls,ExtCtrls;

 

type

//用于设置时间事件发生的间隔

  TInterval=(TenM,TwentyM,ThirtyM,FortyM,FiftyM,SixtyM);

 

  TTimeCount=class(TCustomPanel)

  private

    FTimer:TTimer;

    FCount:integer;  //只读私有成员,计数值

    FInterval:TInterval; //时间事件发生的间隔

    FActive:Boolean; //决定是否发生间隔事件

    TimeLen:Integer;//发生事件的时间长度,以秒为单位。

    TimeNum:integer;//计数值,和TimeLen一起有用,以判断是否事件该发生了

    FTimeOut:TNotifyEvent;//事件的方法指针

    procedure SetInterval(I:TInterval);

    procedure SetActive(A:boolean);

    procedure FTimerTimer(Sender:Tobject);//时间控件的事件处理函数

  protected

    procedure DoTimeOut;dynamic; //调度方法,用于关联事件。

  public

    procedure pause; //暂停计数

    procedure Resume;//从暂停的计数开始计数。

    procedure stop;//停止

    procedure start;//开始计数

    constructor Create(AOwner:TComponent);override;

    destructor  Destroy;override;

    property Count:Integer read FCount; //计数值的只读属性

  published

    property Interval:TInterval read FInterval write SetInterval Default TenM;

    property Active:boolean read FActive write SetActive default false;

    property OnTimeOut:TNotifyEvent read FTimeOut write FTimeOut;

    //显式祖先类的一些属性在对象察看器中

    property BevelInner;

    property BevelOuter;

    property BevelWidth;

    property Color;

    property Font;

    property PopupMenu;

    property ShowHint;

    property TabOrder;

    property TabStop;

    property Visible;

    property OnClick;

    property OnDblClick;

    property OnMouseDown;

    property OnMouseMove;

    property OnMouseUp;

  end;

 

implementation

//构造函数,继承父类的构造函数,并初始化类中的成员。

constructor TTimeCount.Create(AOwner:TComponent);

//创建时间控件并设置相关的参数

  procedure CreateTimer;

  begin

      FTimer:=TTimer.Create(self);

      FTimer.Enabled:=False;

      FTimer.Interval:=1000;

      FTimer.OnTimer:=FTimerTimer;

  end;

//以下是设置外观的

  procedure setView;

  begin

    Width:=100;

    Height:=50;

    Color:=$000000;

    Font.Color:=$FFFFFF;

    Font.Size:=14;

    Font.Style:=[fsBold,fsUnderline];;

    BevelOuter := bvLowered      ;

    Caption:='0';

  end;

begin

  inherited Create(AOwner);

  FCount:=0;

  FInterval:=TenM;

  FActive:=False;

  TimeLen:=600;//十分钟,六百秒

  TimeNum:=0;

  CreateTimer;

  setView;

end;

//析构函数,先释放时间控件,再继承父类的析构函数

destructor TTimeCount.Destroy;

begin

  FTimer.Free;

  inherited Destroy;

end;

//设置时间事件发生间隔,财时要赋相应的间隔秒数

procedure TTimeCount.SetInterval(I:TInterval);

begin

  if FInterval<>I then

  begin

    FInterval:=I;

    case FInterval of

    TenM: TimeLen:=600;

    TwentyM:TimeLen:=1200;

    ThirtyM: TimeLen:=1800;

    FortyM: TimeLen:=2400;

    FiftyM:TimeLen:=3000;

    SixtyM:TimeLen:=3600;

    end;

  end;

end;

 

procedure TTimeCount.SetActive(A:boolean);

begin

  if FActive<>A then

  begin

      FActive:=A;

      TimeNum:=0;

  end;

end;

 

procedure TTimeCount.pause;

begin

 if FTimer.Enabled then

  FTimer.Enabled:=False;

end;

 

procedure TTimeCount.Resume;

begin

  if not FTimer.Enabled then

    FTimer.Enabled:=True;

end;

 

procedure TTimeCount.stop;

begin

  FTimer.Enabled:=False;

  FCount:=0;

  TimeNum:=0;

  caption:='0'

end;

 

procedure TTimeCount.start;

begin

  if (not FTimer.Enabled)and(TimeNum=0) then

     FTimer.Enabled:=True;

end;

 

//最重要的时间函数,用于调用该类的事件触发高度函数。

//以及在容器中显示计数值

procedure TTimeCount.FTimerTimer(Sender:TObject);

begin

  inc(FCount);

  if (FCount mod 3600)=0 then FCount:=0;

  Caption:=InttoStr(FCount);//这个就是显示计数值

  inc(TimeNum);

  if (TimeNum=TimeLen)and(FActive) then

  begin

       DoTimeOut;

       TimeNum:=0;

  end;

end;

//事件调度函数,将外部的事件处理函数和该类的事件方法指针联系起来

procedure TTimeCount.DoTimeOut;

begin

  if Assigned(FTimeOut) then

     FTimeOut(Self);

end;

 

end.

 

五,组件注册安装与删除。

 

组件类编写完毕,接着就要测试了,这个从略,可以在刚才的工程中动态创建它,设置相应属性,指定时间事件,看看是否正确。上面是经过测试的。

到确定正确后,就要来看看装组件的步骤了:

 

首先,我们要把组件单元放在一个指定的文件夹中,以便以后管理,和IDE统一指定路径。

我在Delphi目录下新建了MyCom文件夹,用来存放组件单元

然后选:

Component—>New Component;

启动Component Wizard

Ancestor type(父类):中填TCustomPanel

Class Name(你的组件类名)中填:TimeCount

Palette Page,默认为Samples,我就用这个吧,当然也可以自己创建一个。

Unit File Name(组件类所在的单元名),就写TimeCount吧。

Search Path(搜索路径),必须把上面所建的文件夹路径包含进去,编译时才能能过。

                      点击旁边的”…”按钮,出现一个对话框,里面的编辑框旁边又有一个”…“按钮,点击它出现浏览文件夹框,在其中选中上面建的文件夹的路径,点确定,再点OK,回去New Component;

                      这时再点下面的OK就行啦,出现代码如下:

unit TimeCount;

 

interface

 

uses

 messages, SysUtils,Classes,Graphics,Controls,ExtCtrls;

 

type

  TTimeCount=class(TCustomPanel)

  private

  published

  public

  published  

  end;

 

procedure Register;

 

implementation

 

procedure Register;

begin

  RegisterComponents('Samples', [TTimeCount]);  //注册控件用的函数,自动帮你生成

end;

end.

这时再把上面的组件类替换掉这个类就行啦(当然上面的注册函数可要保留呀)。然后保存到上面所建的文件夹中。

 

接着就要安装组件了,点Component->install Component

对话框中第一项Unit file Name,是组件单元.pas文件的路径名字。其他只要按缺省。

           记得第三项的。。/delphi7/Lib/dclusr.dpk,这个就是存放组件的包名,等一下删除组件时用到的

                   接下来点OK,就可以了,看看Samples是不是有了一个新组件啦。

                   

 不过面板上的组件图标似乎不好看,应该给他一个新的图标了:

 但之前应该把它先从面板删除,File->Open…打开dclusr.dpk.

选中其中的Contains下的TimeCount.pas,点上面的Remove。将其删除

然后再点Compile,重新将包编译一次。就行啦。Samples上的新组件就消失了。

 

接下来是做组件图标:

打开Image Eidtor,新建一个DCR文件,在其中建一个24×24的位图,画上你喜欢的图标,将位图的名字起为组件类名一样,而且用大写,即:TTIMECOUNT

DCR文件起名为组件类所在单元名一样,而且大写,即:TIMECOUNT

保存文件到单元所在的文件夹中,

最后依上面的方法再装一次,看是不是有一个漂亮的图标啦

 

 

六,讲完了,大家应该有一个清楚的概念了吧,其中涉及到了好多的技巧。在以后的篇章中将不再讲这些内容了,只把主要精力花在组件实现技术上。因为我们这个组件还是比较简单,没有涉及到多少高级的主题。但没有关系,跟着我慢慢地做下来,你们会了解组件制作的方方面面的。

推荐阅读
  • PrivateConstLF_FACESIZE32PrivateConstCF_PRINTERFONTS&H2PrivateConstCF_SCREENFONTS ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 本文深入探讨了POJ2762问题,旨在通过强连通分量缩点和单向连通性的判断方法,解决有向图中任意两点之间的可达性问题。文章详细介绍了算法原理、实现步骤,并附带完整的代码示例。 ... [详细]
  • 本文介绍了Linux系统中的文件IO操作,包括文件描述符、基本文件操作函数以及目录操作。详细解释了各个函数的参数和返回值,并提供了代码示例。 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • 树链问题的优化解法:深度优先搜索与质因数分解
    本文介绍了一种通过深度优先搜索(DFS)和质因数分解来解决最长树链问题的方法。我们通过枚举树链上的最大公约数(GCD),将所有节点按其质因子分类,并计算每个类别的最长链,最终求得全局最长链。 ... [详细]
  • 在进行QT交叉编译时,可能会遇到与目标架构不匹配的宏定义问题。例如,当为ARM或MIPS架构编译时,需要确保使用正确的宏(如QT_ARCH_ARM或QT_ARCH_MIPS),而不是默认的QT_ARCH_I386。本文将详细介绍如何正确配置编译环境以避免此类错误。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • 在PB数据窗口中,错误处理技术主要针对两类问题进行优化:一是由用户不当数据输入引发的错误,二是程序执行过程中因代码缺陷导致的异常。高效的应用程序设计需确保无论出现哪种类型的错误,系统都能有效应对,保证稳定性和用户体验。通过引入先进的错误检测与恢复机制,可以显著提升系统的健壮性和可靠性。 ... [详细]
  • 发现《iphone3开发基础教程》中大部分说的是xib的模板,直接学习中用StoryBoard碰到很多问题,在现有阶段要花很多时间来找度娘聊天,故决定,在这次学习过程中先以xib为主,在自学完后 ... [详细]
  • TICortexM3串口转以太网例程上层应用的基础是lwIP,版本是V1.3.2。对于lwIP,陌生的同学可以到网上查查,它是是瑞士的Adam编写的一个开源TCPIP协 ... [详细]
author-avatar
e26462510
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有