响应链接的 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 组件事件的主要内容,如果未能解决你的问题,请参考以下文章

delphi控件属性和事件

《从零开始学Swift》学习笔记(Day 68)——Cocoa Touch设计模式及应用之响应者链与触摸事件

《从零开始学Swift》学习笔记(Day 68)——Cocoa Touch设计模式及应用之响应者链与触摸事件

Delphi 拦截滚轮事件不响应滚轮的上下滚动

iOS事件响应链

iOS 事件响应者链的学习(也有叫 UI连锁链)