当前位置:文档之家› 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源代码包括在客户服务器版本和专业版本里。

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

21.1.2 编写组件的一般步骤

假设已经确定问题所在,并且要创建一个新组件,下面是创建一个组件时应该注意的几个方面:? 首先,要确定是否需要创建一个独特的新组件。

? 接着,坐下来好好规划一下组件的工作方式。

? 做好了准备工作,不要急于实际创建组件,得先问问自己:到底需要这个组件干什么?

? 把组件从逻辑上分为几个部分。这样,不仅有利于组件的模块化、简单化,而且能使代码精炼、组织良好。设计组件时,要考虑到可能会有其他程序员要基于你的组件派生出一个新的组件。

? 设计完一个组件,必须先在一个程序中测试一下该组件,才能将它加到组件面板上。

? 最后,把组件及它的图标加到面板上,这样,就可以在应用程序中使用它。

编写一个组件分为6个步骤:

1) 确定一个祖先类。

2) 创建一个组件单元。

3) 在新组件中添加属性、方法和事件。

4) 测试该组件。

5) 在D e l p h i中注册该组件。

6) 为该组件建立帮助文件。

本章只讨论了前面5个步骤,第6个步骤超出了本章的范围。不过,这并不意味着这个步骤没有其他5个步骤重要。我们建议借助于一个合适的第三方工具来制作帮助文件。D e l p h i的在线帮助中提供了这方面的信息。

21.1.3 确定一个祖先类

第2 0章“V C L元素和运行期类型信息”讨论了V C L的继承关系,以及在不同级继承关系上类的不同用途,介绍了四种基本组件:标准组件、自定义组件、图像组件和非可视组件。譬如,如果需要扩展一个现有的Wi n32组件如T M e m o,应当创建一个标准组件。如果需要创建一个全新的组件,应当创建一个自定义组件。如果如果创建一个图形组件,那么它必须是可视的,并且不占用Wi n32系统太多资源。如果要创建的组件设计时可以在Object Inspector 里编辑,运行时不可见,就应当创建一个非可视组件。不同的V C L类代表不同的类型的组件。你不妨回头复习一下第2 0章,除非已经熟练掌握了这些概念。表2 1-1给出了V C L中的基类。

表21-1 VCL中的组件基类(祖先类)

V C L类描述

TO b j e c t直接从TO b j e c t继承下来的类不是组件。有些以此为基类的对象在设计期不需要使

用,如T I n i F i l e

T C o m p o n e n t这是非可视组件的起点,它的特点是在设计时能够以流的方式在I D E上存取

T G r a p h i c C o n t r o l创建一个不需要窗口句柄、但要在屏幕上显示的组件时,用这个类作为祖先类T W i n C o n t r o l所有需要窗口句柄的组件,都应以该类为基类。该类提供了Wi n d o w s组件的一般

属性和事件

T C u s t o m C o n t r o l该类是从T W i n C o n t r o l继承下来的。它具有C a n v a s属性和P a i n t()方法,能够控制组

件的外观。也用于需要句柄的组件

T C u s t o m C l a s s N a m e V C L中有些类的属性是不公开的,它们用来作为组件的祖先类。可以以它为祖先

类创建出自定义组件,每个组件公开自己的属性

T C o m p o n e n t N a m e一个现有的组件,譬如T E d i t、T P a n e l或T S c r o l l B o x。与其创建一个新的组件,不

如扩展一个现有的组件。大部分自定义组件都是这样的

你应该深刻理解这些类的现有组件的功能。通常情况下,你会觉得以现有的组件作为起点,是很合适的。只有掌握了现有组件的功能,才能有目标地选择现有的组件作为基础。我们无法告诉你该选择哪个组件,只有完全掌握V C L中的每个组件和类,才能更好地扩展现有的组件。

21.1.4 创建一个组件单元

确定了祖先类,就可以创建一个组件单元了。下面几个部分演示了

怎样设计一个新组件。因为重点是讨论怎样创建一个组件,而不是组件

的功能,所以这个组件实际没什么用。

这个组件被称为T

d

d g W o r t h l

e s s 。它是从T C u s t o m C o n t r o l 继承下来的,

于是该组件既具有窗口句柄,又能画自己。它继承了T C u s t o m C o n t r o l 的

属性、方法和事件。

要创建该组件的单元,最容易的方法是使用组件专家( C o m p o n e n t E x p e r t ),如图2 1-1所示。

通过选择Component|New Component 菜单命令来打开组件专家。在组件专家里,输入组件的祖先类名、组件的类名、组件显示在的面板页和组件的单元名。单击O K ,D e l p h i 会自动创建组件单元,该单元有组件类型的声明和注册过程。清单2 1-1列出该单元的代码。

清单21-1 一个范例组件的单元: W o r t h l e s s .p a s

你可能发现,T d d g W o r t h l e s s 组件还只是一个空架子。在后面的章节将逐步给T d d g W o r t h l e s s 加入属性、方法和事件。

21.1.5 添加属性

第2 0章介绍了属性的用法和使用属性的好处。本节介绍怎样在组件里加入属性。

属性的类型

第2 0章的表2 0-1列出了各种属性的类型。我们把各种类型的属性添加到T d d g W o r t h l e s s 组件中,以阐述不同类型属性的区别。在Object Inspector 中不同类型的属性的编辑方式是不同的。你不妨可以试验各种类型,看它们是怎样编辑的。

(1) 加入简单类型的属性

简单类型的属性是指数字、字符串和字符。用户在Object Inspector 中可以直接编辑它们,不要求图21-1 组件专家

特殊的访问方法。清单2 1-2列出了T d d g W o r t h l e s s组件的3个属性。

清单21-2 简单类型的属性

你应该对声明属性的语法已经很熟悉了,因为在第2 0章已经讨论过了这个问题。这里,组件的内部数据是在p r i v a t e部分声明的。而属性是在p u b l i s h e d部分声明的,这就意味着当把组件安装到D e l p h i 的环境中时,就可以在Object Inspector中编辑这些属性。

注意编写组件时,组件内部的数据字段名一般以F打头,而组件和类一般以T打头。如果遵守这样命名规则,程序代码就比较清晰易懂。

