Delphi学习-事件机制

来源:互联网 发布:淘宝买家好评率90低吗 编辑:程序博客网 时间:2024/06/05 10:15

Delphi主要是一个RAD的开发工具,在开发速度上,那是相当的快速的,这都是归功于他的组件了,然而Delphi的那么多组件结合了之后如何工作呢?这里就是今天要说的了,那就是事件机制,众所周知Windows下各个Win32程序的运作都是通过消息来驱动的,而Delphi则将各种各样的消息包装,于是成为了事件,比如你想写一个按钮,让用户点这个按钮的时候,弹出一个对话框,那么我们就可以为这个按钮指定一个单击事件OnClick。这个单击事件实际上也是靠的系统消息运作的(WM_LButtonDown,WM_LButtonUp这两个消息产生一个单击事件,但是最开始的时候,我们不必了解这些东西,这就是Delphi的快速原因,她能够让开发者在不了解系统的运作情况下写出一个像样的程序,但是同时也正是因为这样,导致了很多人的误解,误解认为Delphi只用来开发数据库程序。所以每次讲到这个方面的时候,我都会一而再,再而三的跟那些人说,Delphi是能够做很多事的,关键是要看你对Delphi的了解程度以及对系统底层运作机制的了解程度。如果你一直停留在拖拖拉拉控件的层面上,那自然就上不了另一个台阶了。罢了,废话了一大箩筐,还是继续下面的。)

    事件是连接发生的事情与某些代码的机制,或者说是方法指针(就可以看做为C中的函数指针或者说回调),一个指向特定对象实例的特定方法的指针。从组件用户的角度,事件是与系统事件(如OnClick)有关的名称,用户能给该事件赋予特定的方法供调用。例如,按扭Button1有OnClick方法,默认情况下Delphi在包含该按纽的窗体中产生一个名为ButtonClick的方法,并将其赋给OnClick。当一个Click事件发生在按纽上时,按纽调用赋给OnClick的方法ButtonClick。
组件用户将事件看做是由用户编写的代码,是某个'事情'发生时有系统调用的处理方法。

从组件编写者的角度看事件有更多的含义。最重要的是提供了一个让用户编写代码响应特定事件的场所。
1、事件是方法指针
    Delphi使用方法指针实现事件。一个方法指针是指向特定对象实例的特定方法的特定指针。作为组件编写者,能将方法指针作为一种容器。你的代码一旦检测到事情发生,就调用由用户定义的方法。
方法指针的工作方式就像其他的过程类型,但它们保持一个隐含的指向实例的指针。所有的组件都继承了一个名为Click的方法,以处理Click事件。Click方法调用用户的Click事件处理过程。

Procedure TControl.Click;
begin
  if Assigned(OnClick) then OnClick(Self);  //这里就是判断OnClick是否是一个有效的,有效就调用
end;

如果用户给Control的OnClick事件赋予了处理过程(Handle),当鼠标单击Control时将导致方法被调用。
2、事件是属性
   组件采用属性的形式实现事件。不像大多数其他属性,事件不使用方法来实现 read和write部分。事件属性使用了相同类型的私有对象域作为属性。按约定域名在属性名前加'F'。如Onclick方法的指针,存在TNotifyEvent类型的FOnClick域中。OnClick事件属性的声明如下:
typ
  TControl=class(TComponent)
  private
    FOnClick:TNotifyEvent;//声明保存方法指针的域
  protcted
    property OnClick:TNotifyEvent read FOnClick write FOnClick;
end;

像其他类型的属性一样,可以在运行时设置和改变事件的值。将事件作为属性的主要好处是组件用户能在设计时使用Object Inspector(属性面板)设置事件处理过程
3、事件处理过程类型

因为一个事件是指向事件处理过程的指针,因此事件属性必须是方法指针类型,被用做事件处理过程的代码,必须是相应的对象方法。
所有的事件方法都是过程。为了与所给类型的事件兼容,一个事件处理过程必须有相同数目和相同类型的相同顺序的参数。Delphi定义了所有标准事件处理过程的方法类型,当你创建自己的事件时,你能使用自己有的事件类型,或创建新的。虽然不能使用函数作为事件处理过程,但可以用var参数得到返回信息。

在事件处理过程中传递var参数的典型例子是TKeyPressEvent类型的KeyPressed事件。TKeyPressEvent定义中含有两个参数:一个指示哪个对象产生该事件。另一个指示哪个键被按下:
type
  TKeyPressEvent=procedure(Sender:Tobject;var key:char) of object;
通常key参数包含用户按下键的字符。在某些情况下,组件的用户可能想改变字符值。例如在编辑器中强制所有字符为大写,在这种情况下,用户能定义下列的事件处理过程:

procedure TForm1.Edit1KeyPressed(Sender:TObject;var key:char);
begin
  key:=Upcase(key);
end;

也可以使用var参数让用户覆盖默认的处理。
4、事件处理过程是可选的

在为组件创建事件时要记住组件用户可能并不编写该事件的处理过程。这意味着你的组件不能因为组件用户木编写处理代码而出错。这种事件处理过程的可选性有两个方面:
(1)组件用户并非不得不处理事件

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

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

怎样实现标准事件
Delphi的所有组件继承了大多数windows事件,这些就是标准事件。尽管所有这些事件都嵌在标准组件中,但他们默认是protected,这意味着用户无法访问它们,当创建组件时,则可选择这些事件使用户可用。

标准事件有两种:用于所有组件和只用于标准windows组件
用于所有组件的事件:OnClick OnDragDrop OnEndDrag OnMouseMove OnDblClick OnDragOver OnMouseDown OnMouseUp

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

OnEnter OnKeyDown OnKeyPress OnKeyUp OnExit

标准事件的声明是protected的,如果想使用户在运行或设计时能访问它们,就需要将它们重声明为public和published。重声明属性而不描述它的实现将继承相同的实现方法,只是改变了访问级别

如果想修改自定义组件响应某种事件的方法,可以重写代码并将其赋给事件。将连接每个标准事件的方法声明为protected 是出于缜密的考虑。通过覆盖实现方法,能修改内部事件处理过程,通过调用继承方法,能保持标准事件处理过程。调用继承的方法的顺序很重要,一般首先调用继承的方法,允许用户的事件处理代码过程在你的定制代码前执行,然而也有在调用继承的方法之前执行自己的代码的情况。

:::如何为Delphi程序添加事件和事件处理器:::

---- Delphi是一种功能很强的可视化程序开发工具。我们在使用Delphi开发WINDOWS 应用程序的过程中,虽然Delphi为每个可视化组件都提供了很多属性(Property)和事件(Event),但在实际应用中可能会碰到一些自己需要的特殊事件,这些特殊事件Delphi 又没有提供,这时我们就需要为应用程序添加这些特殊事件。当这些事件发生后,又能马上调用处理这些事件的过程。本文通过实例来说明如何为应用程序添加事件和处理事件的过程。

---- 在Delphi中,事件实际上是专门化的属性,它是一个过程(procedure)的指针。要添加事件,首先应在所定义的类中说明一个用来指向事件过程的指针,该指针的作用是当事件一旦发生,就通过这个指针执行所指向的处理这个事件的过程。最后通过指定符 published公布定义的事件属性以及与之关联的事件处理过程指针。

---- 本例中,FtooBig为定义的事件处理过程指针,OnTooBig为事件属性名。事件处理过程指针FtooBig通过程序的初始化使之指向过程TooBig1。在Delphi的表单(Form1)上放置三个编辑框,分别为Edit1、Edit2和Edit3,放一按钮Button1。程序中设私有整型变量val1、val2和res,变量res用来记录val1和val2的乘积,并用Edit3显示出来。当通过Edit1和Edit2输入的数据有一个大于100时,会触发一个事件,并调用事件处理过程TooBig1显示一个对话框,说明此事件已经发生并已进行处理。源程序代码如下, 该程序在Delphi 3中调试通过。

unit Unit1;

inte**ce
uses

Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,

StdCtrls;
type

TForm1 = class(TForm)
Edit1: TEdit; {输入第一个整数}

Edit2: TEdit; {输入第二个整数}
Edit3: TEdit; {输出前二个整数的积}

Button1: TButton;
procedure Button1Click(Sender: TObject);

procedure TooBig1(Sender: TObject);
{当事件触发后调用此过程}

procedure FormCreate(Sender: TObject);
private

val1,val2,res:integer; {val1和val2存放输入
的两个整数,res存放两数的积}

FTooBig : TNotifyEvent; {定义一个指向事件
处理器的指针FTooBig}

{ Private declarations }
public

{ Public declarations }
published

property OnTooBig:TNotifyevent read
FTooBig write FTooBig;{定义事件}

end;

var
Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin

val1 := StrToInt(Edit1.Text);
val2 := StrToInt(Edit2.Text);

if(val1< 100)and(val2< 100) then
begin

res := val1*val2;
Edit3.Text := IntToStr(res);

end
else

if assigned(FTooBig) then OnTooBig(Self);
end;

procedure TForm1.TooBig1(Sender: TObject);

begin
Application.MessageBox('Too Big',' Test Event! ',MB_OK);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

val1:=1;
val2:=1;

FTooBig := TooBig1;{使事件处理指针指向事件处理器}
end;

end.