明天就是五一节了,辛苦了好几个月,借此机会应该尽情放松一番。可是想到Blog好久没有写文章,似乎缺些什么似的。这几个月来在项目中又增长了许多经验,学到许多实际应用的知识。不如把一些比较有用的记录下来,供朋友们参考可好。
我想到Delphi的事件,那可真是个方便的东西,初学者在窗体上拉几个控件,并指定它们的事件,写几句代码,立刻就得到他们想要的效果。可是事件在方便的同时也有一个不足之处,就是只能指定一个接收事件的对象,这在某些应用中会受收限制,比如多视图对应一个业务逻辑时,当一个业务对象想通知视图更新它们的状态,如果用事件,那只能有一个视图得到通知。
有没有办法让对象触发事件时,多个对象同时能收到呢?其实仔细一想,还是有挺多的,根本的就是维护一张接收事件对象的列表,事件发生时,遍历列表并调用相应的方法。本文介绍两种方法,这两种方法都比较好用。
第一种方法是从ApplicationEvents控件的实现方式学来的。ApplicationEvents是为了方便地处理Application的所有事件,一个程序中放多个ApplicationEvents,它们都能同时传递Application的事件到事件接收类中,下面是一个例子,在一个窗体上放两个ApplicationEvents控件,并指定它们的OnMessage事件,并写如下代码:
procedureTForm1.ApplicationEvents1Message(varMsg:tagMSG;
varHandled:Boolean);
begin
Edit1.Text:=IntToStr(i1);
Inc(i1);
end;
procedureTForm1.ApplicationEvents2Message(varMsg:tagMSG;
varHandled:Boolean);
begin
Edit2.Text:=IntToStr(i2);
Inc(i2);
end;
运行程序,可以看到两个事件处理方法都发生了,i1和i2疯狂的增长。也就是说Application通过ApplicationEvents这个控件使得它的事件可以被多个对象同时接收,显然ApplicationEvents不是简单地传递Application的事件,一定是运用了某些技巧,看看它的源码如何。
打开AppEvnts这个单元,发现里面的代码并不多,在初始节中有这样的代码:
initialization
... ...
MultiCaster:=TMultiCaster.Create(Application);
end.
MultiCaster是TMultiCaster类的一个全局对象,构造函数传进Appication对象,可以肯定,在里面MultiCaster将接收Application的所有事件,看看源码就知道了。
constructorTMultiCaster.Create(AOwner:TComponent);
begin
inheritedCreate(AOwner);
FAppEvents:=TComponentList.Create(False);
withApplicationdo
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;
上面也可以看到有一个FAppEvents列表类,它应该就是保存所有的ApplicationEvents的列表,再看看ApplicationEvents的构造函数。
constructorTCustomApplicationEvents.Create(AOwner:TComponent);
begin
inheritedCreate(AOwner);
ifAssigned(MultiCaster)then
MultiCaster.AddAppEvent(Self);
end;
每创建一个ApplicationEvents,它就将自己加进MultiCaster全局对象的列表中。
procedureTMultiCaster.AddAppEvent(AppEvent:TCustomApplicationEvents);
begin
ifFAppEvents.IndexOf(AppEvent)=-1then
FAppEvents.Add(AppEvent);
end;
事情已经很明白了,每当Application的一个事件触发时,MultiCaster必定会在事件处理处理方法中遍历所有的ApplicationEvents并触发它们的事件。比如Application的OnMessage事件触发时,MultiCaster的DoMessage得到调用,在它里面会调用所有ApplicationEvents的DoMessage方法。
procedureTMultiCaster.DoMessage(varMsg:TMsg;varHandled:Boolean);
var
I:Integer;
begin
BeginDispatch;
try
forI:=Count-1downto0do
begin
AppEvents[I].DoMessage(Msg,Handled);
ifFCancelDispatchingthenBreak;
end;
finally
EndDispatch;
end;
end;
而ApplicationEvents的DoMessage方法里触发一个OnMessage事件。
procedureTCustomApplicationEvents.DoMessage(varMsg:TMsg;varHandled:Boolean);
begin
ifAssigned(FOnMessage)thenFOnMessage(Msg,Handled);
end;
原来Application是借由MultiCaster这个全局对象,将它的所有事件广播给ApplicationEvents,再由ApplicationEvents去触发自己的事件。整个过程就是这么简单。
依据这个原理,我们也可以设计自己的事件广播机制,首先我们的业务对象不一定像Application是全局对象,所以当任MultiCaster这样角色的对象也不一定是全局对象,”MultiCaster”必须在”Application”的生命周期中才有效,既然如此,应该让” MultiCaster”成为”Application”的私有成员,另外像” ApplicationEvents”也不必是独立的组件类,只需要是”MultiCaster”的一个方法即可,假设这个方法为AddObjEvents。如此一来,所有事件机制就都集成到”MultiCaster”一个类中了。
多说无益,用一个简单的例子来说明这种方法的应用最有效。为了尽可能地简单,我将一个画图程序简化为一个拖放矩形的程序:程序中有两个区,一个是画板区,画板区存在一个矩形,现要求可以用鼠标拖动这个矩形,也可以改变它的大小;另一个区是信息区,显示矩形的位置和大小,也可以通过填写信息区的矩形位置和大小信息来改变矩形。
从上面的要求可以看出,矩形就相当于业务对象,我们设计矩形类为TRectangle,两个区是业务对象的两种视图,为了让代码分离以便于以后的维护和扩展,两个区用两个Frame分离出来,这两个区都必须能够接收TRectangle的事件。我们用上面描述的方法去实现TRectangle类,且看下面的代码:
unitwdRect;
interface
uses
Classes,Graphics,Contnrs;
type
TRectangle=class;
TOnRectChange=procedure(Rectangle:TRectangle)ofobject;
//光标在矩形类中的位置标识
TMouseInType=(mitNone,mitLeft,mitTop,mitRight,mitBottom,mitInner,
mitLeftTop,mitLeftBottom,mitRightTop,mitRightBottom);
{矩形的事件触发类}
TRectEvents=class
private
FOnRectChange:TOnRectChange;
FOnBeforeRectChange:TOnRectChange;
public
procedureDoRectChange(Rectangle:TRectangle);
procedureBeforeRectChange(Rectangle:TRectangle);
propertyOnRectChange:TOnRectChangereadFOnRectChangewriteFOnRectChange;
propertyOnBeforeRectChange:TOnRectChangereadFOnBeforeRectChange
writeFOnBeforeRectChange;
end;
{矩形的事件广播类}
TEventBroadcast=class
private
FEventList:TObjectList;//用于保存事件类
procedureDoRectChange(Rectangle:TRectangle);
procedureBeforeRectChange(Rectangle:TRectangle);
public
functionAddRectEvent:TRectEvents;
constructorCreate;
destructorDestroy;override;
end;
TRectangle=class
private
FLeft:Integer;
FTop:Integer;
FWidth:Integer;
FHeight:Integer;
FEventBroadcast:TEventBroadcast;
procedureSetHeight(constValue:Integer);
procedureSetLeft(constValue:Integer);
procedureSetTop(constValue:Integer);
procedureSetWidth(constValue:Integer);
public
constructorCreate;
destructorDestroy;override;
//画自己
procedureDraw(Canvas:TCanvas);
//擦除自己
procedureErase(Canvas:TCanvas);
//光标在什么位置
functionMouseInRect(X,Y:Integer):TMouseInType;
//调整位置大小属性
procedureAdjustRect;
propertyLeft:IntegerreadFLeftwriteSetLeft;
propertyTop:IntegerreadFTopwriteSetTop;
propertyWidth:IntegerreadFWidthwriteSetWidth;
propertyHeight:IntegerreadFHeightwriteSetHeight;
propertyEventBroadcast:TEventBroadcastreadFEventBroadcast;
end;
var
Rectangle:TRectangle;
implementation
{TRectangle}
procedureTRectangle.AdjustRect;
begin
{由于对矩形拖放之后,位置大小属性可以有些不同,所以需要一些调整}
ifFLeft>=FLeft+FWidththen
FLeft:=FLeft+FWidth;
ifFTop>=FTop+FHeightthen
FTop:=FTop+FHeight;
FWidth:=Abs(FWidth);
FHeight:=Abs(FHeight);
end;
constructorTRectangle.Create;
begin
FEventBroadcast:=TEventBroadcast.Create;
end;
destructorTRectangle.Destroy;
begin
FEventBroadcast.Free;
inherited;
end;
procedureTRectangle.Draw(Canvas:TCanvas);
begin
Canvas.Rectangle(FLeft,FTop,FLeft+FWidth,FTop+FHeight);
end;
procedureTRectangle.Erase(Canvas:TCanvas);
begin
Canvas.Rectangle(FLeft,FTop,FLeft+FWidth,FTop+FHeight);
end;
functionTRectangle.MouseInRect(X,Y:Integer):TMouseInType;
begin
//计算鼠标是否在矩形的特定区域中
if(X>=FLeft-2)and(X<=FLeft+2)and
(Y>FTop+2)and(Y<FTop+FHeight-3)then
Result:=mitLeft
elseif(X>=FLeft+FWidth-3)and(X<=FLeft+FWidth)
and(Y>FTop+2)and(Y<FTop+FHeight-3)then
Result:=mitRight
elseif(Y>=FTop-2)and(Y<=FTop+2)and
(X>FLeft+2)and(X<FLeft+FWidth-3)then
Result:=mitTop
elseif(Y>=FTop+FHeight-3)and(Y<=FTop+FHeight)
and(X>FLeft+2)and(X<FLeft+FWidth-3)then
Result:=mitBottom
elseif(X>=FLeft-2)and(X<=FLeft+2)and
(Y>=FTop-2)and(Y<=FTop+2)then
Result:=mitLeftTop
elseif(X>=FLeft-2)and(X<=FLeft+2)and
(Y>=FTop+FHeight-3)and(Y<=FTop+FHeight)then
Result:=mitLeftBottom
elseif(X>=FLeft+FWidth-3)and(X<=FLeft+FWidth)and
(Y>=FTop-2)and(Y<=FTop+2)then
Result:=mitRightTop
elseif(X>=FLeft+FWidth-3)and(X<=FLeft+FWidth)and
(Y>=FTop+FHeight-3)and(Y<=FTop+FHeight)then
Result:=mitRightBottom
elseif(X>FLeft+2)and(X<FLeft+FWidth-3)and
(Y>FTop+2)and(Y<FTop+FHeight-3)then
Result:=mitInner
elseResult:=mitNone;
end;
procedureTRectangle.SetHeight(constValue:Integer);
begin
ifFHeight<>Valuethen
begin
FEventBroadcast.BeforeRectChange(Self);
FHeight:=Value;
FEventBroadcast.DoRectChange(Self);
end;
end;
procedureTRectangle.SetLeft(constValue:Integer);
begin
ifFLeft<>Valuethen
begin
FEventBroadcast.BeforeRectChange(Self);
FLeft:=Value;
FEventBroadcast.DoRectChange(Self);
end;
end;
procedureTRectangle.SetTop(constValue:Integer);
begin
ifFTop<>Valuethen
begin
FEventBroadcast.BeforeRectChange(Self);
FTop:=Value;
FEventBroadcast.DoRectChange(Self);
end;
end;
procedureTRectangle.SetWidth(constValue:Integer);
begin
ifFWidth<>Valuethen
begin
FEventBroadcast.BeforeRectChange(Self);
FWidth:=Value;
FEventBroadcast.DoRectChange(Self);
end;
end;
{TRectEvents}
procedureTRectEvents.BeforeRectChange(Rectangle:TRectangle);
begin
ifAssigned(FOnBeforeRectChange)then
FOnBeforeRectChange(Rectangle);
end;
procedureTRectEvents.DoRectChange(Rectangle:TRectangle);
begin
ifAssigned(FOnRectChange)then
FOnRectChange(Rectangle);
end;
{TEventBroadcast}
functionTEventBroadcast.AddRectEvent:TRectEvents;
var
RectEvents:TRectEvents;
begin
//增加一个事件类
RectEvents:=TRectEvents.Create;
FEventList.Add(RectEvents);
Result:=RectEvents;
end;
procedureTEventBroadcast.BeforeRectChange(Rectangle:TRectangle);
var
i:Integer;
begin
//向外广播事件
fori:=0toFEventList.Count-1do
TRectEvents(FEventList[i]).BeforeRectChange(Rectangle);
end;
constructorTEventBroadcast.Create;
begin
FEventList:=TObjectList.Create;
end;
destructorTEventBroadcast.Destroy;
begin
FEventList.Free;
inherited;
end;
procedureTEventBroadcast.DoRectChange(Rectangle:TRectangle);
var
i:Integer;
begin
//向外广播事件
fori:=0toFEventList.Count-1do
TRectEvents(FEventList[i]).DoRectChange(Rectangle);
end;
end.
单元中的类结构并不复杂,TRectangle拥有TEventBroadcast,而TRectangle的事件皆由TEventBroadcast去处理,当矩形类的大小位置改变时,都会调用TEventBroadcast的两个方法BeforeRectChange和DoRectChange,这两个方法又会遍历所有的TRectEvents类并触发它们的事件。只要调用TEventBroadcast的AddRectEvent即可创建一个TRectEvents对象并加到列表中,所以外部如果要接收TRectangle的事件,则要调用AddRectEvent方法得到一个TRectEvents,再引用这个TRectEvents类的事件。
至于其他代码,大都是实现矩形的拖放功能,这里就略去不讲了。
另外三个单元分别是MainFrm:主窗体包含两个Frame;DrawFme:矩形所在的画布;InfoFme:矩形的信息显示。
MainFrm很简单,看下面的代码:
unitMainFrm;
interface
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,
Dialogs,ExtCtrls,DrawFme,InfoFme,wdRect;
type
TfrmMain=class(TForm)
pnlInfo:TPanel;
pnlDraw:TPanel;
fmeDraw:TfmeDraw;
fmeInfo:TfmeInfo;
procedureFormCreate(Sender:TObject);
procedureFormDestroy(Sender:TObject);
private
{Privatedeclarations}
public
{Publicdeclarations}
end;
var
frmMain:TfrmMain;
implementation
{$R*.dfm}
procedureTfrmMain.FormCreate(Sender:TObject);
var
RectEvents:TRectEvents;
begin
Rectangle:=TRectangle.Create;
//引用矩形类的事件
RectEvents:=Rectangle.EventBroadcast.AddRectEvent;
RectEvents.OnRectChange:=fmeDraw.OnRectChange;
RectEvents.OnBeforeRectChange:=fme
分享到:
相关推荐
使用Delphi完成Android系统广播事件的监听与处理(静态注册广播接收),本程序为监听手机屏幕点亮事件,当屏幕点亮后发送一个本地通知。修改监听事件及处理过程可以实现更多的实用功能。
用delphi写的一个DEMO,用于windows广播消息的处理
这个是·基于Delphi开发的UDP广播和单薄的通讯示例,附有代码
【delphi】Android系统状态广播消息感知控件及演示程序源代码,详细介绍了Android系统消息广播感知原理。 控件感知功能包括: 1. 感知蓝颜状态变化 2. 感知WiFI状态变化 3. 感知电源状态变化 4. 感知网络状态变化 5....
delphi语言udp屏幕广播台湾人的,采用分块算法,值得参考,需要优化
该程序演示了Delphi开发的开发Android系统广播消息App自动感知控件的功能。 包括感知如下事件广播消息 1. 蓝牙 2. WIFI 3. 电源 4. 网络
演示了BroadcastReceiver的简单用法。
一个基于udp的delphi实现的网络聊天工具
帮朋友写的用来局域网广播的软件,很简单,适合新手看,老手就不要下了
屏幕广播的源码(delphi),这是台湾人写的程序,分块传输。希望对你有所帮助。
Delphi IP查找机器名 Delphi IP查找机器名 Delphi IP查找机器名
Delphi条形码扫描相关实例..rar`
使用全Delphi代码实现的二维码扫码程序,静态库方式调用,实现代码简单、方便,扫码过程不卡顿。(请注意,只适应安卓使用)
1. Delphi 开发的WebSocket 控件源代码 2. 包含控件使用的演示程序Demo 3. 支持字符串和数据流传输 4. 使用indy控件 5. 可以单独发送数据,也可也i广播数据 6. 支持后台断开客户端 7. 使用方便 目前不支持 wss
安卓广播监听按键事件和屏幕熄屏亮屏监听,BroadcastReceiver使用
局域网广播小程序 可以设置局域网ip进行聊天 点对点发送 也可以同时接受多人的信息
基于Delphi使用UDP通信,包含客户端和服务器端两个代码。 版本:Delphi builder10.2 主要是用IdUDPClient, ...参考链接:https://blog.csdn.net/qq_38204686/article/details/78244279 delphi简单的聊天室(UDP广播)
多socket通信UDP源码,使用广播,多播地址,指定目的端口号
基于UDP协议的winform项目,具有小型聊天室的功能,希望给大家带来少许的帮助。
delphi for android 条码扫描初步解决方案