(2) 加入枚举型的属性

如果在Object Inspector中编辑枚举型和布尔型的属性,可以反复双击值栏,直到需要的值出现,或者从下拉列表中选择一个值。例如枚举型属性A l i g n,大部分可视的组件都有这个属性。要加入一个枚举型属性,必须首先声明一个枚举类型,如下:

然后声明一些内部的字段来代表每一个枚举值。清单2 1-3列出了T d d g W o r t h l e s s组件中的两个枚举类型。

清单21-3 枚举型的属性

这里其他类型的属性没有被包括。如果要安装该组件,枚举型的属

性将出现在Object Inspector中,如图2 1-2所示。

(3) 加入集合型的属性

要在Object Inspect中编辑集合型的属性,可以把这个属性展开。这样,

集合型的每个元素就好像布尔类型的属性。如果要给T d d g W o r t h l e s s组件

加入一个集合型的属性,首先必须声明一个集合类型:

这里首先声明了一个枚举类型T S e t P r o p O p t i o n,列举了集合中的元素。图21-2 显示枚举型的属性

然后,定义了集合T S e t P r o p O p t i o n s 。

现在可以把一个

T S

e t P r o

p O p

t

i o n s 类型的属性加到T d d g W o r t h l e s s 组件中:

图2 1-3显示了在Object Inspector 中展开这个属性的样子。

(4) 加入对象型的属性

属性可以是对象,也可以是另一组件。例如,T S h a p e 组件的B r u s h 属

性和P e n 属性就分别是T B r u s h 对象和T P e n 对象。如果一个属性是对象,它

就可以在Object Inspector 中被展开,这样就可以编辑对象本身的属性。如

果属性是对象,该对象必须是从T P e r s i s t e n t 或其派生类继承下来的,这样

对象的公开属性才能够被流操作并显示在Object Inspector 中。

要在T d d g W o r t h l e s s 组件中加入对象型属性,必须首先声明该对象。

清单2 1-4列出了该对象。清单21-4 TSomeObject

T S o m e O b j e c t 是直接从T P e r s i s t e n t 继承下来的,其实不一定必须是这样的。只要一个对象是从T P e r s i s t e n t 或其派生类继承下来的,它就可以作为一个属性的类型。

这个对象本身还包括两个属性:P r o p 1和P r o p 2,它们都是简单型的属性。另外,该对象还有一个方法A s s i g n (),这个方法将在后面讲述。

现在可以在T d d g W o r t h l e s s 组件中加入一个T S o m e O b j e c t 类型的字段。不过,因为该属性是一个对象,所以必须创建该对象的实例。否则,当用户把T d d g W o r t h l e s s 组件放到窗体上时,就无法编辑这个属性。因此,必须覆盖T d d g W o r t h l e s s 组件的C r e a t e ()来创建T S o m e O b j e c t 的实例。清单2 1-5列出了T d d g W o r t h l e s s 组件的对象型属性。

清单21-5 加入对象型属性

图21-3 显示集合型的属性

请注意这里覆盖了C r e a t e ()和D e s t r o y ()。此外,还应注意这里用了一个写访问方法S e t S o m e O b j e c t ()来设置S o m e O b j e c t 属性的值。所谓写访问方法也可称之为写方法。相应地,读访问方法也称为读方法。回顾一下第2 0章,写方法需要传递一个与属性同类型的参数。通常约定,写方法的名称以S e t 打头。T d d g W o r t h l e s s .C r e a t e ()声明如下:

这里,我们首先调用继承的C r e a t e (),然后创建T S o m e O b j e c t 的实例。因为当用户把组件放到窗口上和应用程序运行时,都要调用C r e a t e (),这样可以保证F S o m e O b j e c t 是合法的。

另外,必须覆盖D e s t r o y (),这用来在释放T d d g W o r t h l e s s 组件时释放T S o m e O b j e c t 的实例,代码如下:

现在,假设执行下列代码,会发生什么事情:

如果T d d g W o r t h l e s s .S o m e O b j e c t 属性不是用下面的写方法声明的,那么当用户把一个对象赋给S o m e O b j e c t 属性时,F S o m e O b j e c t 字段所引用的对象实例就会丢失。

回顾一下第2章“Object Pascal 语言”,对象实例实际上是一个指针。当把一个对象赋给变量时,其实就是当前实例的指针指向另一个对象,而原来的对象仍然在内存中。设计组件时,应当避免对用户访问属性设置前提条件。这样,最好给属性加上写方法。写方法可以确保当用户给属性赋值时,不会有资源丢失。S o m e O b j e c t 的写方法是这样定义的:

S e t S o m e O b j e c t ()方法调用了F S o m e O b j e c t .A s s i g n (),并传递了新对象T S o m e O b j e c t 。T S o m e O b j e c t .

A s s i g n 是这样定义的:

在T S o m e O b j e c t .A s s i g n ()里,首先检查用户传递的T S o m e O b j e c t 对象实例是否合法。如果是的话,就会逐个复制S o u r c e (T S o m e O b j e c t 对象)的属性。这里阐述了对象相互复制的另一种技术,在V C L 中经6 1 2第三部分基于组件的开发下载

常用到。如果你有V C L 的源代码,就可以查看一下A s s i g n ()方法如T B r u s h 和T S h a p e ,看看它们是怎样实现的。

警告在属性的写方法中不要对属性赋值。例如,参阅下面的声明:由于允许访问属性本身(不是内部的存储字段),写S o m e

P

r o p 属性调用S e t S o m e P r o p (),而S e t S o m e -P r o p ()又去写S o m e P r o p 属性,导致无限循环。最后,应用程序因为堆栈溢出而崩溃。

(5) 加入数组型的属性

有些属性要像访问数组一样来访问它们,也就是说,属性本身包含了若干项,这些项都有相应的索引值,这些项可以是对象类型。譬如典型的数组型属性有T S c r e e n .F o n t s 、T M e m o .L i n e s 及T D B G r i d .

C o l u m n s 。编辑这些属性需要有专门的编辑器。下一章将介绍怎样创建属性编辑器。在这里我们就不详细讨论数组型属性。现在,我们介绍怎样加入一个数组型的属性。

