汇编 | Java | C# | Delphi | C/C++ |

DELPHI基础教程 第十九章 Delphi自定义部件开发(二)

第十九章 Delphi自定义部件开发(二) 织梦好,好织梦

19.2.2 Delphi部件编程 

织梦好,好织梦

19.2.2.1 创建属性  织梦好,好织梦

 属性(Property)是部件中最特殊的部分,主要因为部件用户在设计时可以看见和操作它们,并且在交互过程中能立即得到返回结果。属性也很重要,因为如果将它们设计好后,将使用户更容易地使用,自己维护起来也很容易。 织梦内容管理系统

  为了使你在部件中更好地使用属性,本部分将介绍下列内容:

本文来自织梦

 ● 为什么要创建属性 copyright dedecms

  属性的种类

织梦内容管理系统

 ● 公布(publishing)继承的属性

copyright dedecms

  定义部件属性

内容来自dedecms

  编写属性编辑器

织梦好,好织梦

  dedecms.com

  1. 为什么要创建属性 本文来自织梦

  属性提供非常重要的好处,最明显的好处是属性在设计时能出现在Object Inspector窗口中,这将简化编程工作,因为你只需读用户所赋的值,而不要处理构造对象的参数。 织梦内容管理系统

  从部件使用者的观点看,属性象变量。用户可以给属性赋值或读值,就好象属性是对象的域。 本文来自织梦

  从部件编写者的观点看属性比对象的域有更强的功能; 内容来自dedecms

  用户可以在设计时设置属性

本文来自织梦

  这是非常重要的,因为不象方法,只能在运行时访问。属性使用户在运行程序之前就能定制部件,通常你的部件不应包含很多的方法,它们的功能可以通过属性来实现。

织梦好,好织梦

  属性能隐藏详细的实现细节 copyright dedecms

  属性能引起简单地赋值之外的响应,如触发事件

织梦好,好织梦

  ⑷ 用于属性的实现方法可以是虚拟方法,这样看似简单的属性在不同的部件中,将实现不同的功能。 dedecms.com

 

内容来自dedecms

 2. 属性的类型

dedecms.com

  属性可以是函数能返回的任何类型,因为属性的实现可以使用函数。所有的Pascal类型,兼容性规则都适用属性。为属性选择类型的最重要的方面是不同的类型出现在Object Inspector窗口中的方式不同。Object Inspector将按不同的类型决定其出现的方式。

本文来自织梦

你也能在注册部件时描述不同的属性编辑器。 内容来自dedecms

  下表列出属性出现在Object Inspector窗口中的方式 内容来自dedecms

 

织梦内容管理系统

19.3 属性出现在Object Inspector窗口中的方式 织梦好,好织梦

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ copyright dedecms

属性类型       处 内容来自dedecms

───────────────────────────────────────

dedecms.com

简单类型   NumericCharacter String属性出现在Object Inspector中,用户可 织梦好,好织梦

以直接编辑 织梦好,好织梦

枚举类型 枚举类型的属性显示值的方式定义在代码中。选择时将出现下拉   dedecms.com

式列表框,显示所有的可能取值。 织梦好,好织梦

集合类型 集合类型出现在Object Inspector窗口中时正如一个集合,展开后,用          内容来自dedecms

户通过将集合元素设为TrueFalse来选择。 本文来自织梦

对象类型 作为对象的属性本身有属性编辑器,如果对象有自己的published 织梦内容管理系统

性,用户在Object Inspector中通过展开对象属性列,可以独立编辑它们, dedecms.com

对象类型的属性必须从TPersistent继承。

本文来自织梦

数组类型 数组属性必须有它们自己的属性编辑器,Object Inspector没有内嵌对数

本文来自织梦

组属性编辑的支持。  

dedecms.com

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

织梦内容管理系统

  dedecms.com

3. 公布继承的属性

织梦好,好织梦

  所有部件都从祖先类型继承属性。当你从已有部件继承时,新部件将继承祖先类型的所有属性。如果你继承的是抽象类,则继承的属性是protectedpublic,但不是published。如想使用户访问protectedpublic属性,可以将该属性重定义为published。如果你使用TWinControl继承,它继承了Ctl3D属性,但是protected的,因此用户在设计和运行时不能访问Ctl3D,通过在新部件中将Ctl3D重声明为published,就改变了Ctl3D的访问级别。下面的代码演示如何将Ctl3D声明为published,使之在设计时可被访问。 dedecms.com

  copyright dedecms

  type 织梦内容管理系统

TSampleComponent=class(TWinControl) 织梦好,好织梦

published 内容来自dedecms

property Ctl3D; 织梦好,好织梦

end;

织梦好,好织梦

 

织梦内容管理系统

4. 定义部件属性 内容来自dedecms

  ⑴ 属性的声明

织梦好,好织梦

  声明部件的属性,你要描述:

本文来自织梦

 ● 属性名

copyright dedecms

  属性的类型

内容来自dedecms

  ● 读和设置属性值的方法 内容来自dedecms

 

织梦内容管理系统

  至少,部件属性应当定义在部件对象声明的public部分,这样可以在运行时很方便地从外部访问;为了能在设计时编辑属性,应当将属性在published部分声明,这样属性能自动显示在Object Inspector窗口中。下面是典型的属性声明:

本文来自织梦

  dedecms.com

  type dedecms.com

TYourComponent=class(TComponent) 本文来自织梦

copyright dedecms

private

内容来自dedecms

FCount: Integer { 内部存储域 } 本文来自织梦

function GetCount: Integer; { 读方法 }

copyright dedecms

procedure SetCount(ACount: Integer); { 写方法 }

本文来自织梦

pubilic

copyright dedecms

property Count: Integer read GetCount write SetCount; dedecms.com

end;

