Color属性是灵活的它允许在Object Inspector窗口中以多种方式选择他们或者键入或者从列表中选择定编辑器因此TColorProperty的GetAttributes方法在返回值中包含多种属性
function TColorPropertyGetAttributes: TProrertyAttributes;
begin
Result := [PaMultiselect paDialog paValuelist];
end;
⑸ 注册属性编辑器
一旦创建属性编辑器必须在Delphi中注册注册属性编辑器时要与某种属性相联
调用RegisterPropertyEditor过程来注册属性编辑器该过程接受四个参数
● 要编辑的属性的类型信息的指针这总是通过调用调用TypeInfo函数得到如TypeInfo ( TMyComponent )
● 编辑器应用的部件类型如果该参数为nil则编辑器应用于所给的类型的所有属性
● 属性名该参数只有在前一参数描述了部件的情况下才可用
● 使用该属性编辑器的属性的类型
下面引用了注册标准部件的过程
procedure Register;
begin
RegisterPropertyEditor (TypeInfo(TComponent) nil TComponentProperty
RegisterPropertyEditor(TypeInfo(TComponentName) TComponent
Name (ComponentNamePropety)
RegisterPropertyEditor (TypeInfo(TMenuItem) TMenu TMenuItemProperty)
end;
这三句表达式使用RegisterPropertyEditor三种不同的用法
● 第一种最典型
它注册了用于所有TComponent类型属性的属性编辑器TComponentProperty通常当为某种类型属性注册属性编辑器时它就能应用于所有这种类型的属性因此第二和第三个参数为nil
● 第二个表达式注册特定类型的属性编辑器
它为特定部件的特定属性注册属性编辑器在这种情况下编辑器用于所有部件的Name属性
● 第三个表达式介于第一个和第二个表达式之间
它为部件TMenu的TMenuItem类型的所有属性注册了属性编辑器
创建事件
事件是部件的很重要的部分事件是部件必须响应的系统事件与响应事件的一段代码的联接响应代码被称为事件处理过程它总是由部件用户来编写通过使用事件应用开发者不需要改变部件本身就能定制部件的行为作为部件编写者运用事件能使应用发者定制所有的标准Delphi部件要创建事件应当理解
● 什么是事件
● 怎样实现标准事件
● 怎样定义自己的事件
什么是事件
事件是联接发生的事情与某些代码的机制或者说是方法指针一个指向特定对象实例的特定方法的指针从部件用户的角度事件是与系统事件(如OnClick)有关的名称用户能给该事件赋特定的方法供调用例如按钮Buttonl有OnClick方法缺省情况下Delphi在包含该按钮的窗体中产生一个为ButtonlClick的方法并将其赋给OnClick当一个Click事件发生在按钮上时按钮调用赋给OnClick的方法ButtonlClick:
部件用户将事件看作是由用户编写的代码而事件发生时由系统调用的处理办法
从部件编写者角度事件有更多的含义最重要的是提供了一个让用户编写代码响应特定事情的场所
要编写一个事件应当理解
● 事件和方法指针
● 事件是属性
● 事件处理过程类型
● 事件处理过程是可选的
⑴ 事件是方法指针
Delphi使用方法指针实现事件一个方法指针是指向特定对象实例的特定方法的特定指针作为部件编写者能将方法指针作为一种容器你的代码一发现事情发生就调用由用户定义的方法
方法指针的工作方式就象其它的过程类型但它们保持一个隐含的指向对象实例的指针所有的控制都继承了一个名为Click的方法以处理Click事件Click方法调用用户的Click事件处理过程
procedure TControlClick;
begin
if Assigned(OnClick ) then OnClick( Self )
end;
如果用户给Control的OnClick事件赋了处理过程(Handle)那鼠标点按Control时将导致方法被调用
⑵ 事件是属性
部件采用属性的形式实现事件不象大多数其它属性事件不使用方法来使实现read和write部分事件属性使用了相同类型的私有对象域作为属性按约定域名在属性名前加F例如OnClick方法的指针存在TNotifyEvent类型FOnClick域中OnClick事件属性的声明如下
type
TControl=class ( TComponent )
private
FOnClick: TNofiFyEvent; { 声明保存方法指针的域 }
protected
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
象其它类型的属性一样你能在运行时设置和改变事件的值将事件做成属性的主要好处是部件用户能在设计时使用Object Inspector设置事件处理过程
⑶ 事件处理过程类型
因为一个事件是指向事件处理过程的指针因此事件属性必须是方法指针类型被用作事件处理过程的代码必须是相应的对象的方法
所有的事件方法都是过程为了与所给类型的事件兼容一个事件处理过程必须有相同数目和相同类型的相同顺序的参数Delphi定义了所有标准事件处理过程的方法类型当你创建自己的事件时你能使用已有的事件类型或创建新的虽然不能用函数做事件处理过程但可以用var参数得到返回信息
在事件处理过程中传递var参数的典型例子是TKeyPressEvent类型的KeyPressed事件TKeyPressEvent定义中含有两个参数一个指示哪个对象产生该事件另一个指示那个键按下
type
TKeyPressEvent=procedure( Sender: TObject; var key: char) of Object;
通常key参数包含用户按下键的字符在某些情况下部件的用户可能想改变字符值例如在编辑器中强制所有字符为大写在这种情况下用户能定义下列的事件处理过程
procedure TFormlEditlKeyPressed( Sender: TObject; var key: char)
begin
key := Upcase( key )
end;
也可使用var参数让用户覆盖缺省的处理
⑷ 事件处理过程是可选的
在为部件创建事件时要记住部件用户可能并不编写该事件的处理过程这意味着你的部件不能因为部件用户没有编写处理代码而出错这种事件处理过程的可选性有两个方面
① 部件用户并非不得不处理事件
事件总是不断地发生在Windows应用程序中例如在部件上方移动鼠标就引起Windows发送大量的MouseMove消息给部件部件将鼠标消息传给OnMouseMove事件在大多数情况下部件用户不需要关心MouseMove事件这不会产生问题因为部件不依赖鼠标事件的处理过程同样自定义部件也不能依赖用户的事件处理过程
② 部件用户能在事件处理过程写任意的代码
一般说来对用户在事件处理过程中的代码没有限制Delphi部件库的部件都支持这种方式以使所写代码产生错误的可能性最小显然不能防止用户代码出现逻辑错误
怎样实现标准事件
Delphi带的所有控制继承了大多数Windows事件这些就是标准事件尽管所有这些事件都嵌在标准控制中但它们缺省是protected这意味着用户无法访问它们当创建控制时则可选择这些事件使用户可用将这些标准事件嵌入自定义控制需要考虑如下
● 什么是标准事件
● 怎样使事件可见
● 怎样修改标准事件处理过程
⑴ 什么是标准事件
有两种标准事件用于所有控制和只用于标准Windows控制
最基本的事件都定义在对象TControl中窗口控制图形控制和自定义控制都继承了这些事件下面列出用于所有控制的事件
OnClick OnDragDrop OnEndDrag OnMouseMove
OnDblClick OnDragOver OnMouseDown OnMouseUp
所有标准事件在TControl中都定义了相应的protected动态方法只是没有加On例如OnClick事件调用名为Click的方法
标准控制(从TWinControl继承)具有下列事件
OnEnter OnKeyDown OnkeyPress OnKeyUp OnExit
正如TControl中的标准事件窗口控制也有相应protected动态方法
⑵ 怎样使事件可见
标准事件的声明是protected如果想使用户在运行时或设计时能访问它们就需要将它们重声明为public和 published重声明属性而不描述它的实现将继承相同的实现方法只是改变了访问级别例如创建一个部件并使它的OnClick事件出现在运行时你可增加下面的部件声明
type
TMyControl=class(TCustomControl)
published
property OnClick; { 使OnClick在objectinspector中可见 }
end;
⑶ 怎样修改标准事件处理过程
如果想修改自定义部件响应某种事件的方法可以重写代码并将其赋给事件将联接每个标准事件的方法声明的protected是出于慎密的考虑通过覆盖实现方法能修改内部事件处理过程通过调用继承的方法能保持标准事件处理过程
调用继承的方法的顺序是很重要的一般首先调用继承的方法允许用户的事件处理过程代码在你的定制代码前执行然而也有在调用继承的方法之前执行自己的代码情况出现
下面是一个覆盖Click事件的例子
procedure TMyControlClick;
begin
inherited Click; { 执行标准处理包括调用事件处理过程你自己的定制代码 }
end;
定义自己的事件
定义全新的事件的情况是很少见的只有当部件的行为完全不同于任何其它事件才需要定义新事件定义新事件一般包含三个步骤
● 触发事件
● 定义处理过程类型
● 声明事件
● 调用事件
⑴ 触发事件
定义自己的事件要遇到的第一个关键是当使用标准事件时你不需要考虑由什么触发事件对某些事件问题是显然的例如一个MouseDown事件是在用户按下鼠标的左键时发生Windows给应用发送WM_LBUTTONDOWN消息接到消息后一个部件调用它的MouseDown方法它依次调用用户的OnMouseDown事件处理过程代码但是有些事件却不是那么可以描述清楚的例如滚行槓有一个OnChange事件可被各种情况触发包括按键鼠标点按或其它按制中的改变当定义事件时你必须使各种情况的发生调用正确的事件
这里有TControl处理WM_LBUTTONDOWN消息的方法DoMouseDown是私有的实现方法它提供了一般的处理左右和中按钮的方法并将Windows消息的参数转换为MouseDown方法的值
type
TControl = class(TComponent)
private
FOnMouseDown: TMouseEvent;
procedure DoMouseDown(var Message: TWMMouse; Button: TMouseButton;
Shift: TShiftState)
procedure WMLButtonDown(var Message: TWMLButtonDown)
message M_LBUTTONDOWN;
protected
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X Y: Integer) dynamic;
end;
procedure TControlMouseDown(Button: TMouseButton; Shift: TShiftState; X Y: Integer)
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self Button Shift X Y) { 调用事件处理过程 }
end;
procedure TControlDoMouseDown(var Message: TWMMouse; Button: TMouseButton;
Shift: ShiftState)
begin
with Message do
MouseDown(Button KeysToShiftState(Keys) + Shift XPos YPos) { 调用动态方法 }
end;
procedure TControlWMLButtonDown(var Message: TWMLButtonDown)
begin
inherited; { perform default handling }
if csCaptureMouse in ControlStyle then
MouseCapture := True;
if csClickEvents in ControlStyle then
Include(FControlState csClicked)
DoMouseDown(Message mbLeft []) { 调用常规的mousedown 方法 }
end;
当两种事情状态变化和用户交互发生时处理机制是相同的但过程稍微不同用户交互事件将总是由Windows消息触发状态改变事件也与Windows消息有关但它们也可由属性变化或其它代码产生你拥有对自定义事件触发的完全控制
[] [] [] []