现在我们先把T d d g W o r t h l e s s 组件放在一边,看看T d d g P l a n e t s 组件。这个组件包括两个属性:P l a n e t N a m e 和P l a n e t P o s i t i o n 。P l a n e t N a m e 属性就是数组型的,它的下标是一个整数,内容代表的是各个行星的名称。P l a n e t P o s i t i o n 属性也是数组型的,但它的下标不是整数,而是一个字符串。如果给出的字符串正好是行星的名称,P l a n e t P o s i t i o n 返回的结果就是行星在太阳系的位置。

例如,下面的代码通过使用T d d g P l a n e t s .P l a n e t N a m e 属性来显示字符串“N e p t u n e ”:

再看下面这条语句:

在列出T d d g P l a n e t s 组件的代码之前,先把数组型属性不同于其他类型的特征介绍一下:

? 数组型属性要带下标。下标必须是简单类型,例如整数、字符串,但不能是记录或类。

? 数组型属性必须带读、写访问方法,但不能是组件的字段。

? 如果数组型属性是多重数组,那么访问方法必须有相应个数的参数。

现在,清单2 1-6列出了T d d g P l a n e t s 组件的代码。

清单21-6 演示数组型属性

// 这将导致无限循环

6 14第三部分基于组件的开发

上面代码演示了两种数组型属性,一种是以整数为下标,另一种是以字符串为下标。要注意的是这些属性的值是由读方法返回的,而不是内部字段返回的。你可以参阅代码的注释部分。

(6) 默认值

要使一个属性有默认值,可以在组件的C r e a t e()中对属性赋值。在C r e a t e()中加入下列代码,当这个组件被加到窗体上时,F I n t e g e r P r o p属性的值就被默认为1 00:

现在解释一下D e f a u l t和N o D e f a u l t指示字。如果查看V C L的源代码,就发现有的属性是用D e f a u l t指示字声明的。譬如T C o m p o n e n t.F T a g属性:

不要以为这个属性的默认值为0,属性的默认值是在组件的C r

e a t e ()中设定的。再看下面的属性:这也并不意味着I n t e g e r P r o p 属性的默认值是1 00。这个值只是影响当保存包含T d d g W o r t h l e s s 组件的窗体时是否要保存这个属性的值。如果i n t e g e r P r o p 属性的值不是1 00,就会把这个属性的值保存到

D F M 文件中。否则,就不保存这个值(因为1 00是最新创建的属性值,这个值优先于从流中读出的属性)。建议尽量使用D e f a u l t 指示字来声明属性,因为这样能够加快打开窗口的速度。该注意的是,D e f a u l t 指示字并没有设置属性的默认值。要设置属性的默认值,必须在组件的C r e a t e ()中进行,正如前面所示。

N o D e f a u l t 指示字用来再次声明指定默认值的属性,不管属性值是多少,都将保存属性的值。例如,可以不指定默认值来声明Ta g 属性:

注意,一般不用N o D e f a u l t 指示字,除非有特殊原因。例如,T F o r m .P i x e l s P e r I n c h 属性必须被保存,以便在运行时缩放正确工作。另外,字符串类型、浮点类型及i n t 64类型属性不必声明默认值。

改变一个属性的默认值,必须重新声明新默认值(但不是读或者写方法)。

(7) 默认数组型属性

可以声明一个数组属性使之成为一个组件的默认属性。这样,使用对象实例就像使用数组变量一样。例如,T d d g P l a n e t s 组件中声明了一个P l a n e t N a m e 属性,后面用了关键字d e f a u l t 。这样做,T d d g P l a n e t s 组件的默认属性就是P l a n e t N a m e 。紧跟在对象标识后的索引必须是简单型的。因此,下面两行代码的结果是一样的:

一个对象只能有一个默认的数组型属性,并且不能在派生类中覆盖。

21.1.6 加入事件

在第2 0章里,我们曾经提到事件,它是一种特殊的属性,当某个动作发生时,就会执行预先指定的代码。在本章节里,我们将更详细地讨论事件,讨论事件是怎样发生的,并且怎样在自定义组件中加入事件。

1. 事件是从那里来的

事件有可能产生于用户交互操作、操作系统、程序代码。事件的机制就是事件与相应的代码相联系,当事件发生时,就会调用相应的代码。事件与代码的链接称为事件属性,以方法指针的形式提供。而这样的代码就是处理事件的方法,称之为事件处理过程(event handler)。

例如,当用户单击鼠标,一个W M _M O U S E D O W N 消息就会被发送到Wi n 32系统。Wi n 32系统就会把这个消息传递给预定义的控件,该控件就会响应该消息。该控件首先检查是否有相应的执行代码。如果有的话,就执行这段代码,即事件处理过程。

O n C l i c k 事件是D e l p h i 的一种标准事件属性。O n C l i c k 事件或者其他事件都有一个相应的事件调度方法。事件调度方法通常是在组件的p r o t e c t e d 部分声明,由它来检查事件属性是否指向了用户自定义组件提供的代码。对于O n C l i c k 事件来说,它的调度方法就是C l i c k ()方法。O n C l i c k 事件和C l i c k ()方法都是在T C o n t r o l 中声明的:

第2 1章编写自定义组件 6 1下载

T C o n

t r

o l

.C l i c k ()方法如下:

你必须明白的是,事件属性实际上就是一方法指针。请注意 F O n C l i c k 的类型就是T N o t i f y E v e n t 。T N o t i f y E v e n t 是这样声明的:

这表明,T N o t i f y E v e n t 是一个过程,只带一个S e n d e r 参数,类型是TO b j e c t 。指示字of object ,表示该过程是一个方法,这意味着一个隐含参数S e l f 会被传递给调用过程,这个参数代表方法实际所在的对象。当组件的C l i c k ()方法被调用时,它就会检查F O n C l i c k 是否指向了T N o t i f y E v e n t 类型的方法。作为一个组件编写者,需要编写事件声明、事件属性及事件调度方法的所有代码。组件的使用者只需建立事件处理过程。事件调度方法检查是否存在处理事件的代码,存在,就执行它。第2 0章里。我们讨论了怎样在设计期和运行期建立事件处理过程。下面的章节里,将介绍怎样创建自定义事件、事件属性、调度方法。

