这是它的声明,它的数据成员全部都是Event,而没有真正意义上的数据(如此一来,几乎可以猜测,它本身什么都做不了):

  TCustomApplicationEvents = class(TComponent)
private
FOnActionExecute: TActionEvent;
FOnActionUpdate: TActionEvent;
FOnException: TExceptionEvent;
FOnMessage: TMessageEvent;
FOnHelp: THelpEvent;
FOnHint: TNotifyEvent;
FOnIdle: TIdleEvent;
FOnDeactivate: TNotifyEvent;
FOnActivate: TNotifyEvent;
FOnMinimize: TNotifyEvent;
FOnRestore: TNotifyEvent;
FOnShortCut: TShortCutEvent;
FOnShowHint: TShowHintEvent;
FOnSettingChange: TSettingChangeEvent;
FOnModalBegin: TNotifyEvent;
FOnModalEnd: TNotifyEvent;
procedure DoActionExecute(Action: TBasicAction; var Handled: Boolean);
procedure DoActionUpdate(Action: TBasicAction; var Handled: Boolean);
procedure DoActivate(Sender: TObject);
procedure DoDeactivate(Sender: TObject);
procedure DoException(Sender: TObject; E: Exception);
procedure DoIdle(Sender: TObject; var Done: Boolean);
function DoHelp(Command: Word; Data: Longint; var CallHelp: Boolean): Boolean;
procedure DoHint(Sender: TObject);
procedure DoMessage(var Msg: TMsg; var Handled: Boolean);
procedure DoMinimize(Sender: TObject);
procedure DoRestore(Sender: TObject);
procedure DoShowHint(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo);
procedure DoShortcut(var Msg: TWMKey; var Handled: Boolean);
procedure DoSettingChange(Sender: TObject; Flag: Integer; const Section: string; var Result: Longint);
procedure DoModalBegin(Sender: TObject);
procedure DoModalEnd(Sender: TObject);
protected
property OnActionExecute: TActionEvent read FOnActionExecute write FOnActionExecute;
property OnActionUpdate: TActionEvent read FOnActionUpdate write FOnActionUpdate;
property OnActivate: TNotifyEvent read FOnActivate write FOnActivate;
property OnDeactivate: TNotifyEvent read FOnDeactivate write FOnDeactivate;
property OnException: TExceptionEvent read FOnException write FOnException;
property OnIdle: TIdleEvent read FOnIdle write FOnIdle;
property OnHelp: THelpEvent read FOnHelp write FOnHelp;
property OnHint: TNotifyEvent read FOnHint write FOnHint;
property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
property OnMinimize: TNotifyEvent read FOnMinimize write FOnMinimize;
property OnRestore: TNotifyEvent read FOnRestore write FOnRestore;
property OnShowHint: TShowHintEvent read FOnShowHint write FOnShowHint;
property OnShortCut: TShortCutEvent read FOnShortCut write FOnShortCut;
property OnSettingChange: TSettingChangeEvent read FOnSettingChange write FOnSettingChange;
property OnModalBegin: TNotifyEvent read FOnModalBegin write FOnModalBegin;
property OnModalEnd: TNotifyEvent read FOnModalEnd write FOnModalEnd;
public
constructor Create(AOwner: TComponent); override;
procedure Activate;
procedure CancelDispatch;
end;

它的构造函数平淡无奇:

constructor TCustomApplicationEvents.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if Assigned(MultiCaster) then
MultiCaster.AddAppEvent(Self);
end; procedure TMultiCaster.AddAppEvent(AppEvent: TCustomApplicationEvents);
begin
if FAppEvents.IndexOf(AppEvent) = - then
FAppEvents.Add(AppEvent);
end;

它的秘密在于:一旦使用了这个控件,那么就会引入AppEvents单元,因此会执行:

initialization
GroupDescendentsWith(TCustomApplicationEvents, Controls.TControl);
MultiCaster := TMultiCaster.Create(Application);
end.

其中GroupDescendentsWith函数来自classes.pas单元:

procedure GroupDescendentsWith(AClass, AClassGroup: TPersistentClass);
begin
RegGroups.Lock;
try
RegGroups.GroupWith(AClass, AClassGroup);
finally
RegGroups.Unlock;
end;
end;

而MultiCaster是AppEvents.pas的全局变量:

var
MultiCaster: TMultiCaster = nil;

其实就是靠创建MultiCaster的时候进行对接:

constructor TMultiCaster.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FAppEvents := TComponentList.Create(False);
with Application do
begin
OnActionExecute := DoActionExecute;
OnActionUpdate := DoActionUpdate;
OnActivate := DoActivate;
OnDeactivate := DoDeactivate;
OnException := DoException;
OnHelp := DoHelp;
OnHint := DoHint;
OnIdle := DoIdle;
OnMessage := DoMessage;
OnMinimize := DoMinimize;
OnRestore := DoRestore;
OnShowHint := DoShowHint;
OnShortCut := DoShortcut;
OnSettingChange := DoSettingChange;
OnModalBegin := DoModalBegin;
OnModalEnd := DoModalEnd;
end;
end;

它对消息的处理就是转发,这里举三个例子(OnMessage,OnMinimize,OnException):

procedure TCustomApplicationEvents.DoMessage(var Msg: TMsg; var Handled: Boolean);
begin
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
end; procedure TMultiCaster.DoMessage(var Msg: TMsg; var Handled: Boolean);
var
I: Integer;
begin
BeginDispatch;
try
for I := Count - downto do
begin
AppEvents[I].DoMessage(Msg, Handled);
if FCancelDispatching then Break;
end;
finally
EndDispatch;
end;
end; procedure TCustomApplicationEvents.DoMinimize(Sender: TObject);
begin
if Assigned(FOnMinimize) then FOnMinimize(Sender);
end; procedure TMultiCaster.DoMinimize(Sender: TObject);
var
I: Integer;
begin
BeginDispatch;
try
for I := Count - downto do
begin
AppEvents[I].DoMinimize(Sender);
if FCancelDispatching then Break;
end;
finally
EndDispatch;
end;
end; procedure TCustomApplicationEvents.DoException(Sender: TObject;
E: Exception);
begin
if not (E is EAbort) and Assigned(FOnException) then
FOnException(Sender, E)
end; procedure TMultiCaster.DoException(Sender: TObject; E: Exception);
var
I: Integer;
FExceptionHandled: Boolean;
begin
BeginDispatch;
FExceptionHandled := False;
try
for I := Count - downto do
begin
if Assigned(AppEvents[I].OnException) then
begin
FExceptionHandled := True;
AppEvents[I].DoException(Sender, E);
if FCancelDispatching then Break;
end;
end;
finally
if not FExceptionHandled then
if not (E is EAbort) then
Application.ShowException(E);
EndDispatch;
end;
end;

其实就是这么简单,这个AppEvents.pas单元连finalization模块都没有。。。

不过说真的,这个控件简单实用,在看它的析构函数的时候,我忽然有点明白了,为什么TApplication要处理这么多消息,原本它应该没有机会处理的嘛:

destructor TMultiCaster.Destroy;
begin
MultiCaster := nil;
with Application do
begin
OnActionExecute := nil;
OnActionUpdate := nil;
OnActivate := nil;
OnDeactivate := nil;
OnException := nil;
OnHelp := nil;
OnHint := nil;
OnIdle := nil;
OnMessage := nil;
OnMinimize := nil;
OnRestore := nil;
OnShowHint := nil;
OnShortCut := nil;
OnSettingChange := nil;
OnModalBegin := nil;
OnModalEnd := nil;
end;
FAppEvents.Free;
inherited Destroy;
end;

最后还发现,TApplicationEvents终于也不甘示弱,终于自己处理了一个消息,这可真是不容易呀。但是我搞不明白,为什么会有这里例外,而且在TMultiCaster里同样有定义:

procedure TCustomApplicationEvents.DoHint(Sender: TObject);
begin
if Assigned(FOnHint) then
FOnHint(Sender)
else
with THintAction.Create(Self) do
try
Hint := Application.Hint;
Execute;
finally
Free;
end;
end; procedure TMultiCaster.DoHint(Sender: TObject);
var
I: Integer;
begin
BeginDispatch;
try
for I := Count - downto do
begin
AppEvents[I].DoHint(Sender);
if FCancelDispatching then Break;
end;
finally
EndDispatch;
end;
end;

