响应链接的 Delphi 组件事件
Posted
技术标签:
【中文标题】响应链接的 Delphi 组件事件【英文标题】:Responding to linked Delphi Component Events 【发布时间】:2011-01-04 11:32:42 【问题描述】:我有一个自定义组件(删减)
TMyComponent = class(TComponent)
public
procedure ClientConnected;
published
property ClientSocket: TClientSocket Read ...etc
现在我在 ClientSocket 调用 ClientConnected 的 OnConnect 事件中,例如
procedure TForm1.ElvinClient1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
MyComponent1.ClientConnected;
end;
有没有办法在 TMyComponent 类中做到这一点而不需要外部事件?
编辑: 忘了说ClientSocket不是由组件创建的,它是在运行时分配的。
我也尝试过使用私有 Proc
procedure TMyComponent.OnClientConnected(sender: TObject);
begin
ClientConnected;
if Assigned(oldOnClientConnected) then
oldOnClientConnected(sender);
end;
以及 ClientSocket 的设置器
procedure TMyComponent.SetClientSocket(const Value: TClientSocket);
begin
fClientSocket := Value;
oldOnClientConnected:= fClientSocket.OnElvinConnected;
fClientSocket.oldOnClientConnected:= OnClientConnected;
end;
但我感觉它会回来困扰我......
【问题讨论】:
【参考方案1】:您需要成功挂钩分配的 ClientSocket(我们称之为子组件)的事件是:
不“干扰”正常的设计时行为:仅在运行时分配您自己的事件处理程序 保存用户(开发者)分配的子组件事件处理程序 确保安全地调用保存的方法。 允许运行时重新分配子组件。 记录您在幕后所做的事情,因为开发人员的无知可能会搞砸所有事情例如:
TMyComponent = class(TComponent)
private
FClientSocket: TClientSocket;
FSavedClientConnected: TClientConnectedEvent; //Look for the Event type you want to save
procedure ClientConnected(AValidFirm: TFirm); //This method shall have a valid TClientConnectedEvent firm
procedure Hook; //Set my own event handlers to the child component
procedure UnHook; //Free the child component of my own event handlers ant let it as it was before
protected
procedure Loaded; override; //Touch the child component after the container streaming load is finished
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
//Stay safe if the child component goes away
public
destructor Destroy; override; //Cleanup the child component if I'm going away
published
property ClientSocket: TClientSocket read FClientSocket write SetClientSocket;
//let's play
end;
procedure TMyComponent.SetClientSocket(Value: TClientSocket);
begin
if FClientSocket <> Value then
begin
UnHook;
FClientSocket := Value;
Hook;
end;
end;
procedure TMyComponent.Hook;
begin
if (csDesigning in ComponentState) or (csLoading in ComponentState) then Exit;
if Assigned(FClientSocket) then
begin
FSavedClientConnected := FClientSocket.SavedClientConnected;
FClientSocket.ClientConnected := ClientConnected;
end;
end;
procedure TMyComponent.UnHook;
begin
if Assigned(FClientSocket) then
begin
FClientSocket.ClientConnected := FSavedClientConnected;
FSavedClientConnected := nil;
end;
end;
procedure TMyComponent.Loaded;
begin
Hook;
end;
destructor TMyComponent.Destroy;
begin
UnHook;
end;
procedure TMyComponent.Notification(AComponent: TComponent; Operation: TOperation);
begin
if (AComponent = FClientSocket) and (Operation = opRemove) then
UnHook;
FClientSocket := nil;
end;
//the important part!
procedure TMyComponent.ClientConnected(AValidFirm: TFirm);
begin
DoMyOwnStuffWith(FClientSocket);
if Assigned(FSavedClientConnected) then
FSavedClientConnected(AValidFirm);
//as you see, you can call the saved event before, after or in the mid of your own stuff.
end;
当然,它不会编译(我敢肯定,从未尝试过),但这只是一个简短的想法。也许我错过了一些东西,如果您遇到错误,请告诉我进行评论。
【讨论】:
谢谢你,看起来基本上是我开始做的,但有更多的想法:)【参考方案2】:是的,您可以在您的组件中实现一个带有该签名的过程,并在您构造 TClientSocket 时将其分配给事件。
但是...由于您公开了 ClientSocket,这意味着可以使用您的组件从代码中覆盖该事件分配。根据事情发生的顺序以及谁将使用您的组件,这对您来说可能不是问题 - 但您可能需要考虑将 ClientSocket 设为私有或受保护,或者可能编写一个简单的代理,然后使用确保在用户的事件处理程序之前调用您的 ClientConnected。
【讨论】:
忘了说这个,但是ClientSocket不是由组件创建的,它是在设计时分配的。我认为不管我怎么做,这可能会很乱。 分配或创建它并不重要,只要您“及时”分配事件......但不,它不太可能是漂亮的。就个人而言,我会说编写代理是您最好的选择,但这只是基于对 TClientSocket 的快速了解,这实际上取决于您的确切需求。当然,如果它绝对必须是从您的组件外部提供的普通 TClientSocket,那么除了您当前的解决方案之外,我真的看不出您能做什么。【参考方案3】:也许您可以覆盖 TClientSocket 类,并在调用事件的地方也调用您自己的例程。唯一的障碍是您需要一个标记为虚拟或动态的例程来执行此覆盖,我还没有检查 TClientSocket 是否这样做。
在这种情况下,您仍然可以根据需要将客户端套接字公开为 TClientsocket。
【讨论】:
以上是关于响应链接的 Delphi 组件事件的主要内容,如果未能解决你的问题,请参考以下文章
《从零开始学Swift》学习笔记(Day 68)——Cocoa Touch设计模式及应用之响应者链与触摸事件