2. 声明事件属性

在声明一个事件属性之前,要确定是否需要特殊类型的事件。对于 D e l p h i 的V C L 中已有的事件,最好先熟悉它们。大多数情况下,自定义的组件往往是个已有的组件派生出来的,也就是自定义组件继承了一些事件。除非已有的事件不能满足用户要求,这时,才有必要定义自己的事件。例如,假想一个组件要想每半分钟触发一次事件。换句话说,事件一分钟内被触发两次。可以用T T i m e r 组件来定时操作。不过,假如专门定义一个O n H a l f M i n u t e 事件的话,这就可以不必用T T i m e r 组件,只需调用O n H a l f M i n u t e 事件就可以了。

清单2 1-7列出了T d d g H a l f M i n u t e 组件的代码,演示了怎样设计一个自定义组件,更为重要的是,还演示了怎样创建自己的事件。

清单21-7 创建T d d g H a l f M i n u t e 事件

6 1 6第三部分基于组件的开发

第2 1章编写自定义组件 6 1

下载

当创建事件时,必须考虑事件处理过程需要那些参数。例如,创建T E d i t 的O n K e y P r e s s 事件就是这

样的:从这个事件处理过程的参数中,不但可以知道哪个事件触发了这个事件,而且还知道用户按的是什么键( K

e y 参数)。要是深入到V C L 中的话,你就会知道,这个事件的发生实际上就是Win32 的W M _C H A R 消息引起的。D e l p h i 会把必要的信息以参数的形式传递给组件的使用者,于是,组件的使用者就会明白怎样做。

请注意,E d i t 1K e y P r e s s ()方法的K e y 是用v a r 声明的。你可能在想,为什么不把这个方法设计为函数呢?这是因为,如果把事件处理过程设计为函数会引起不确定性。当把一个函数类型的方法指针赋值给一个事件属性时,被赋值的是函数指针还是函数的返回值呢?这就是二意性。顺便说一下,在Delphi 1的时候,V C L 中只有一个事件的处理过程是函数。这个事件就是TA p p l i c a t i o n .O n H e l p 。清单2 1-7中,有这样一行代码:

T T i m e E v e n t 就是处理O n H a l f M i n u t e 事件处理过程的类型。这儿传递了两个参数,一个是触发该事件的对象,另一个是事件发生的时间。

F O n H a l f M i n u t e 字段是属于用户的事件处理过程的,在设计时,可以通过O n H a l f M i n u t e 属性在Object Inspector 中修改。

组件用了一个T Ti m e r 对象,每半秒钟检测一次秒值。如果秒值为0或3 0时,就会调用D o H a l f M i n u t e ()方法,检查事件处理过程是否存在,存在则调用它。你可以参阅代码的注释部分。

当把这个组件安装到D e l p h i 的组件面板上后,就可以把组件放到窗体上,并可以将下面的事件处理过程加到O n H a l f M i n u t e 事件中:

这里将讨论怎样将声明的事件类型转换为事件处理过程。

6 1 8第三部分基于组件的开发

21.1.7 创建自定义的方法把方法加到组件中与把方法加到别的对象中没有什么区别。不过,当设计组件时,需要考虑下列规则。

1. 保持相互独立

创建一个自定义组件,一个关键的问题是使最终用户使用起来方便。因此,应当尽可能避免方法之间的相互依赖。例如,不能强迫用户在使用组件时必须调用某个方法,也不能使一个方法的调用与另一个方法的调用顺序有关。另外,一个方法调用后不能使组件的其他方法和事件无法使用。最后,应当给方法取一个有意义的名字,使用户从名称里就可以猜测到该方法大致是干什么的。

2. 方法的可见性

设计组件时,必须确定方法是在p r i v a t e 、p u b l i c 还是在p r o t e c t e d 部分声明的。不仅要考虑到使用该组件的最终用户,还要考虑到由该组件派生出另一个组件的用户。可以从表2 1-2中获得关键字p r i v a t e 、p r o t e c t e d 、p u b l i c 和p u b l i s h e d 的区别。

表21-2 Private 、P r o t e c t e d 、P u b l i c 或P u b b l i s h e d

关键字

描述p r i v a t e

在这部分声明的变量和方法对于派生类来说是不能访问的。通过应为私有实例变量提供读/写方法。但不应提供对属性实现方法的访问p r o t e c t e d

在这部分声明的变量、方法及属性允许派生类访问,但不允许组件的使用者访问p u b l i c

允许组件的使用者访问其方法和属性,并且只在运行时而不是在设计时访问,这些成员必须在p u b l i c 部分声明p u b l i s h e d 在这部分声明的属性可以出现在Object Inspector 中。这部分的属性会有RT T I 产生21.1.8 构造器和析构器

当要创建一个新组件时,可以覆盖祖先类的构造器来定义自己的构造器。这里,有一些需要注意的地方。

1. 覆盖构造器

当声明一个T C o m p o n e n t 派生类的构造器时,务必要使用o v e r r i d e 指示字。例如:

注意T C o m p o n e n t 的C r e a t e ()本身是虚拟的。如果一个类是从T C o m p o n e n t 的上级即TO b j e c t 或T P e r s i s t a n t 继承下来的,不是虚拟的,那么它的构造就不能覆盖。例如下面这个例子,就不能覆盖:

T M y O b j e c t =c l a s s (T P e r s i s t a n t )

在这个实例中,只需重新声明该构造器。

尽管不加o v e r r i d e 指示字在语法上是合法的,但是组件被使用时,可能会引起麻烦。这是因为当使用该组件时(设计期和运行期),无法通过这个类来引用这个非虚拟的构造。

覆盖祖先类的构造器时,务必调用祖先类的构造器,如下代码:

// 在这里加进代码。

2. 设计期行为

记住,构造器是在创建组件时被调用的,包括用户在设计期把组件放到窗体上时。设计组件时,应当避免对组件进行任何操作。例如,对于T d d g H a l f M i n u t e组件来说,在它的构造器中创建T T i m e r组件,尽管这并不难实现,但要防止在设计期这样做。