TApplicationEvents的前世今生(待续)的更多相关文章

  1. 【调侃】IOC前世今生

    前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...

  2. [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性

    回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...

  3. javascript有用小功能总结(未完待续)

    1)javascript让页面标题滚动效果 代码如下: <title>您好,欢迎访问我的博客</title> <script type="text/javasc ...

  4. docker4dotnet #1 – 前世今生 & 世界你好

    作为一名.NET Developer,这几年看着docker的流行实在是有些眼馋.可惜的是,Docker是基于Linux环境的,眼瞧着那些 java, python, node.js, go 甚至连p ...

  5. ASP.NET MVC 系列随笔汇总[未完待续……]

    ASP.NET MVC 系列随笔汇总[未完待续……] 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的. 学前篇之: ASP.NET MVC学前篇之扩展方法.链式编程 ASP. ...

  6. Atitit 智能云网络摄像机的前世今生与历史 优点  密码默认888888

    Atitit 智能云网络摄像机的前世今生与历史 优点  密码默认888888 用户名admin  密码aaaaaa 网络摄像机是一种结合传统摄像机与网络技术所产生的新一代摄像机,它可以将影像通过网络传 ...

  7. 关于DOM的一些总结(未完待续......)

    DOM 实例1:购物车实例(数量,小计和总计的变化) 这里主要是如何获取页面元素的节点: document.getElementById("...") cocument.query ...

  8. 阿里开源消息中间件RocketMQ的前世今生-转自阿里中间件

    昨天,我们将分布式消息中间件RocketMQ捐赠给了开源软件基金会Apache. 孵化成功后,RocketMQ或将成为国内首个互联网中间件在Apache上的顶级项目. 消息一出,本以为群众的反应是这样 ...

  9. JavaScript的前世今生

    和CSS一样,JavaScript在各浏览器下并非完全一致,它所带来的兼容性问题时常困扰着我们,以至于现在“能否处理流行浏览器的兼容性问题”成为了检验一个程序员是否合格的标准之一.了解JavaScri ...

随机推荐

  1. 收集 天创恒达高清采集卡TC-5A0N7

    版权声明:本文博客琅邪工作室原创文章,博客,未经同意不得转载.

  2. 不安装 oracle的客户,就可以使用pl/sql访问远程oracle 数据库的方法

    免安装Oracle客户端使用PL/SQL连接Oracle      大家都知道,用PL/SQL连接Oracle,是需要安装Oracle客户端软件的.有没要想过不安装Oracle客户端直接连接Oracl ...

  3. matlab 高级函数 —— circshift、squeeze

    circshift:顾名思义,循环移动,循环的意义在于,移出的数据不丢失,而是来到队列的首部位置,也即其实是将原始序列视为一种圆环. 1. 基本用法 默认为右移. Y = circshift(A,K) ...

  4. C#颜色对照使用表

    这篇文章来来源于C# Color Table,这里是我翻译的中文版本,其中已经加上了我的一些理解和注释.翻译这篇文章的原因是我在写C#程序的时候发现,C#自带的颜色种类极多(详见下表),如果没有直观的 ...

  5. QT之QSignalMapper(可以理解为转发器,多个按钮绑定到一个Edit上,且能分辨。每个单独连接的话,反而麻烦)

    QT之QSignalMapper QT之QSignalMapper 简述 效果图 上代码 相关知识点文章 结尾 简述 QSignalMapper我们可以理解为转发器,此话怎讲呢?比如,按钮点击的响应槽 ...

  6. Android中集成支付宝

    手机的在线支付,被认为是2012年最看好的功能,我个人认为这也是移动互联网较传统互联网将会大放光彩的一个功能. 人人有手机,人人携带手机,花钱买东西,不再需要取钱付现,不再需要回家上网银,想买什么,扫 ...

  7. SpringMVC+easyUI 分页,查询 (完整的CRUD)

    最终完毕CRUD的功能了,注意,这里会对前面有一些修改,UserController的listUser() 已经改写了,如今把所有整理一下吧. JSP: <%@ page language=&q ...

  8. OpenGL(二十三) 各向异性纹理过滤

    如果使用一般的纹理过滤,当观察方向跟模型表面不是相互垂直的的情况下,会出现纹理信息的丢失,表现为图像看上去比较模糊,如下图所示,远处场景的细节信息很差: 针对这种情况,可以采用同向异性过滤的方式处理纹 ...

  9. HDU 3360 National Treasures 奇偶匹配的最低点覆盖

    标题来源:pid=3360">HDU 3360 National Treasures 意甲冠军:假设a[i][j] != -1 把他转成二进制 最多有12位 代表题目那张图的12个位置 ...

  10. WPF与缓动(三) 指数缓动

    原文:WPF与缓动(三) 指数缓动 WPF与缓动(三) 指数缓动                                                                     ...