织梦内容管理系统

  copyright dedecms

  ⑵ 内部数据存储 copyright dedecms

  关于如何存储属性的数据值,Delphi没有特别的规定,通常Delphi部件遵循下列规定:

dedecms.com

 ● 属性数据存储在对象的数据域处 dedecms.com

  属性对象域的标识符以F开头,例如定义在TControl中的属性FWidth 织梦内容管理系统

  ● 属性数据的对象域应声明在private部分

copyright dedecms

  dedecms.com

  后代部件只应使用继承的属性自身,而不能直接访问内部的数据存储。 copyright dedecms

  ⑶ 直接访问 内容来自dedecms

  使属性数据可用的最简单的办法是直接访问。属性声明的read write部分描述了怎样不通过调用访问方法来给内部数据域赋值。但一般都用read进行直接访问,而用write进行方法访问,以改变部件的状态。

本文来自织梦

  下面的部件声明演示了怎样在属性定义的read write部分都采用直接访问: 内容来自dedecms

 

织梦内容管理系统

  type 织梦好,好织梦

TYourComponent=class(TComponent) copyright dedecms

本文来自织梦

private { 内部存储是私有 } 织梦好,好织梦

FReadOnly: Boolean; { 声明保存属性值的域 }

织梦内容管理系统

published { 使属性在设计时可用 } 本文来自织梦

property ReadOnly: Boolean read FReadOnly write FReadOnly; 本文来自织梦

end;

copyright dedecms

  内容来自dedecms

  ⑷ 访问方法

织梦内容管理系统

  属性的声明语法允许属性声明的readwrite部分用访问方法取代对象私有数据域。不管属性是如何实现它的read write部分,方法实现应当是private,后代部件只能使用继承的属性访问。

本文来自织梦

  ① 读方法 dedecms.com

属性的读方法是不带参数的函数,并且返回同属性相同类型的值。通常读函数的名字是“Get”后加属性名,例如,属性Count的读方法是GetCount。不带参数的唯一例外是数组属性。如果你不定义read方法,则属性是只写的。 织梦内容管理系统

  ② 写方法

织梦内容管理系统

  属性的写方法总是只带一个参数的过程。参数可以是引用或值。通常过程名是"Set"加属性名。例如,属性Count的写方法名是SetCount。参数的值采用设置属性的新值,因此,写方法需要执行在内部存储数据中写的操作。

dedecms.com

  如果没有声明写方法,那么属性是只读的。 copyright dedecms

  通常在设置新值前要检测新值是否与当前值不同。

内容来自dedecms

  下面是一个简单的整数属性Count的写方法:

本文来自织梦

  本文来自织梦

  procedure TMyComponent.SetCount( value: Integer); 内容来自dedecms

begin dedecms.com

if value <>FCount then

dedecms.com

begin 织梦内容管理系统

FCount := Value;

织梦内容管理系统

update; dedecms.com

end; 本文来自织梦

end;

本文来自织梦

  织梦内容管理系统

缺省属性值 织梦好,好织梦

  当声明一个属性,能有选择地声明属性的缺省值。部件属性的缺省值是部件构造方法中的属性值集。例如,当从Component Palette选择某部件置于窗体中时,Delphi通过调用部件构造方法创建部件,并决定部件属性初始值。

织梦好,好织梦

  Delphi使用声明缺省值决定是否将属性值存在DFM文件中。如果不描述缺省值,Delphi将总是保存该属性值。声明缺省值的方法是在属性声明后加default指令,再跟缺省值。

内容来自dedecms

  当重声明一个属性时,能够描述没有缺省值的属性。如果继承的属性已有一个,则设立没有缺省值的属性的方法是在属性声明后加nodefault指令。如果是第一次声明属性,则没有必要加nodefault指令,因为没有default指令即表示如此。

本文来自织梦

  下例是名为IsTrue的布尔类型属性设置缺省值True的过程: copyright dedecms

 

内容来自dedecms

  type 织梦内容管理系统

TSampleComponent=class(TComponent) 织梦好,好织梦

private 织梦内容管理系统

FIsaTrue: Boolean;

copyright dedecms

pubilic

dedecms.com

constructor Create (AOwner: TComponent); Overvide; 织梦好,好织梦

published 织梦内容管理系统

property Istrue: Boolean read FIsTrue write FIsTrue default True; 织梦内容管理系统

end;

copyright dedecms

 

dedecms.com

constructor TSampleComponent.Create (AOwner: TComponent);

copyright dedecms

begin dedecms.com

inherited Create ( Aowner);

织梦内容管理系统

Fistvue := True; { 设置缺省值 }

copyright dedecms

end; 本文来自织梦

  织梦内容管理系统

5. 编写属性编辑器

copyright dedecms

Object Inspector提供所有类型属性的缺省编辑器,Delphi也支持通过编写和注册属性编辑器的方法为属性设计自己的编辑器。可以注册专门为自定义部件的属性设计的编辑器,也可设计用于所有某类型的属性。编写属性编辑器需要下列五个步骤:

copyright dedecms

继承一个属性编辑器对象 织梦内容管理系统

 ● 将属性作为文本编辑

织梦内容管理系统

  将属性作为整体编辑 织梦好,好织梦

  ● 描述编辑器属性

本文来自织梦

 ● 注册属性编辑器 织梦内容管理系统

  本文来自织梦

  ⑴ 继承属性编辑器对象

本文来自织梦

  DsgnIntf库单元中定义了几种属性编辑器。它们都是从TPropertyEditor继承而来。当创建属性编辑器时,可以直接从TPropertyEditor中继承或从表中的任一属性编辑器中继承。 内容来自dedecms

 

织梦内容管理系统

 

dedecms.com

19.4 属性编辑器的类型 copyright dedecms

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