你可以检查组件的C o m p o n e n t S t a t e属性来判断组件的状态。表2 1-3列出了组件的各种状态,并且在Delphi 5的在线帮助里有其详细说明。

表21-3 组件的状态

标志组件的状态

c s A n c e s t o r组件在祖先的窗体引入

csDesigning 处于设计状态,即组件在窗体上,并被窗体设计器操作

c s D e s t r o y i n g组件将被删除

c s F i x u p s组件被链接到一个还没有打开的窗体上,链接完毕,清除标志

c s L o a

d i n g从文件对象中调入内存

c s R e a

d i n g从一个流中读它的属性值

c s U p

d a t i n g组件随着祖先窗体的改变而更新

c s W r i t i n g把一个属性值写到一个流中

经常用到的c s D e s i g n i n g状态表示组件正处于设计状态。请看下面的代码:

{ 加入代码}

你应该注意到了,在调用inherited Create(AOwner)这个构造器之后,组件的状态才处于c s D e s i-

g n i n g。在I D E的窗口设计器中经常发生这种情况。

3. 覆盖析构器

按照一般规则,如果要覆盖析构器,必须确保在所有的资源被释放以后,调用祖先类的析构器。请看下列代码:

提示这是一个重要规则。覆盖构造器时,通常是首先调用inherited Create();覆盖析构器时,往往是最后调用inherited Destroy。这样可以确保在修改它之前类已经建立,在释放一个类时所有资源已被清除。当然,规则也有例外的时候,不过,尽量不要违背它。

21.1.9 注册组件

所谓注册组件,就是让D e l p h i知道把哪个组件放到组件面板上。如果你是使用组件专家( C o m p o n e n t E x p e r t)来创建组件的,那就不必考虑注册的问题,因为D e l p h i已经自动生成了相关代码。不过,如果组

件是手工创建的,就应把R e g i s t e r()过程加到组件单元中。

必须要做的就是把R e g i s t e r()过程加到组件单元的I n t e r f a c e部分。

在D e l p h i的R e g i s t e r()过程中,每注册一个组件,就调用一次R e g i s t e r C o m p o n e n t s()过程。R e g i s t e r C o m p o n e n t s()过程有两个参数:一个是指定要把组件加到组件面板上的哪一页,一个是指定组件的类型。清单2 1-8演示了怎样注册组件。

清单21-8 注册组件

上面代码注册了两个组件T M y C o m p和TO t h e r C o m p,并把它们放到组件面板的D D G页上。

组件面板

在Delphi 1和2中,组件库就是一个库文件,集中了所有的组件、图标、编辑器。尽管有时处理单个文件比较方便,但是如果多个组件都放在该文件里,就会显得相当笨拙。另外,当需要向组件库中加入新组件时,要等待很长时间重新组建该库文件。

Delphi 3引入了包的概念后,就可以把组件分别放到几个设计期包中。尽管处理多个文件时稍微复杂些,但是使配置更为方便简洁,加入一个新组件时只需要重新组建一个包,而不是整个组件库。

默认情况下,新组件都是被加到一个叫作 D C L U S R50的包中,但是可以使用F i l e|N e w| P a c k a g e菜单命令创建和安装新的设计期包。本书附带的光盘中有一个设计期包叫D d g D s g n50.

dpk,运行期包叫Ddgstd50.dpk。另外还包含了本书中创建的组件。

如果在设计期提供的不仅仅是R e g i s t e r C o m p o n e n t s()(如属性编辑器、组件编辑器、专家注册)的调用,可以将R e g i s t e r()过程从组件中分离出来。这是因为如果紧缩单元被编译成运行期包,紧缩单元的R e g i s t e r过程赋给设计期I D E包的类或过程,这时运行期包不可用。设计期包与运行期包应当分离。

21.1.10 测试组件

这是令人非常兴奋的一件事,终于完成了一个组件的编写,现在进入测试阶段。该注意的是,只有在充分调试后,才可以将该组件加到组件面板上。最好用一个动态的程序实例来测试该组件,这是因为在设计期,组件是在I D E窗体中。如果编写的组件含有缺陷,它可能会破坏内存和I D E。清单2 1-9用来测试T d d g E x t e n d e d M e m o组件,在本书附带的C D中能够找到Te s t E M e m.d p r。

清单21-9 测试T d d g E x t e n d e d M e m o组件

请记住,在设计期测试组件,并不能确保组件没问题。有些设计期行为会使Delphi IDE 出现混乱,譬如,忘了调用inherited Create() 构造器。

注意在设计环境下,创建并设置好组件,还不能确保它可以用。只有当Create()构造器执行后,该组件才能确定可用。因此,L o a d e d ()方法不能当做是组件构造部分。当组件从流中调入时,L o a d e d ()方法才被调用。L o a d e d ()标志流的末端,如果组件是以流的方式创建的,L o a d e d ()方法才被调用。

21.1.11 提供组件图标

如果自定义组件没有图标,那么该组件是不完整的。要创建这样的图标,可以使用D e l p h i 的I m a g e E

d i t o r (或者是其他的位图编辑器)来创建24×24的位图,并且这样的位图必须保存到D C R 文件。D C R 文件与R E S 文件一样,都是资源文件。所以,如果图标保存在R E S 文件,那么只要将其扩展名改为D C R 即可。

提示即使你的显示设备是2 56色或者更多颜色的,但是如果你要把该组件发布出去,最好不要超过16色,因为一个256色的位图在16色的机器上显示效果非常糟糕。

创建了一个位图后,必须给这个位图命名。位图的名称要跟组件的类名相同,而且要大写。 D C R 文件的名称与组件的单元名称相同。因此,如果组件名称为T X Y Z C o m p o n e n t ,那么位图的名称为T X Y Z C O M P O N E N T ,如果组件单元的名称为X Y Z C O M P . P A S ,那么D C R 文件的名称就是X Y Z C O M P .

D C R 。该位图文件必须与组件的单元文件位于同一个目录,编译这个单元时,位图资源会自动加到组件库中。

21.2 一个组件的示例

下面将创建一个组件。这个组件有两个主要用途。首先是演示了前一章节所将的技术,其次,可第2 1章编写自定义组件 6 下载

