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 ...
随机推荐
- DML,DDL,DCL,DQL的区别
DML 英文缩写 DML = Data Manipulation Language,数据操纵语言,命令使用户能够查询数据库以及操作已有数据库中的数据的计算机语言.具体是指是UPDATE更新.INS ...
- 图片放大缩小(和ViewPager配合使用流畅显示)--第三方开源--PhotoView
图片的放大缩小实现效果是使用的github上的一个开源项目photoView实现的,下载地址:https://github.com/chrisbanes/PhotoView 下面看测试代码: acti ...
- 11g RAC R2 体系结构---Grid
基于agent的管理方式 从oracle 11.2开始出现了多用户的概念,oracle开始使用一组多线程的daemon来同时支持多个用户的使用.管理资源,这些daemon叫做Agent.这些Agent ...
- jquery冒泡及阻止
javascript, jquery的事件中都存在事件冒泡和事件捕获的问题,下面将两种问题及其解决方案做详细总结. 事件冒泡是一个从子节点向祖先节点冒泡的过程: 事件捕获刚好相反,是从祖先节点到子节点 ...
- 未能加载文件或程序集“Report.Basic”或它的某一个依赖项。试图加载格式不正确的程序
出现问题如下: 解决办法: 这是由于没有开启32位程序兼容模式 具体操作如下:找到对应的程序池--------高级设置-------修改“启用32位应用程序”状态修改为true
- Yii2 composer更新后报错: Calling unknown method: yii\web\UrlManager::addRules()
网上搜了好多答案,基本上都是让你在composer.json里面添加这样一段话: "extra": { "asset-installer-paths": { & ...
- 什么是XAML?
XAML类似于XML一样的一种标记语言,主要用来设计UI. 对于XAML的历史,哪些都是废话了,至于Microsoft怎么整出XAML,然后,又怎么让XAML来开发windows 8应用程序,这些,都 ...
- 典型的js页面
var myurl; var mydata; var postype = "POST"; var getype = "GET"; var jsontype = ...
- C#系统缓存全解析
原文:http://blog.csdn.net/wyxhd2008/article/details/8076105 目录(?)[-] 系统缓存的概述 页面输出缓存 页面局部缓存 文件缓存依赖 数据库缓 ...
- Python Socket File Transfer
I have a RPi which I intented to use it to crawl data. The development environment in RPi is very ba ...