织梦好,好织梦

  类型 编辑的属性

内容来自dedecms

─────────────────────────────────────

copyright dedecms

TOrdinalProperty    所有有序的属性(整数、字符、枚举) dedecms.com

TIntegerProperty    所有整型,包括子界类型 织梦好,好织梦

TCharProperty     字符类型或字符子集

dedecms.com

TEnumProperty   任何枚举类型

本文来自织梦

TFloatProperty   所有浮点数 织梦好,好织梦

TStringProperty   字符串,包括定长的字符串

dedecms.com

TSetElementProperty 集合中的独立元素 织梦好,好织梦

TSetElementProperty 所有的集合,并不是直接编辑集合类型,而是展开成一列

本文来自织梦

集合元素属性 内容来自dedecms

TClassProperty 对象,显示对象名,并允许对象属性的展开

dedecms.com

TMethodPropevty 方法指针,主要指事件

本文来自织梦

TComponentProperty 相同窗体中的部件,用户不能编辑部件的属性, dedecms.com

但能指向兼容的部件 织梦内容管理系统

TColorProperty    部件颜色,显示颜色常量,否则显示十六进制数

织梦内容管理系统

TFontNameProperty 字体名称

copyright dedecms

TFontProperty 字体,允许展开字体的属性或弹出字体对话框 dedecms.com

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

织梦内容管理系统

 

copyright dedecms

  内容来自dedecms

  本文来自织梦

下面是TFloatPropertyEditor的定义: 本文来自织梦

 

copyright dedecms

type

dedecms.com

TFloatProperty=Class(TPropertyEditor)

织梦内容管理系统

public

本文来自织梦

function AllEqual: Boolean; override; 织梦好,好织梦

function GetValue: String; override;

织梦好,好织梦

procedure SetValue ( Const Value: string ); override; dedecms.com

end;

dedecms.com

 

本文来自织梦

象文本一样编辑属性

织梦好,好织梦

  所有的属性都需要将它们的值在Object Inspector窗口中以文本的方式显示。属性编辑器对象提供了文本表现和实际值之间转换的虚方法。这些虚方法是GetValueSetValue,你的属性编辑器也能继承了一系列的方法用于读和写不同类型的值。见下表:

dedecms.com

 

dedecms.com

19.5 读写属性值的方法 织梦内容管理系统

━━━━━━━━━━━━━━━━━━━━━━━━━━ 织梦内容管理系统

属性类型   "Get"方法 "Set"方法

copyright dedecms

────────────────────────── 本文来自织梦

浮点数 GetFloatValue SetFloatVallue

dedecms.com

方法指针 GetMethodValue SetMehodValue

copyright dedecms

有序类型 GetOrdValue SetOrdValue 织梦好,好织梦

字符串 GetStrValue SetStrValue 内容来自dedecms

━━━━━━━━━━━━━━━━━━━━━━━━━━

内容来自dedecms

 

copyright dedecms

当覆盖GetValue方法时,调用一个"Get"方法;当覆盖SetValue方法时调用一个"Set"方法。

织梦内容管理系统

  属性编辑器的GetValue方法返回一个字符串以表现当前属性值。缺省情况下GetValue返回"unknown"

织梦好,好织梦

属性编辑器的SetValue接收Object Inspector窗口String类型的参数,并将其转换成合适的类型,并设置属性值。

织梦内容管理系统

  下面是TIntegerPropertyGetValueSetValue的例子:

织梦好,好织梦

  织梦内容管理系统

function TIntegerProperty GetValue: string; dedecms.com

begin 内容来自dedecms

Result := IntToStr (GetOrdValue); 织梦好,好织梦

end;

本文来自织梦

 

本文来自织梦

proceduve TIntegerPropertySetValue (Const Value: string); 本文来自织梦

var 内容来自dedecms

L: Longint; dedecms.com

begin dedecms.com

L := StrToInt(Value); { 将字符串转换为数学 } 织梦好,好织梦

with GetTypeData (GetPropType)^ do

织梦好,好织梦

if ( L < Minvalue ) or ( L > MaxValue ) then 内容来自dedecms

Raise EPropertyError.Create (FmtloadStr(SOutOfRange, 织梦内容管理系统

[MinValueMaxValue])); 织梦内容管理系统

SetOrdValue (L); 织梦好,好织梦

end;

内容来自dedecms

 

织梦好,好织梦

将属性作为一个整体来编辑 织梦内容管理系统

  Delphi支持提供用户以对话框的方式可视化地编辑属性。这种情况常用于对对象类型属性的编辑。一个典型的例子是Font属性,用户可以找开Font对话框来选择字体的属性。

本文来自织梦

  提供整体属性编辑对话框,要覆盖属性编辑对象的Edit方法。Edit方法也使用"Get""Set"方法。 织梦内容管理系统

 在大多数部件中使用的Color属性将标准的Windows颜色对话框作为属性编辑器。下面是TColorPropertyEdit方法

内容来自dedecms

 

内容来自dedecms

procedure TColorProperty.Edit

dedecms.com

var dedecms.com

ColorDialog: TColorDialog; 织梦内容管理系统

begin

dedecms.com

ColorDialog := TColorDialog.Create(Application); { 创建编辑器 }

本文来自织梦

try

织梦内容管理系统

ColorDialog.Color := GetOrdValue; { 使用已有的值 }

内容来自dedecms

if ColorDialog.Execute then 织梦内容管理系统

  SetOrdValue (ColorDialog.Color);

织梦好,好织梦

finally 内容来自dedecms

ColorDialog.Free;

本文来自织梦

end;

织梦好,好织梦

 end; 内容来自dedecms

  copyright dedecms