以将该组件应用到实际程序中,也可以根据需要扩展它的功能。

21.2.1 扩展Win32组件外套功能

有些情况下,可能需要扩展已有的组件的功能,尤其是那些Wi n32组件类的外套组件。下面通过创建T M e m o和T L i s t B o x两个组件来说明怎样扩展其功能。

1. TddgExtendMemo:TMemo组件的扩展

尽管T M e m o组件功能相当不错,但是有一些功能仍需改进。譬如,它就没有提供插入符所在的行、列的信息功能。下面我们就来扩展该功能。

另外,在实际操作中,让用户用滚动条来操作是很方便的。下面又加入了一些事件来处理滚动操作。

清单2 1-10列出了T d d g E x t e n d e d M e m o组件的源代码。

清单21-10 ExtMemo.pas:TddgExtendedMemo组件的源代码

第2 1章编写自定义组件下载

6 26第三部分基于组件的开发

首先,T d d g E x t e n d e d M e m o组件加入了行、列信息。请注意,在该组件中加入了两个私有的字段:F R o w和F C o l u m n,这两个字段用于记录插入符的当前行、列位置信息。请注意,这里在P u b l i c部分声明了两个属性:R o w和C o l u m n。在P u b l i c部分声明是因为在设计期这两个属性没有什么用处。这两个属性都有读方法和写方法。对于R o w属性,其访问方法是G e t R o w()和S e t R o w(),C o l u m n属性的访问方法是G e t C o l u m n()和S e t C o l u m n()。对于所有的实际应用,可能会去掉存储字段F R o w和F C o l u m n,因为通过访问方法,可以给R o w和C o l u m n赋值。不过,我们现在保留这两个字段,因为它们有助于组件的扩展。

这四种访问方法都利用了E M_X X X X消息。在上面的代码的注释部分,已经解释了怎样通过这些消息来获取行、列信息。

T d d g E x t e n d e d M e m o组件有两个新事件:O n H S c r o l l和O n V S c r o l l。当用户单击水平滚动条时会触发O n H S c r o l l事件,同样,单击垂直滚动条时就会触发O n V S c r o l l事件。要触发这两个事件,该组件分别捕捉Wi n 3 2的W M _ H S C R O L L消息和W M _ V S C R O L L消息。这时需要创建两个消息处理过程:W M H S c r o l l()和W M V S c r o l l()。这两个消息处理过程分别调用了事件调度方法H S c r o l l()和V S c r o l l()。这两个事件调度方法检查组件的使用者是否提供了处理O n H s c r o l l事件和O n V s c r o l l事件的处理过程。如果提供了,就调用该处理过程。你也许想问,为什么不直接在消息处理过程中进行这样的检查呢?这是因为,很多情况下需要根据实际情况来决定是否调用事件处理过程。

在应用程序中,可以安装和使用T d d g E x t e n e d M e m o组件。也许需要扩展该组件;譬如,用户要改变插入符的位置,就会发送W M_C O M M A N D消息给组件的父事件发生时,H i W o r d(w P a r a m)携带代码标志信息。E N_C H A N G E的值代表编辑标志信息的改变。这样就可以在父窗口中捕获信息,然后自动地更新F R o w和F C o l u m n字段。

2. TddgTa b b e d L i s t B o x:T L i s t B o x组件的扩展

V C L的T L i s t B o x组件其实是Win32 API LISTBOX控件的Object Pascal外套。尽管它封装了T L i s t B o x组件的大部分功能,但还有一些需要改进的地方。本章节将介绍怎样一步一步扩展T L i s t b o x 组件。

(1) 思想

就像在大多数情况下一样,扩展该组件是出于某些需要。Win32 API提供的列表框允许Ta b键停,并且字符串的长度超出了了列表框的宽度,就会自动出现滚动条,而T L i s t b o x不支持这些功能。T d d g-Ta b L i s t b o x是扩展其功能后的组件。

其实,T d d g T a b L i s t b o x组件并不复杂,只要以T L i s t b o x为祖先类派生出一个新的组件,然后覆盖某些方法,并加入一些新方法,就可以达到目的。

(2) 代码

首先,创建一个列表框的窗口,它支持Ta b键停和水平滚动。这两个功能有两个标志,分别是

l b s_U s e T a b S t o p s和w s_H S c r o l l。要在T W i n C o n t r o l派生类中添加窗口风格,需要覆盖C r e a t e P a r a m s()方法,如下代码所示:

delphi制作多文档界面

最为一个巩固之前有关窗体和对象的有关知识,下面就建立一个简单的MDI示范程序,这个程序的功能是打开和保存图形文件(包括位图、图标等),为了完成这个任务,我们有一个大概的计划,计划内容如下: (1)建立主窗体(一个MDI父窗体),包括菜单。 (2)为【File | Open…】和【File | Save…】菜单选项编写代码。 (3)为Windows菜单上的Cascade、Tile和Arrange All选项编写代码。 (4)建立MDI子窗体。 (5)建立一个About对话框。 (6)然后再回忆和欣赏一下这段工作。 时间就是金钱,即刻就做吧。 一、创建主窗口窗体 首先创建一个主窗口窗体,一个MDI应用程序的主窗口的FormStyle属性必须设置为fsMDIForm。不但要为应用程序增加File Open和File Save 对话框,还要增加一个菜单。 1、启动Delphi,并从主菜单选择【File | New | Application】; 2、把主窗体的Name属性设置为MainForm; 3、把Caption属性设置为Picture Viewer; 4、把FormStyle属性设置为fsMDIForm; 好了,下面为此窗体增加一个菜单,利用Delphi特性,引进一个预定义菜单,具体如下:1、点击组件选项板的Standard标签,并点击MainMenu按钮;

2、把MainMenu组件点击放置到窗体上,具体放到哪个地方无所谓了,因为在运行阶段,代表菜单的图标只是占地方而不显示,这是非可视化组件。 3、把MainMenu组件Name属性改为MainMenu; 4、双击MainMenu组件,就会出现Menu Designer对话框; 5、在MainMenu上点击鼠标右键,选择“Insert From Template…”,将出现Insert Template对话框;

