Delphi中实现事件“注入”
2007-01-22 23:37
295 查看
C#中,事件是以委托的形式出现的,我们可以使用+=操作符来实现一个组件中同一事件的串联。但是,在Delphi中,事件是以procedure of Object的形式出现的,没有+=操作符的支持,也就是说,一个组件的事件只能指向一个函数地址(C#中的委托使用了指针列表来保存委托,因此可以实现多个事件的挂接)。那么,有没有一个变通的方法来间接实现事件的随意“注入”(即不影响原有事件,把自己的多个事件插入到事件执行队列,或者一次插入到多个事件中)呢?答案是肯定的。好,让我们一起来。
所谓事件,实质是一个实现定义了形式的函数指针,那么既然是指针,就可以用地址来表示。
比如,有一个按钮,我们想在它的OnClick事件注入我们的代码,那么可以这样:
//这里是全局变量
var
temp: TNotifyEvent;
//下面是实现
begin
temp := Button.OnClick; //把原有事件保留到临时的变量
Button.OnClick := myClick; //用我们的事件替换之
end;
//在myClick事件函数中写入
if Assigned(temp) then
temp(Sender);
这样,我们成功的使用自己的事件“注入”了原有事件。
这是在组件或者事件个数明确的情况下实现的,那么组件的个数不明确的话我们该怎么办呢?其实,我们完全可以使用指针数组/列表来实现事件的“注入”,只是其中使用了一个小小的转换来保证类型的一致。
比如,有一个PageControl控件,我们需要注入到每一个TabSheet的OnShow中,而对于TabSheet的个数我们不明确,那么可以这样:
//定义两个列表
var
OnTabSheetShowCodeList: TList; //用于保存TMethod.Code的内容
OnTabSheetShowDataList: TList; //用于保存TMethod.Data的内容
//在注入代码实现中
var
os: TMethod;
begin
for i := 0 to PageControl.PageCount -1 do
begin
os := TMethod(PageControl.Pages[i].OnShow); //获取指定TabSheet的OnShow事件,并将其转换成TMethod类型,这里是关键
OnTabSheetShowCodeList.Add(os.Code); //保存该函数指针的地址
OnTabSheetShowDataList.Add(os.Data); //保存该函数指针的额外数据
PageControl.Pages[i].OnShow := myOnShow; //使用我们自己的事件函数来替换之
end;
end;
//那么在myOnShow中
var
onShow: TNotifyEvent;
begin
if (Sender <>nil) and(Assigned((Sender as TTabSheet).PageControl)) then
begin
TMethod(onShow).Code := (FOnTabSheetShowCodeList.Items[(Sender as TTabSheet).pageIndex]); //恢复函数指针的地址
TMethod(onShow).Data := (FOnTabSheetShowDataList.Items[(Sender as TTabSheet).pageIndex]); //恢复函数指针的额外数据
if (Assigned(onShow)) then
begin
onShow(Sender); //调用原始事件
end;
end;
end;
上面代码最不容易理解的是 TMethod(onShow).Data 这个变量,网上很多例子都对它赋予了Self,但是在本例中,赋予Self反而会在调用原始事件时出错,原因很简单,TMethod.Data是对象实例(Instance)的首地址,TMethod的结构定义方式就是目前O-O语言实现的原理。Code和Data分离,Code表示类的首地址,Data表示类的实例的首地址。方法表示对Code的偏移量,字段(Field)表示对Data的偏移量。因此,如果赋予其Self的话,在调用原始事件时就会出现原始调用者指针丢失,造成内存访问异常。
所谓事件,实质是一个实现定义了形式的函数指针,那么既然是指针,就可以用地址来表示。
比如,有一个按钮,我们想在它的OnClick事件注入我们的代码,那么可以这样:
//这里是全局变量
var
temp: TNotifyEvent;
//下面是实现
begin
temp := Button.OnClick; //把原有事件保留到临时的变量
Button.OnClick := myClick; //用我们的事件替换之
end;
//在myClick事件函数中写入
if Assigned(temp) then
temp(Sender);
这样,我们成功的使用自己的事件“注入”了原有事件。
这是在组件或者事件个数明确的情况下实现的,那么组件的个数不明确的话我们该怎么办呢?其实,我们完全可以使用指针数组/列表来实现事件的“注入”,只是其中使用了一个小小的转换来保证类型的一致。
比如,有一个PageControl控件,我们需要注入到每一个TabSheet的OnShow中,而对于TabSheet的个数我们不明确,那么可以这样:
//定义两个列表
var
OnTabSheetShowCodeList: TList; //用于保存TMethod.Code的内容
OnTabSheetShowDataList: TList; //用于保存TMethod.Data的内容
//在注入代码实现中
var
os: TMethod;
begin
for i := 0 to PageControl.PageCount -1 do
begin
os := TMethod(PageControl.Pages[i].OnShow); //获取指定TabSheet的OnShow事件,并将其转换成TMethod类型,这里是关键
OnTabSheetShowCodeList.Add(os.Code); //保存该函数指针的地址
OnTabSheetShowDataList.Add(os.Data); //保存该函数指针的额外数据
PageControl.Pages[i].OnShow := myOnShow; //使用我们自己的事件函数来替换之
end;
end;
//那么在myOnShow中
var
onShow: TNotifyEvent;
begin
if (Sender <>nil) and(Assigned((Sender as TTabSheet).PageControl)) then
begin
TMethod(onShow).Code := (FOnTabSheetShowCodeList.Items[(Sender as TTabSheet).pageIndex]); //恢复函数指针的地址
TMethod(onShow).Data := (FOnTabSheetShowDataList.Items[(Sender as TTabSheet).pageIndex]); //恢复函数指针的额外数据
if (Assigned(onShow)) then
begin
onShow(Sender); //调用原始事件
end;
end;
end;
上面代码最不容易理解的是 TMethod(onShow).Data 这个变量,网上很多例子都对它赋予了Self,但是在本例中,赋予Self反而会在调用原始事件时出错,原因很简单,TMethod.Data是对象实例(Instance)的首地址,TMethod的结构定义方式就是目前O-O语言实现的原理。Code和Data分离,Code表示类的首地址,Data表示类的实例的首地址。方法表示对Code的偏移量,字段(Field)表示对Data的偏移量。因此,如果赋予其Self的话,在调用原始事件时就会出现原始调用者指针丢失,造成内存访问异常。
相关文章推荐
- Delphi实现Android 广播事件监听(动态注册广播)
- 利用RTTI实现Delphi的多播事件代理研究
- Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 ),利用RTTI实现Delphi的多播事件代理研究
- delphi多线程编程中利用事件机制同步的初步实现
- android平台上编程实现事件注入 part1
- 【android学习】安卓设备实现触摸事件的监听,跨进程事件注入
- js实现手机横竖屏事件
- javascript实现禁止鼠标滚轮事件
- ssh,spring 泛型注入,泛型dao和service的实现
- 原来DELPHI里面实现数据列表框的列排序是这么简单?
- Delphi中如何实现透明按钮 也可用于BCB
- 基于Delphi的三层数据库系统的实现方法
- DELPHI中用MSCOMM实现串口通信
- 在一个form表单中根据不同按钮实现多个action事件
- 一周乱弹(1,摸态框增加纵向滚动条2,jQuery 鼠标点击事件3,通过ajax实现批量导出。4,windows查询占用某个端口号程序并杀死其进程)
- Delphi实现菜单项上出现提示
- Delphi主从窗体调用的实现
- iOS控制反转(IoC)与依赖注入(DI)的实现
- 鼠标移入移出事件的多种方法实现
- 用DELPHI实现对象持久层(-)