描述编辑器的属性 内容来自dedecms

  属性编辑必须告诉Object Inspector窗口如何采用合适的显示工具。例如Object Inspector窗口需要知道属性是否有子属性,或者是否能显示可能取值的列表。描述编辑器的属性通常覆盖属性编辑器的GetAttributes方法。 本文来自织梦

GetAttributes返回TPropertyAttributes类型的集合。集合中包括表中任何或所有的值: 织梦内容管理系统

 

内容来自dedecms

19.6 属性编辑器特征标志 dedecms.com

   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 织梦内容管理系统

  标志 相关方法 copyright dedecms

   ──────────────────────────────

本文来自织梦

paValuelist 编辑器能给予一组枚举值 GetValues

copyright dedecms

paSubPropertie 属性有子属性 GetPropertises

dedecms.com

paDialog 编辑器能显示编辑对话框 Edit 织梦好,好织梦

PaMultiSelect 当用户选择多于一个部件

内容来自dedecms

时,属性应能显示 N/A copyright dedecms

   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ copyright dedecms

  织梦好,好织梦

Color属性是灵活的,它允许在Object Inspector窗口中以多种方式选择他们。或者键入,或者从列表中选择定编辑器。因此TColorPropertyGetAttributes方法在返回值中包含多种属性。

本文来自织梦

  内容来自dedecms

  function TColorProperty.GetAttributes: TProrertyAttributes; copyright dedecms

begin

内容来自dedecms

Result := [PaMultiselect, paDialog, paValuelist]; copyright dedecms

end; 内容来自dedecms

  织梦好,好织梦

 ⑸ 注册属性编辑器 本文来自织梦

  一旦创建属性编辑器,必须在Delphi中注册。注册属性编辑器时,要与某种属性相联。 本文来自织梦

  调用RegisterPropertyEditor过程来注册属性编辑器。该过程接受四个参数:

copyright dedecms

  ● 要编辑的属性的类型信息的指针。这总是通过调用调用TypeInfo函数得到,如TypeInfo ( TMyComponent )

织梦好,好织梦

  ● 编辑器应用的部件类型,如果该参数为nil则编辑器应用于所给的类型的所有属性 织梦内容管理系统

  ● 属性名,该参数只有在前一参数描述了部件的情况下才可用

内容来自dedecms

  ● 使用该属性编辑器的属性的类型 dedecms.com

  内容来自dedecms

  下面引用了注册标准部件的过程:

本文来自织梦

 

copyright dedecms

procedure Register; 织梦内容管理系统

begin

内容来自dedecms