delphi制作登陆界面

///////////////////// (一)项目文件 test.dpr ////////////////////// program SerialGet; uses Forms, UMain in UMain.pas {frmMain}, ULogin in ULogin.pas {frmLogin}, UDataModule in UDataModule.pas {DataModule1: TDataModule}, {$R *.res} begin Application.Initialize; if CreateMutex then //创建句柄,判断此应用程序是否在运行 begin //调用全局函数,创建并显示登陆界面 if doLogin then //登陆成功 begin Application.CreateForm(TfrmMain, frmMain); //数据模块文件不须在这儿创建,因为 ULogin.pas 中已创建 //Application.CreateForm(TDataModule1, DataModule1); Application.Run; end else //登陆不成功 begin try DataModule1.free; Application.terminate; except end; end; end else begin DestroyMutex; //释放句柄 end; end. //////////////// (二)登陆窗体 ULogin.pas ULogin.dfm ////////////////// unit ULogin; interface uses ...... type ... ... ...

用delphi设计图书管理系统

《数据库技术与应用》 大作业 题目:数据库应用系统的设计和实现 适用专业: 指导老师: 班级: 姓名: 学号: 中南大学信息科学与工程学院 2010 年1月

图书是人类文明的见证,随着高科技的发展各式各样图书越来越多,图书馆和书店的管理也越来越复杂。如今图书管理系统是一 个书店或一个图书馆的必备系统。一个安全可靠并且对于用户比较实用的图书管理系统来说变得尤其重要。 本次设计设计了一个在DELPHI平台基于SQL Server 2000开发出的图书管理系统。在系统的设计上面实现了该系统的交互式界面。 从系统的需求分析、系统设计、系统实现入手,详细阐述了一个 C/S 结构的图书管理信息系统主要功能的实现过程。每一阶段均给 出了相应的理论依据和实现内容,并介绍了系统实现部分的主要算法。整个系统的设计与开发严格按照软件工程思想进行,从需求分析到系统设计和实现、从原型系统设计到迭代完善,本图书管理系统涵盖了六个主要的子系统:图书资料管理、读者资料管理、借书 操作、还书操作、删除、修改和查询处理,提高了工作的准确率和效率。 本图书管理信息系统是利用计算机管理信息处理的迅速、准确、可靠且具有强大存储能力的突出特点,全面提高图书馆的管理水平 和工作效率,并以及时、完整的业务经营资料,为图书馆的及时转换提供一定的支持。

关键词:数据库,SQL server2000,Delphi 7,图书管理系统 1.1 课题研究现状分析 在现金信息化发达的社会,图书的发行量与日俱增。因此需要对书籍资源、读者资源、借书信息、还书信息进行管理,及时了解各个环节中信息的变更,有利用管理效率的提高。传统的图书馆需要制作有署名与对应编号的书卡,由读者按分类查找再交由图书管理员来处理借书工作,还书也要人工翻阅大量的记录,这样使得工作量大并及其繁琐,效率变得低下。建立网上电子图书管理系统的目的是实现无纸化图书管理,减轻管理员的工作量,方便读者查阅所需的图书,还可以利用网络实现提醒读者还书的功能。管理员还可以对各种图书进行分析统计,对过时、损坏的图书进行删除。达到降低成本提高工作效率的目标。 1.1.1本领域内已开展的研究工作 1. 理论研究基础 (1)数据库设计方法的完整化、多样化,又有多学科的综合性。 (2)数据库设计步骤的规范化和细致化。 (3)数据库技术不断的更新,并不断增加新的技术,兼容性范围也逐渐扩大。2. 技术层面的支持 SQL Server的普遍应用,并得到的用户的支持,与Windows操作系统的完全兼容性也决定了它在今天仍然有着庞大的使用群体。SQL Server2000作为后台数据库与前台开发软件Visual Basic、C#、Visual C++、Delphi等都能够无缝连接。 1.1.2已经取得的研究成果 (1)开发出了一套系统的数据库理论,安全性、完整性、并发性、恢复性都有完整的概述。 (2)随着Internet的迅猛发展,数据库在各个领域作为后台存储得到了广泛的 支持。 (3)完善的数据库管理并与前台开发软件的结合使用开发了一系列优秀的图书管理系统,在商业、经济等方面得到了广泛的应用。

delphi可视化编程讲稿

第三章D e l p h i的窗体与组件 教学要求: 掌握窗体、编辑类组件、按钮类控件、列表类控件、分组类组件、时钟组件、菜单组件、工具栏组件和工具按钮、多选项卡的的属性、方法与事件,学会使用这些组件设计应用程序的界面。 掌握ShowMessage、ShowMessageFmt、MassageDlg、MassageDlgPos、InputBox、InputQuery对话框函数与对话框过程的特点,并能熟练应用。熟悉公共对话框控件组件的属性,方法、事件及应用。 掌握创建新窗体的两种不同的方法及其应用。 掌握windows的模式与非模式窗体的概念及应用方法。 掌握焦点的含义,与焦点相关的属性,应用焦点设计程序的方法。 了解CreateMessageDialog函数的定义与用法、speedbutton和文件有关的控件的属性、方法与事件。

从本章开始,将介绍Delphi 编程的一些基本方法。通过这一部分的学习,可以了解开发Windows 环境下应用程序的一些关键技术,并可以方便、快捷地开发一般的应用程序。 为了简化编程工作,Delphi 提供了许多功能强大的函数和类。其中很重要的一部分就是组件类。本章将重点介绍常见组件及其编程应用。 现在深入普及的Windows操作系统中,方便快捷的人机交互界面主要是通过窗体和对话框实现的。在Delphi中,这些窗体和对话框就是程序设计阶段的窗体,Delphi的可视化设计工作就是在窗体中进行的。 通常,窗体中会有文字输入、滚动条、复选框、单选框、按钮等一系列组件,通过这些直观易懂的组件,可以方便的实现多种多样的功能。在Delphi中,这些运行期间出现在窗体和对话框中的组件称为可视组件。在窗体中,还可以放置一些运行期间非可视的组件,对应其它的TComponent子类。这些不可视组件集中地实现了一些特殊的功能。比如,时钟控制等功能。 ●TComponent组件类,所有的组件都是由这个类派生来的。 ●TControl子类(可视组件)可分为两类:窗体组件和图 形组件。

