Delphi 7事件的多处理机制

Delphi 7事件的多处理机制
Allen Tao
2007-08-19

  首先解释一下这个题目。在我使用Delphi 7的过程中发现,一个对象的事件只能被一个过程处理。如果多次给这个对象的事件赋给处理事件的过程,最后真正处理事件的将是最后赋值的那个过程。例如,有类TMyClass中定义了一个事件OnSomeFired,在类TClientClass中该类被实例化,它的事件被处理。如下所示:
constructor TClientClass.Create;
var
  myObj: TMyClass;
begin
  //…
  myObj:= TMyClass.Create;
  myObj.OnSomeFired:= SomeFired1;
  myObj.OnSomeFired:= SomeFired2;
  这里的SomeFired1与SomeFired2都是TClientClass中定义的处理过程。其最终的结果是当OnSomeFired事件发生时,只有SomeFired2被调用。
  但在编程的实际中,往往需要一个事件被多个方法所处理。为此,我参考了一些对这个问题的解决办法,总结得出了一个自己的方法,称为事件的多处理机制。

原理
  Delphi 7中的事件本质上是一种过程指针。但事件类型在定义时要比一般过程指针在最后多一个“of object”,如常用的TNotifyEvent的定义是:
TNotifyEvent = procedure(Sender: TObject) of object;
  因此,给一个事件属性赋值,也就是给一个类的过程指针类型的成员变量赋值,当然是最后一次的赋值才有效。要想多次赋值有效就必须有一个数据结构把每次赋值赋给的过程指针变量都记录下来,最合适的数据结构当然是列表TList。但如果在每一个有事件的类中都加一个记录事件赋值的列表对象,自然是不方便的,而且使用这个列表的代码在不同类中也差不多,应该抽取出来形成一个类。这个类就是事件多处理机制的核心内容。

做法
  要记录事件处理过程,就需要将过程指针变量放入列表对象中。但列表对象只能添加指针类型对象(也就是引用类型),而过程指针变量是指类型变量,不能直接添加。这就需要有一个类对过程指针变量进行包装,转化为指针类型对象。于是,先要定义包装类TCallMethod,如下所示:
TCallMethod = class
  private
    _callback: TMethod;
  public
    property Callback: TMethod read _callback write _callback;
  end;
  这里的TMethod是Delphi预定义的记录类型,任何过程指针变量都可以强制转化为这种类型。之后,再定义记录处理过程的类,如下所示:
  TEventObject = class
  private
    callList: TList;
    function GetItem(i: Integer): TMethod;
    function GetCount: Integer;
  public
    constructor Create;

procedure Add(p: TMethod);
    procedure Remove(p: TMethod);

property Count: Integer read GetCount;
    property Items[i: Integer]: TMethod read GetItem;  default;
  end;
  下面是实现部分:
constructor TEventObject.Create;
begin
  callList:= TList.Create;
end;

procedure TEventObject.Add(p: TMethod);
var
  a: TCallMethod;
begin
  a:= TCallMethod.Create;
  a.Callback:= p;
  callList.Add(a);
end;

procedure TEventObject.Remove(p: TMethod);
var
  i: Integer;
begin
  for i:= 0 to GetCount - 1 do
    if (TCallMethod(callList[i]).Callback.Code = p.Code) and
      (TCallMethod(callList[i]).Callback.Data = p.Data) then
      callList.Delete(i);
end;

function TEventObject.GetCount: Integer;
begin
  Result:= callList.Count;
end;

function TEventObject.GetItem(i: Integer): TMethod;
var
  a: TCallMethod;
begin
  a:= TCallMethod(callList[i]);
  Result:= a.Callback;
end;
  从上面的代码可以看到,TEventObject的目的是包装TList对象,屏蔽了TList类的大多数方法,只对外暴露了2个过程、1个属性与1个索引,分别用于添加、删除处理事件过程、获得过程个数及通过序号检索记录的过程变量。

使用
  使用时,在需要使用事件的类中将事件的类型由过程指针改为TEventObject,即可使该事件拥有多处理的能力。如下面代码所示:
  TMyClass=class
  private
    someFired: TEventObject;
  public
    constructor Create;
    procedure DoSth;
    property OnSomeFired: TEventObject read someFired write someFired; // 多处理事件
  end;
  以下是实现代码:
constructor TMyClass.Create;
begin
  Self.someFired:= TEventObject.Create;
end;

procedure TMyClass.DoSth;
var
  i: Integer;
  method: TNotifyEvent;
begin
  if someFired.Count > 0 then
    for i:= 0  to someFired.Count - 1 do
    begin
      method:= TNotifyEvent(someFired[i]); // 在该类中事件的真正类型是TNotifyEvent,因此在触发事件时先要转成这种类型的过程指针后再进行调用
      method(Self);
    end;
end;
  定义了一个包含多处理事件的类后,再看看这种类如果在其客户类中被调用。如以下代码:
{类TClientClass中}
var
  myObj: TMyClass;
  //…
  myObj:= TMyClass.Create;
  myObj.OnSomeFired.Add(SomeFired1);
  myObj.OnSomeFired.Add(SomeFired2);
  当客户类代码中调用TMyClass的DoSth方法时,事件将被触发,而2个处理过程SomeFired1与SomeFired2则会依次调用,实现多处理的目的。

再发展一步
  上面的TEventObject类可以实现事件多处理的目的,但它对加入其中的过程指针类型没有检查,这是一个隐患。因此可以针对每一种事件要求的过程指针类型从TEventObject继承一个类,实现类型检查。如要求事件的类型是TNotifyEvent,就可以继承一个TNotifyEventObject类,如下面代码:
  TNotifyEventObject = class(TEventObject)
  public
    procedure Add(p: TNotifyEvent); overload;
    procedure Remove(p: TNotifyEvent); overload;
  end;
  以下是实现部分:
procedure TNotifyEventObject.Add(p: TNotifyEvent);
begin
  inherited Add(TMethod(p));
end;

procedure TNotifyEventObject.Remove(p: TNotifyEvent);
begin
  inherited Remove(TMethod(p));
end;
  在这个类中重载了添加与移除方法,可以有效地对过程指针类型进行检查。

  以上是我对Delphi 7中事件多处理问题的一个解决办法。如有兴趣,请光临我的博客http://allentao430.spaces.live.com赐教。

参考:http://blog.csdn.net/iseekcode/article/details/5352709

时间: 2024-10-25 07:11:35

Delphi 7事件的多处理机制的相关文章

左右JAVA示例代码事件分发和监督机制来实现-绝对原创有用

文章标题:左右JAVA示例代码事件分发和监督机制来实现 文章地址: http://blog.csdn.net/5iasp/article/details/37054171 作者: javaboy2012Email:[email protected]qq:    1046011462 一.场景如果 如果有博客系统中须要实现例如以下功能: 系统中用户公布文章.改动文章.删除文章时,须要一些相关的操作须要运行. 公布文章后,给好友发送邮件通知.给用户加积分,对文章做全文索引. 改动文章后,给好友发送邮

delphi之事件

delphi的事件如上图所示: 图中oncloseup代表的是日期选择下拉框关闭时触发的事件. //事件定义 procedure Ondatechange(Sender: TObject); //事件执行: procedure TFrmSellReturnTicket.Ondatechange(Sender: TObject); begin if ((Sender as TComponent).Name = 'dtpstartdate') or ((Sender as TComponent).N

android中的事件传递和处理机制

一直以来,都被android中的事件传递和处理机制深深的困扰!今天特意来好好的探讨一下.现在的感觉是,只要你理解到位,其实事件的 传递和处理机制并没有想象中的那么难.总之,不要自己打击自己,要相信自己能掌握这块知识.好了,下面是我今天的收获,希望也 能对你有一点帮助. 一.拟人化来理解android中的事件机制 其实android中的事件传递与处理机制跟我们生活中的事件处理是一样的.这里有一个生活中的例子,很能说明这个问题.阐述如下: 你是一个公司的员工,你的上头有一个主管,主管上头呢还有一个经

图片会说话系列之Android事件的分发传递机制

在一些复杂布局中,经常会遇到事件冲突,事件失效等问题,这就需要我们深入理解Android事件的分发传递机制.最好的方法是自己写一个demo,打印事件相关的日志查看其运行流程,然后再结合源码去深入理解.当然这里不会做这些,这里只有总结性的东西,如果你喜欢这种东西,那么请继续往下看. 老规矩,先上图: 用户的交互一般发生在触摸屏上,因此Android事件的传递必然涉及到ViewGroup和View,ViewGroup与View包含的处理事件传递的方法分别如下: ViewGroup: <span st

基于CSS class的事件监听管理机制 (转)

背景: 做了那么多web项目,总会发现到处都是事件绑定,同一个按钮的执行动作,也许会分布在多个js文件中. 而且对于js动态生成的文档片段,里面会经常出现“onclick=...”之类的代码,一到功能升级,或者代码重构的时候, 就会发现,这个难度以及工作量,和重写一遍没什么区别,有时候甚至工作量更大! 基于各种情况的分析.以及以往的经验总结,百度空间则有了一套自己的事件监听管理机制:基于CSS class的事件监听管理机制 方案: 1.js代码中,不出现对某节点的事件监听,如:$('#elm')

【朝花夕拾】Android自定义View篇之(五)Android事件分发及传递机制

前言 在自定义View中,经常需要处理Android事件分发的问题,尤其在有多个输入设备(如遥控.鼠标.游戏手柄等)时,事件处理问题尤为突出.Android事件分发机制,一直以来都是一个让众多开发者困扰的难点,至少笔者在工作的前几年中,没有特意研究它之前,就经常云里雾里.实际上,该问题的“七寸”就是dispatchTouchEvent(MotionEvent ev).onInterceptTouchEvent(MotionEvent ev).onTouchEvent(MotionEvent ev

深入理解Spring的容器内事件发布监听机制

目录 1. 什么是事件监听机制 2. JDK中对事件监听机制的支持 2.1 基于JDK实现对任务执行结果的监听 3.Spring容器对事件监听机制的支持 3.1 基于Spring实现对任务执行结果的监听 4.Spring事件监听源码解析 4.1 初始化事件发布器流程 4.2 注册事件监听器流程 4.3 容器事件发布流程 5.总结 1. 什么是事件监听机制 在讲解事件监听机制前,我们先回顾下设计模式中的观察者模式,因为事件监听机制可以说是在典型观察者模式基础上的进一步抽象和改进.我们可以在JDK或

delphi VCL研究之消息分发机制(转)

原文来源,http://blog.csdn.net/sushengmiyan/article/details/8635550 1.VCL 概貌 先看一下VCL类图的主要分支,如图4.1所示.在图中可以看到,TObject是VCL的祖先类,这也是Object Pascal语言所规定的.但实际上,TObject以及TObject声明所在的system.pas整个单元,包括在“编译器魔法”话题中提到的_ClassCreate等函数,都是编译器内置支持的.因此,无法修改.删除system.pas中的任何

android中的事件分发和消费机制

一.思维导图 查看大图:http://img.blog.csdn.net/20150524191211931 二.更多参考 Android中的Touch事件处理:http://www.cnblogs.com/mengdd/p/3394345.html Android 编程下 Touch 事件的分发和消费机制 :http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html Android事件分发完全解析之为什么是她:http://blog