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
Delphi 7事件的多处理机制的更多相关文章
- 通俗理解Android事件分发与消费机制
深入:Android Touch事件传递机制全面解析(从WMS到View树) 通俗理解Android事件分发与消费机制 说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与Li ...
- delphi之事件
delphi的事件如上图所示: 图中oncloseup代表的是日期选择下拉框关闭时触发的事件. //事件定义 procedure Ondatechange(Sender: TObject); //事件 ...
- Javascript事件模型系列(二)事件的捕获-冒泡机制及事件委托机制
一.事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监 ...
- android中的事件传递和处理机制
一直以来,都被android中的事件传递和处理机制深深的困扰!今天特意来好好的探讨一下.现在的感觉是,只要你理解到位,其实事件的 传递和处理机制并没有想象中的那么难.总之,不要自己打击自己,要相信自己 ...
- Delphi动态事件深入分析(对象方法在调用的时候会传递一个隐含的Self指针,而该指针的值在EAX中。即左边第一个参数)
Delphi动态事件深入分析 2009-2-7 作者:不得闲核心提示:本实验证明了在类中方法的调用时候,所有的方法都隐含了一个Self参数,并且该参数作为对象方法的第一个参数传递... 首先做一个空窗 ...
- WM_SYSCOMMAND包括很多功能,比如:拖动左边框、拖动标题栏、滚动条滚动、点击最小化、双击标题栏——Delphi 通过事件代替了大部分常用的消息,所以Delphi 简单、易用、高效
procedure TForm1.WMSysCommand(var Message: TWMSysCommand); var str: string; begin case Message.CmdTy ...
- 左右JAVA示例代码事件分发和监督机制来实现-绝对原创有用
文章标题:左右JAVA示例代码事件分发和监督机制来实现 文章地址: http://blog.csdn.net/5iasp/article/details/37054171 作者: javaboy201 ...
- 深入理解Spring的容器内事件发布监听机制
目录 1. 什么是事件监听机制 2. JDK中对事件监听机制的支持 2.1 基于JDK实现对任务执行结果的监听 3.Spring容器对事件监听机制的支持 3.1 基于Spring实现对任务执行结果的监 ...
- 【spring源码学习】spring的事件发布监听机制源码解析
[一]相关源代码类 (1)spring的事件发布监听机制的核心管理类:org.springframework.context.event.SimpleApplicationEventMulticast ...
随机推荐
- php中echo、print、print_r、printf的返回值
1.echo 无返回值,是一个语言结构.在输出多个参数时不可以使用小括号; 2.print返回值为1:如:$x = 0; echo print $x."<br/>";/ ...
- CSS各个浏览器Hack的写法
Hack是针对不同的浏览器去写不同的CSS样式,从而让各浏览器能达到一致的渲染效果,那么针对不同的浏览器写不同的CSS CODE的过程,就叫CSS HACK,同时也叫写CSS Hack.然后将Hack ...
- 查找指定表的字段not null约束,并生成删除Sql
SElECT 'ALTER TABLE '+OBJECT_NAME(c.parent_obj)+' DROP CONSTRAINT '+ c.name FROM sys.sysconstraints ...
- ASP.NET对HTML元素进行权限控制(一)
一个HTML页面有很多的元素比如<DIV>,<P>等.这些元素构成了HTML页面.在Web开发中权限控制是每个系统都要用到了.界面每个元素的权限也是需要控制的.比如一个查询用户 ...
- yabeblog.me 关于Tomcat7部署 一台机器部署两个项目,一个用域名访问,一个用IP访问
该内容来自 http://www.yabeBlog.me,转载请说明出处. 1.使用IP访问的项目放在Tomcat7 的webapps目录下面:比如:AAA 2.使用域名访问的项目放在Tomcat7的 ...
- centos 格式化硬盘并挂载,添加重启后生效
[root@cloud /]# passwd 更改用户 root 的密码 . 新的 密码: 重新输入新的 密码: passwd: 所有的身份验证令牌已经成功更新. [root@cloud /]# fd ...
- 点击图标 标记为星标记事mac中修改默认的apache网站根目录位置
在Mac OS X中可以很方便的通过开启“Web共享”启用Apache服务:设置方法如下: 打开“系统设置偏好(System Preferences)” -> “共享(Sharing)” -&g ...
- 微软职位内部推荐-SDE2 (Windows driver)
微软近期Open的职位: SDE2 (Windows driver) Job title: Software Development Engineer 2 Location: Shanghai, Ch ...
- select * from table where 1=1
转自:http://www.dzwebs.net/2418.html 我们先来看看这个语句的结果:select * from table where 1=1,其中where 1=1,由于1=1永远是成 ...
- 客户端通过spice-gtk实现USB重定向
1.安装必要的工具: sudo apt-get install build-essential autoconf git-core intltool 2.安装必要的依赖包: -dev libxfixe ...