delphi的精美界面设计

Delphi界面设计专辑 [前言:]界面的美观和用户亲和性是应用软件成功的首要条件,因此界面往往是程序员最费心的地方。在这个专辑中,将向读者全面介绍Delphi中界面设计的原则和技巧 窗体设计 制作固定大小的Form 固定的Form像一个对话框,何不试试下面的语句 巧用Delphi制作溅射屏幕 精心编写的WINDOWS程序显示启动注意事项,称之为溅射屏幕(splash screen)。利用一点儿小小的内容,即可给程序的显示添加不少色彩 LED数码管仿真显示程序 在电子设备上广泛地使用LED数码管显示数据,在许多应用软件中也经常模拟LED数码管显示数据,使程序画面看起来很有特色 菜单设计 DELPHI中自适应表单的实现 我们知道,屏幕分辨率的设置影响着表单布局,假设你的机器上屏幕分辨率是800*600,而最终要分发应用的机器分辨率为640*480,或1024*768,这样你原先设计的表单在新机器上势必会走样 作非常规程序菜单掌握delphi高级秘籍 大家可能见过诸如金山毒霸,瑞星杀毒,以及五笔输入法等等在系统托盘(即右下角有时间和输入法图标的地方)在的控制菜单,而在正常的任务栏(即屏幕最下方的“开始”按钮的右边的各式各样)中却不出现按钮的程序,即我们常说的在后台运行的程序 用Delphi制作动态菜单 所谓动态菜单是指菜单项随着程序的操作变化而变化。现在,我们用Delphi来实现这一功能,具体步骤如下 工具栏和状态条 为Windows窗口标题栏添加新按钮 对于我们熟悉的标准windows窗口来讲,标题栏上一般包含有3个按钮,即最大化按钮,最小化按钮和关闭按钮。你想不想在Windows的窗口标题栏上添加一个新的自定义按钮用Delphi4实现风Word97格的工具栏 用过Word97的人对它的工具栏印象很深刻,因为它的风格很“酷”,同样IE4.0的工具栏也有类似的风格,Win98的出现,使这种风格的工具栏得到了推广 如何隐藏和显示Windows的任务条 如果隐藏和显示Windows的任务条?仅仅调用以下的函数就可以. 其他技巧 Delphi利用Windows GDI实现文字倾斜 在Delphi开发环境中,文字的输出效果一般都是头上脚下的"正统"字符,如何输出带有一定倾斜角度的文字以达到特殊的显示效果呢 Delphi之三十六计之界面篇 设置状态栏面板对象的Style为OwnerDraw,并在状态栏对象的DrawPanel事件中书写以下代码 利用COM技术实现外壳扩展的属性页 当用户在资源管理器中调用右键菜单时,会显示一个"属性"菜单项,点击属性菜单项会显

Delphi用BusinessSkinForm 做界面皮肤的使用说明

Delphi用BusinessSkinForm 做界面皮肤的使用说明 注意:新版的Delphi 已经用bsCompressedStoredSkin 代替了bsStoredSkin 1、先放bsBusinessSkinForm、bsSkinData、bsStoredSkin(bsCompressedStoredSkin)各一个到窗体上 2、修改bsBusinessSkinForm的SkinData属性为bsSkinData1 3、修改bsSkinData1的StoredSkin属性为bsStoredSkin1 4、修改bsStoredSkin1的filename属性,指向一个皮肤文件,例如我的55套皮肤包是放在C:\Program Files\Borland\Delphi7\BSF-Skins\Skins文件夹里,我就修改 bsStoredSkin1的filename属性为C:\Program Files\Borland\Delphi7\BSF-Skins\Skins\Animate\skin.ini 5、在delphi设计窗口中按下F9 6、爽吧!!!!! 第一步-安装: 1、双击BSFD7.dpk文件,在delphi 7.0中打开 2、单击compile按钮,你会发现原来不可用的install按钮可以使用了 3、单击install按钮,会弹出来一个对话框提示你“安装成功!” 4、将所有源文件复制到C:\Program Files\Borland\Delphi7\Lib文件夹中(如delphi安装路径不同的话,请各位老兄自行更改) 第二步-最基本的使用方法: 当你安装成功后,你会在delphi的组件面板上发现三个控件组,它们是: businessskinform vcl和businessskinform db vcl和businessskinform vcl dialogs,顾名思义,第一个是常用组件,第二个是数据库组件,第三个是对话框组件。 按照下面的步骤,不用写一行代码,你可以马上做出一个有漂亮界面的程序来 1、先放bsBusinessSkinForm、bsSkinData、bsStoredSkin(在businessskinform vcl控件组中)各一个到窗体上 2、修改bsBusinessSkinForm的SkinData属性为bsSkinData1 3、修改bsSkinData1的StoredSkin属性为bsStoredSkin1

Delphi XE FireMonkey 自带Demo体验与效果图

Delphi XE FireMonkey Demo体验与效果图 版本:Delphi XE6 FireMonkey DEMO:delphi FireMonkey自带 DEMO路径,在DELPHI XE6安装目录下:E:\Documents and Settings\All Users\Documents\Embarcadero\Studio\14.0\Samples 1●TabSlideTransition(分页可滑动翻页) 功能:每页可填写,并支持可滑动翻页 路径:Samples\Object Pascal\Mobile Samples\User Interface\TabSlideTransition 开启界面:

2●TabletMasterDetailWithSearch(查找联系人,定位,并显示明细) 功能:可以查找列表联系人,并定位,和显示明细 路径:Samples\Object Pascal\Mobile Samples\User Interface\TabletMasterDetailWithSearch 开启界面:

3●Settings Project(系统设置) 功能:打开USB调试,WIFI等设置 路径:Samples\Object Pascal\Mobile Samples\User Interface\Settings Project 开启界面:

4●ScrollableForm(滚动编辑框) 功能:滚动编辑框 路径:Samples\Object Pascal\Mobile Samples\User Interface\ScrollableForm 开启界面:

相关主题
文本预览
相关文档 最新文档