RegisterPropertyEditor (TypeInfo(TComponent), nil, TComponentProperty, copyright dedecms

RegisterPropertyEditor(TypeInfo(TComponentName), TComponent, 织梦好,好织梦

'Name', (ComponentNamePropety); 本文来自织梦

RegisterPropertyEditor (TypeInfo(TMenuItem), TMenu, '', TMenuItemProperty);

copyright dedecms

end;

dedecms.com

  本文来自织梦

  这三句表达式使用RegisterPropertyEditor三种不同的用法: 内容来自dedecms

  ● 第一种最典型 copyright dedecms

它注册了用于所有TComponent类型属性的属性编辑器TComponentProperty。通常,当为某种类型属性注册属性编辑器时,它就能应用于所有这种类型的属性,因此,第二和第三个参数为nil copyright dedecms

  ● 第二个表达式注册特定类型的属性编辑器 内容来自dedecms

它为特定部件的特定属性注册属性编辑器,在这种情况下,编辑器用于所有部件的Name属性。 织梦好,好织梦

 ● 第三个表达式介于第一个和第二个表达式之间 copyright dedecms

它为部件TMenuTMenuItem类型的所有属性注册了属性编辑器。

dedecms.com

  织梦好,好织梦

19.2.2.2 创建事件

本文来自织梦

  织梦内容管理系统

  事件是部件的很重要的部分。事件是部件必须响应的系统事件与响应事件的一段代码的联接。响应代码被称为事件处理过程,它总是由部件用户来编写。通过使用事件,应用开发者不需要改变部件本身就能定制部件的行为。作为部件编写者,运用事件能使应用发者定制所有的标准Delphi部件。要创建事件,应当理解: dedecms.com

  什么是事件

copyright dedecms

  怎样实现标准事件

本文来自织梦

  怎样定义自己的事件

织梦内容管理系统

 

本文来自织梦

1. 什么是事件

copyright dedecms

事件是联接发生的事情与某些代码的机制,或者说是方法指针,一个指向特定对象实例的特定方法的指针。从部件用户的角度,事件是与系统事件(OnClick)有关的名称,用户能给该事件赋特定的方法供调用。例如,按钮ButtonlOnClick方法,缺省情况下Delphi在包含该按钮的窗体中产生一个为ButtonlClick的方法,并将其赋给OnClick。当一个Click事件发生在按钮上时,按钮调用赋给OnClick的方法ButtonlClick

dedecms.com

部件用户将事件看作是由用户编写的代码,而事件发生时由系统调用的处理办法。

内容来自dedecms

  从部件编写者角度事件有更多的含义。最重要的是提供了一个让用户编写代码响应特定事情的场所。

内容来自dedecms

  要编写一个事件,应当理解: 织梦内容管理系统

 ● 事件和方法指针 织梦内容管理系统

  事件是属性

织梦内容管理系统

  ● 事件处理过程类型 织梦内容管理系统

 ● 事件处理过程是可选的 织梦好,好织梦

  织梦好,好织梦

  ⑴ 事件是方法指针 织梦好,好织梦

  Delphi使用方法指针实现事件。一个方法指针是指向特定对象实例的特定方法的特定指针。作为部件编写者,能将方法指针作为一种容器。你的代码一发现事情发生,就调用由用户定义的方法。   内容来自dedecms

  方法指针的工作方式就象其它的过程类型,但它们保持一个隐含的指向对象实例的指针。所有的控制都继承了一个名为Click的方法,以处理Click事件。Click方法调用用户的Click事件处理过程。

dedecms.com

  内容来自dedecms

  procedure TControl.Click; 织梦内容管理系统

begin

本文来自织梦

if Assigned(OnClick ) then OnClick( Self );

内容来自dedecms

end; dedecms.com

  dedecms.com

如果用户给ControlOnClick事件赋了处理过程(Handle),那鼠标点按Control时将导致方法被调用。 织梦好,好织梦

  ⑵ 事件是属性 织梦内容管理系统

  部件采用属性的形式实现事件。不象大多数其它属性,事件不使用方法来使实现readwrite部分。事件属性使用了相同类型的私有对象域作为属性。按约定域名在属性名前加“F”。例如OnClick方法的指针,存在TNotifyEvent类型FOnClick域中。OnClick事件属性的声明如下: 内容来自dedecms

 

内容来自dedecms

type

内容来自dedecms

TControl=class ( TComponent ) 织梦内容管理系统

private 织梦内容管理系统

FOnClick: TNofiFyEvent; { 声明保存方法指针的域 }

本文来自织梦

protected

织梦内容管理系统

property OnClick: TNotifyEvent read FOnClick write FOnClick;

内容来自dedecms

end; 织梦内容管理系统

  内容来自dedecms

 象其它类型的属性一样,你能在运行时设置和改变事件的值。将事件做成属性的主要好处是部件用户能在设计时使用Object Inspector设置事件处理过程。

织梦好,好织梦

  ⑶ 事件处理过程类型 织梦好,好织梦

  因为一个事件是指向事件处理过程的指针,因此事件属性必须是方法指针类型,被用作事件处理过程的代码,必须是相应的对象的方法。

织梦内容管理系统

  所有的事件方法都是过程。为了与所给类型的事件兼容,一个事件处理过程必须有相同数目和相同类型的相同顺序的参数。Delphi定义了所有标准事件处理过程的方法类型,当你创建自己的事件时,你能使用已有的事件类型,或创建新的。虽然不能用函数做事件处理过程,但可以用var参数得到返回信息。 内容来自dedecms

  在事件处理过程中传递var参数的典型例子是TKeyPressEvent类型的KeyPressed事件。TKeyPressEvent定义中含有两个参数。一个指示哪个对象产生该事件。另一个指示那个键按下:

织梦好,好织梦

 

织梦内容管理系统

  type

内容来自dedecms

TKeyPressEvent=procedure( Sender: TObject; var key: char) of Object;

织梦好,好织梦

 

织梦好,好织梦

通常key参数包含用户按下键的字符。在某些情况下,部件的用户可能想改变字符值。例如在编辑器中强制所有字符为大写,在这种情况下,用户能定义下列的事件处理过程: 本文来自织梦

  织梦好,好织梦

 procedure TForml.EditlKeyPressed( Sender: TObject; var key: char); dedecms.com

begin 本文来自织梦

key := Upcase( key );

织梦好,好织梦

end; 内容来自dedecms

  copyright dedecms

也可使用var参数让用户覆盖缺省的处理。

copyright dedecms

  ⑷ 事件处理过程是可选的 织梦好,好织梦

  在为部件创建事件时要记住部件用户可能并不编写该事件的处理过程。这意味着你的部件不能因为部件用户没有编写处理代码而出错。这种事件处理过程的可选性有两个方面: 内容来自dedecms

  部件用户并非不得不处理事件

copyright dedecms

事件总是不断地发生在Windows应用程序中。例如,在部件上方移动鼠标就引起Windows发送大量的Mouse-Move消息给部件,部件将鼠标消息传给OnMouseMove事件。在大多数情况下,部件用户不需要关心MouseMove事件,这不会产生问题,因为部件不依赖鼠标事件的处理过程。同样,自定义部件也不能依赖用户的事件处理过程。

dedecms.com

  部件用户能在事件处理过程写任意的代码 dedecms.com

  一般说来,对用户在事件处理过程中的代码没有限制。Delphi部件库的部件都支持这种方式以使所写代码产生错误的可能性最小。显然,不能防止用户代码出现逻辑错误。 织梦好,好织梦

  2. 怎样实现标准事件

dedecms.com

  Delphi带的所有控制继承了大多数Windows事件,这些就是标准事件。尽管所有这些事件都嵌在标准控制中,但它们缺省是protected,这意味着用户无法访问它们,当创建控制时,则可选择这些事件使用户可用。将这些标准事件嵌入自定义控制需要考虑如下:

织梦内容管理系统

  什么是标准事件 本文来自织梦

  怎样使事件可见 本文来自织梦

  怎样修改标准事件处理过程 内容来自dedecms

  织梦好,好织梦

  ⑴ 什么是标准事件

织梦好,好织梦

  有两种标准事件:用于所有控制和只用于标准Windows控制。

dedecms.com

  最基本的事件都定义在对象TControl中。窗口控制、图形控制和自定义控制都继承了这些事件,下面列出用于所有控制的事件:

copyright dedecms

  OnClick OnDragDrop OnEndDrag OnMouseMove dedecms.com

 OnDblClick OnDragOver OnMouseDown OnMouseUp

本文来自织梦

  copyright dedecms

  所有标准事件在TControl中都定义了相应的protected动态方法,只是没有加“On”例如OnClick事件调用名为Click的方法。

copyright dedecms

  标准控制(从TWinControl继承)具有下列事件:

本文来自织梦

 OnEnter OnKeyDown OnkeyPress OnKeyUp OnExit

copyright dedecms

 

织梦好,好织梦

正如TControl中的标准事件,窗口控制也有相应protected动态方法。 dedecms.com

  ⑵ 怎样使事件可见

本文来自织梦

  标准事件的声明是protected,如果想使用户在运行时或设计时能访问它们,就需要将它们重声明为public published。重声明属性而不描述它的实现将继承相同的实现方法,只是改变了访问级别。例如,创建一个部件并使它的OnClick事件出现在运行时,你可增加下面的部件声明:

内容来自dedecms

 

本文来自织梦

  type 织梦内容管理系统

TMyControl=class(TCustomControl) dedecms.com

published

本文来自织梦

property OnClick; { 使OnClickobjectinspector中可见 } dedecms.com

end; 织梦好,好织梦

 

dedecms.com

怎样修改标准事件处理过程

copyright dedecms

  如果想修改自定义部件响应某种事件的方法,可以重写代码并将其赋给事件。将联接每个标准事件的方法声明的protected是出于慎密的考虑。通过,覆盖实现方法,能修改内部事件处理过程,通过调用继承的方法,能保持标准事件处理过程。

织梦好,好织梦

  调用继承的方法的顺序是很重要的。一般首先调用继承的方法,允许用户的事件处理过程代码在你的定制代码前执行。然而也有在调用继承的方法之前执行自己的代码情况出现。 本文来自织梦

  下面是一个覆盖Click事件的例子:

copyright dedecms

 

dedecms.com

procedure TMyControl.Click; copyright dedecms

begin 织梦好,好织梦

inherited Click; { 执行标准处理,包括调用事件处理过程你自己的定制代码 }

内容来自dedecms

end; 本文来自织梦

  织梦好,好织梦

3. 定义自己的事件 dedecms.com

  定义全新的事件的情况是很少见的。只有当部件的行为完全不同于任何其它事件才需要定义新事件。定义新事件一般包含三个步骤:

本文来自织梦

  ● 触发事件 copyright dedecms

   ● 定义处理过程类型 织梦内容管理系统

   声明事件 copyright dedecms

  ● 调用事件 织梦内容管理系统

 

本文来自织梦

触发事件 copyright dedecms

  定义自己的事件要遇到的第一个关键是:当使用标准事件时你不需要考虑由什么触发事件。对某些事件,问题是显然的。例如:一个MouseDown事件是在用户按下鼠标的左键时发生,Windows给应用发送WM_LBUTTONDOWN消息。接到消息后,一个部件调用它的MouseDown方法,它依次调用用户的OnMouseDown事件处理过程代码。但是有些事件却不是那么可以描述清楚的。例如:滚行杠有一个OnChange事件,可被各种情况触发,包括按键、鼠标点按或其它按制中的改变。当定义事件时,你必须使各种情况的发生调用正确的事件。

本文来自织梦

  这里有TControl处理WM_LBUTTONDOWN消息的方法,DoMouseDown是私有的实现方法,它提供了一般的处理左、右和中按钮的方法,并将Windows消息的参数转换为MouseDown方法的值。

本文来自织梦

  本文来自织梦

type

织梦好,好织梦

TControl = class(TComponent)

dedecms.com

private dedecms.com

FOnMouseDown: TMouseEvent; copyright dedecms

procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;

dedecms.com

Shift: TShiftState);

copyright dedecms

procedure WMLButtonDown(var Message: TWMLButtonDown);

织梦内容管理系统

message M_LBUTTONDOWN;

内容来自dedecms

protected 织梦内容管理系统

procedure MouseDown(Button: TMouseButton; Shift: TShiftState;

内容来自dedecms

X, Y: Integer); dynamic;

织梦内容管理系统

end; copyright dedecms

  本文来自织梦

procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

copyright dedecms

begin

本文来自织梦

if Assigned(FOnMouseDown) then 织梦内容管理系统

FOnMouseDown(Self, Button, Shift, X, Y); { 调用事件处理过程 } 本文来自织梦

end;

织梦好,好织梦

  织梦内容管理系统

procedure TControl.DoMouseDown(var Message: TWMMouse; Button: TMouseButton;

织梦内容管理系统

Shift: ShiftState); dedecms.com

begin copyright dedecms

with Message do 内容来自dedecms

MouseDown(Button, KeysToShiftState(Keys) + Shift, XPos, YPos); { 调用动态方法 }

内容来自dedecms

end; copyright dedecms

  copyright dedecms

procedure TControl.WMLButtonDown(var Message: TWMLButtonDown); 织梦内容管理系统

begin 织梦好,好织梦

inherited; { perform default handling }

本文来自织梦

if csCaptureMouse in ControlStyle then

织梦好,好织梦

MouseCapture := True; dedecms.com

if csClickEvents in ControlStyle then

织梦好,好织梦

Include(FControlState, csClicked);

本文来自织梦

DoMouseDown(Message, mbLeft, []); { 调用常规的mouse-down 方法 } 内容来自dedecms

end; 本文来自织梦

 

内容来自dedecms

  当两种事情-状态变化和用户交互—发生时,处理机制是相同的,但过程稍微不同。用户交互事件将总是由Windows消息触发。状态改变事件也与Windows消息有关,但它们也可由属性变化或其它代码产生。你拥有对自定义事件触发的完全控制。

本文来自织梦

定义处理过程类型 织梦内容管理系统

  一旦你决定产生事件,就要定义事件如何被处理,这就是要决定事件处理过程的类型。在大多数情况下,定义的事件处理过程的类型是简单的通知类型(TNotifyEvent)和已定义的事件类型。 copyright dedecms

  通知事件只是告诉你特定的事件发生了,而没有描述什么时候和什么地方。通知事件使用时只带一个TObject类型的参数,该参数是Sender。然而所有通知事件的处理过程都知道是什么样的事件发生和发生在那个部件。例如:Click事件是通知类型。当编写Click事件的处理过程时,你知道的是Click事件发生和哪个部件被点按了。通知事件是单向过程。没有提供反馈机制。 dedecms.com

  在某些情况下,只知道什么事件发生和发生在那个部件是不够的。如果按键事件发生,事件处理过程往往要知道用户按了哪个键。在这种情况下,需要事件处理过程包含有关事件的必要信息的参数。如果事件产生是为了响应消息,那么传递给事件的参数最好是直接来自消息参数。 dedecms.com

  因为所有事件处理过程都是过程,所以从事件处理过程中返回信息的唯一方法是通过var参数。自定义部件可以用这些信息决定在用户事件处理过程执行后是否和怎样处理事件。

dedecms.com

  例如,所有的击键事件(OnKeyDownOnKeyUpOnKeyPressed)通过名为keyvar参数传递键值。为了使应用程序看见包含在事件中的不同的键,事件处理过程可以改变key变量值。 copyright dedecms

 

内容来自dedecms

声明事件

dedecms.com

  一旦你决定了事件处理过程的类型,你就要准备声明事件的方法指针和属性。为了让用户易于理解事件的功能,应当给事件一个有意义的名字,而且还要与部件中相似的属性的名称保持一致。 织梦好,好织梦

  Delphi中所有标准事件的名称都以“On”开头。这只是出于方便,编译器并不强制它。Object Inspector是看属性类型来决定属性是否是事件,所有的方法指针属性都被看作事件,并出现在事件页中。 本文来自织梦

调用事件

织梦好,好织梦

  一般说来,最好将调用集中在事件上。就是说在部件中创建一个虚方法来调用用户的事件处理过程和提供任何缺省处理。当调用事件时,应考虑以下两点:

copyright dedecms

  ● 必须允许空事件 copyright dedecms

用户能覆盖缺省处理 织梦内容管理系统

  内容来自dedecms

不能允许使空事件处理过程产生错误的情况出现。就是说,自定义部件的正常功能不能依赖来自用户事件处理过程的响应。实际上,空事件处理过程应当产生与无事件处理过程一样的结果。 dedecms.com

  部件不应当要求用户以特殊方式使用它们。既然一个空事件处理过程应当与无事件处理过程一样动作,那么调用用户事件处理过程的代码应当象这样:

dedecms.com

 

织梦内容管理系统

  if Assigned(OnClick) then OnClick(Self);

copyright dedecms

{ 执行缺省处理 } 本文来自织梦

  织梦好,好织梦

而不应该有这样的代码:

dedecms.com

 

dedecms.com

if Assigned(OnClick) then copyright dedecms

OnClick(Self)

copyright dedecms

else 内容来自dedecms

; { 执行缺省处理 }

dedecms.com

  本文来自织梦

  对于某些种类的事件,用户可能想取代缺省处理甚至删除所有的响应。为支持用户实现这种功能,你需要传递var参数给事件处理过程,并在事件处理过程返回时检测某个值。空事件处理过程与无事件处理过程有相同作用。因为空事件处理过程不会改变任何var参数值。所以缺省处理总是在调用空事件处理过程后发生。

织梦好,好织梦

  例如在处理Key-Press事件,用户可以通过将var参数key的值设置为空字符(#0)来压制部件的缺省处理,代码如下: 织梦内容管理系统

  本文来自织梦

  if Assigned(OnkeyPress) then OnkeyPress(Self key);

dedecms.com

if key <> #0 then { 执行缺省处理 } ;

本文来自织梦

  本文来自织梦

实际的代码将与这稍有不同,因为它只处理窗口消息,但处理逻辑是相同的。在缺省情况下,部件先调用任何用户赋予的事件处理过程,然后执行标准处理。如果用户的事件处理过程将key设为空,则部件跳过缺省处理。 织梦内容管理系统

  本文来自织梦

19.2.2.3 处理消息 内容来自dedecms

 

织梦好,好织梦

  在传统Windows编程中,一个很关键的方面是处理Windows发送给应用程序的消息。Delphi已经帮你处理了大多数的普通消息,但是在创建部件的过程中有可能Delphi没有处理方法,得由自己处理消息,也可能创建了新的消息需要处理它们。 dedecms.com

  学习掌握Delphi的消息处理,要掌握以下三个方面:

dedecms.com

 ● 理解消息处理系统 织梦内容管理系统

修改(改变)消息处理方法

织梦内容管理系统

建立新的消息处理方法 dedecms.com

  织梦内容管理系统

1. 理解消息处理系统

织梦内容管理系统

  所有的Delphi对象内部具有处理消息的机制,如调用消息处理方法或消息处理过程。消息处理的基本思想是对象接收某种消息并派送它们,这是通过调用与接收的消息相应的方法来实现的,如果没有相应于消息的指定的方法,那就调用缺省处理。下面的图解表示消息派送系统: copyright dedecms

Delphi部件库定义了将所有Windows消息(包括用户自定义消息)直接转换到对象方法调用的消息派送系统。一般没有必要改变这种消息派送系统,只要建立消息处理方法。

dedecms.com

  ⑴Windows消息中有什么?

本文来自织梦

Windows消息是包含若干有用的域的数据记录。记录中最重要的是一个整型大小的值,该值标识消息。Windows定义了大量的消息。库单元Messages声明了所有消息的标识。消息中其它的有用信息包括两个域参数和结果域。两个参数分别是16位和32位的。Windows代码总是以wParamlParam来引用它们。 dedecms.com

  最初,Windows程序员不得不记住包含的每一个参数。现在,微软公司已经命名了这参数。这样理解伴随这些消息的信息就更简单了。例如,WM_KEYDOWN消息的参数被称为vkeykeydata,这就比wParamlParam给出了更多的描述信息。

dedecms.com

  Delphi为不同类型的消息定义了指定的记录类型。如鼠标消息在long参数中传递鼠标事件的xy座标,一个在高字,一个在低字。使用鼠标消息记录,你不需要自己关心哪个字是哪个座标,因为引用这些参数时通过名子XposYpos取代了lParamLolParamHi 织梦好,好织梦

  ⑵ 派送方法 织梦好,好织梦

  当应用程序创建窗口时,在Windows Kernel中注册了一个窗口过程。窗口过程是处理窗口消息的函数。传统上,窗口过程包括了Case表达式,表达式的每个入口是窗口要处理的每一条消息。当你每次创建窗口时,必须建立完整的窗口过程。

dedecms.com

  Delphi在下列三方面简化了消息派送: dedecms.com

  ● 每个部件继承了完整的消息派送系统

织梦内容管理系统

派送系统具有缺省处理。用户只需定义想响应的消息的处理方法

织梦内容管理系统

可以修改消息处理的一部分,依靠继承的方法完成大多数处理 内容来自dedecms

  本文来自织梦

这种消息派送系统的最大优点是用户能在任何时候安全地发送任何消息给任何部件。如果部件没有为该消息定义处理方法,那缺省处理方法会解决这个问题,通常是忽略它。 本文来自织梦

  Delphi为应用程序每种类型的部件注册了名为MainWndProc的方法作为窗口过程。MainWndProc包含了异常处理块,它完成从Windows到名为WndProc的虚方法传送消息记录,并且通过调用应用程序对象的HandleException方法处理异常。 copyright dedecms

  MainWndProc是静态方法,没有包含任何消息的指定处理方法。定制过程发生在WndProc中,因为每个部件类型都能覆盖该方法以适合特定的需要。 dedecms.com

  WndProc方法为每个影响它们处理的任何条件进行检查,以捕捉不要的消息。例如,当被拖动时,部件忽略键盘事件,因此,TWinControlWndProc只在没有拖动时传送键盘事件。最后WndProc调用Dispatch方法,该方法是从TObject继承来的静态方法,决定什么方法来处理消息。

织梦好,好织梦

  Dispatch使用消息记录的Msg域来决定怎样派送特定消息。如果部件已经给该消息定义了处理方法,则Dispatch调用该方法,反之,Dispatch调用缺省处理方法。 dedecms.com

  2. 改变消息处理方法

本文来自织梦

  在改变自定义部件的消息处理方法之前,先要弄清楚你真正想要做什么。Delphi将大多数的Windows消息转换成部件编写者和部件用户都能处理的事件。一般来说,你应当改变事件处理行为而不是改变消息处理行为。 本文来自织梦

为了改变消息处理行为,要覆盖消息处理方法。也能提供捕获消息防止部件处理该消息。 dedecms.com

覆盖处理方法 织梦内容管理系统

为了改变部件处理特定消息的方法,要覆盖那个消息的处理方法。如果部件不处理该消息,你就需要声明新的消息处理方法。

copyright dedecms

为了覆盖消息处理方法,要在部件中以相同的消息索引声明新的方法。不要使用override指令,你必须使用Message指令和相应的消息索引。

织梦好,好织梦

  例如,为了覆盖一个处理WM_PAINT消息的方法,你要重声明WMPaint方法: dedecms.com

  织梦好,好织梦

type 内容来自dedecms

TMyComponent=class()

内容来自dedecms

procedure WMPaint(var Message: TWMPaint); message WM_PAINT;

本文来自织梦

end; 织梦好,好织梦

  dedecms.com

使用消息参数 dedecms.com

  在消息处理方法内部,自定义部件访问消息记录的所有参数。因为消息总是var参数,如果需要的话,事件处理过程可以改变参数的值。Result域是经常改变的参数。ResultWindows文档中所指的消息的返回值:由SendMessage返回。 copyright dedecms

  因为消息处理方法的消息参数的类型随着被处理的消息的变化而变化,所以应当参考Windows消息文档中的参数的名字和含义。如果出于某种原因要使用旧风格的消息参数(wParamlParam),可以配合通用类型TMessage来决定Message 织梦好,好织梦

  ⑶ 捕获消息

copyright dedecms

  在某种情况下,你可能希望自定义部件能忽略某种消息。就是说,阻止部件将该消息派送给它的处理方法。为了那样来捕获消息,可以覆盖虚方法WndProc

织梦内容管理系统

  WndProc方法在将消息传给Dispatch方法前屏蔽该消息。它依次决定哪一个方法来处理消息。通过覆盖WndProc,部件得到了派送消息之前过滤它们的机会。 dedecms.com

  通常,象下面这样覆盖WndProc copyright dedecms

 

织梦好,好织梦

procedure TMyControl.WndProc(var Message: TMessage); 织梦好,好织梦

begin

copyright dedecms

{ 决定是否继续处理过程 }

内容来自dedecms

inherited WndProc (Message); 织梦内容管理系统

end;

dedecms.com

  本文来自织梦

下面的代码是TControlWndProc的一部分。TControl定义整个范围内的鼠标消息,当用户拖动和放置控制时,它们将被滤过。 copyright dedecms

 

dedecms.com

  procedure TControl WndProc(var Message:TMessage);

织梦内容管理系统

begin 织梦内容管理系统

if (Message.Msg >= WM_MOVSEFIRST) and

织梦内容管理系统

(Message.Msg <= WM_MOUSELAST) then

织梦好,好织梦

if Dragging then

织梦内容管理系统

DragMouseMsg(TWMMOUSE(Message)) { 处理拖动 } 本文来自织梦

else 本文来自织梦

…   { 正常处理其它 }

内容来自dedecms

…   { 否则正常处理 } 织梦内容管理系统

end;

织梦内容管理系统

精彩推荐
热点内容
最近更新