views:

231

answers:

3

I have a custom Component (cut down)

 TMyComponent = class(TComponent)
 public
    procedure ClientConnected;
 published 
    property ClientSocket: TClientSocket Read ...etc

Right now i have in the OnConnect Event of the ClientSocket Call ClientConnected e.g.

 procedure TForm1.ElvinClient1Connect(Sender: TObject; Socket: TCustomWinSocket);
 begin
   MyComponent1.ClientConnected;
 end;

Is there a way to do this with in the TMyComponent Class with out the need of the external event?

Edit:
Forgot to say that the ClientSocket is not create by the component, its assigned at runtime.

i have also tried having a private Proc

 procedure TMyComponent.OnClientConnected(sender: TObject);
 begin
  ClientConnected;
  if Assigned(oldOnClientConnected) then
   oldOnClientConnected(sender);
 end;

and the a setter for the ClientSocket

 procedure TMyComponent.SetClientSocket(const Value: TClientSocket);
 begin
   fClientSocket := Value;
   oldOnClientConnected:= fClientSocket.OnElvinConnected;
   fClientSocket.oldOnClientConnected:= OnClientConnected;
 end;

But i get the feeling that it will come back to haunt me...

A: 

Yes, you can just implement a procedure with that signature in your component and assign it to the event when you construct the TClientSocket.

HOWEVER... since you expose the ClientSocket, that means it's possible to overwrite that event assignment from the code using your component. Depending on the order in which things happen and who's going to be using your component, that might not be a problem for you - but you might want to consider making the ClientSocket private or protected, or possibly writing a simple proxy which you then use to make sure your ClientConnected is called before the user's event handler.

Michael Madsen
forgot to say this, but the ClientSocket isnt Created by the Component, its assigned at design time. I think this might be messy not matter how i do it.
Christopher Chase
It doesn't really matter if it's assigned or created, as long as you assign the event "in time"... but no, it's not likely to be pretty. Personally, I'd say writing that proxy is your best bet, but that's just based on a very quick glimpse at TClientSocket and it's really going to depend on your exact needs. Of course, if it absolutely must be a plain TClientSocket supplied from outside your component, I don't really see what you could do other than your current solution.
Michael Madsen
A: 

Perhaps you van override TClientSocket class and at the place where the event is called you also call your own routine. The only snag is that you need a routine that is marked virtual or dynamic to do this override and I haven't checked if TClientSocket does that.

In this case you can still expose the client socket as a TClientsocket as and if needed.

Ritsaert Hornstra
+1  A: 

What you need to succesfully hook the events for the assigned ClientSocket (let's call it child component) is:

  • Not "disturb" normal design time behavior: Assign your own event handlers only at runtime
  • Save the user (developer) assigned event handlers of the child component
  • Make sure to call the saved methods safely.
  • Allow runtime reassignment of the child component.
  • Document what you're doing behind the scenes, because developer's ignorance could mess all the things

For example:

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;

Of course, it doesn't compile (I'm sure, never tried), but it's only a brief with the idea. Maybe I miss something, let me know if you get errors to make a review.

jachguate
Thanks for that, seems basically what i started doing, but with a bit more thought put into it :)
Christopher Chase
good to hear it helps ;)
jachguate