How do I detect when another control changes its limits?

advertisements

I have a custom TLabel that in principle can be attached to any other visual component in the form. The component has a property position which tells where it will be positioned towards the attached control (left, above, etc.). This works fine when the related control is attached, and the component positions itself according to the position property.

Problem is that I can't make the component detect when the related control changes it bounds so it can properly reposition itself. I guess that has to do with WMMove and WMResize. How can I do for the related control to notify the TLabel that any of the bounds properties has changed?


A control's OnResize event is triggered whenever its position and/or dimension changes. So one simply solution is to assign a handler to that event when you attach your Label to a control, eg:

private
  FControl: TControl;

// OnResize is protected in TControl so use an accessor class to reach it...
type
  TControlAccess = class(TControl)
  end;

procedure TMyLabel.Destroy;
begin
  SetControl(nil);
  inherited;
end;

procedure TMyLabel.SetControl(AControl: TControl);
begin
  if FControl <> AControl then
  begin
    if FControl <> nil then
    begin
      TControlAccess(FControl).OnResize := nil;
      FControl.RemoveFreeNotification(Self);
    end;
    FControl := AControl;
    if FControl <> nil then
    begin
      FControl.FreeNotification(Self);
      TControlAccess(FControl).OnResize := ControlResized;
    end;
    ...
  end;
end;

procedure TMyLabel.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FControl) then
    FControl := nil;
end;

procedure TMyLabel.ControlResized(Sender: TObject);
begin
  // reposition as needed...
end;

Of course, this will cause issues if the user wants to assign their own OnResize handler to the control.

The alternative is to subclass the control's WindowProc property instead:

private
  FControl: TControl;
  FControlWndProc: TWndMethod;

procedure TMyLabel.Destroy;
begin
  SetControl(nil);
  inherited;
end;

procedure TMyLabel.SetControl(AControl: TControl);
begin
  if FControl <> AControl then
  begin
    if FControl <> nil then
    begin
      FControl.WindowProc := FControlWndProc;
      FControl.RemoveFreeNotification(Self);
    end;
    FControl := AControl;
    if FControl <> nil then
    begin
      FControlWndProc := FControl.WindowProc;
      FControl.WindowProc := ControlWndProc;
      FControl.FreeNotification(Self);
    end else
     FControlWndProc := nil;
    ...
  end;
end;

procedure TMyLabel.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FControl) then
  begin
    FControl := nil;
    FControlWndProc := nil;
  end;
end;

procedure TMyLabel.ControlWndProc(var Message: TMessage);
begin
  FControlWndProc(Message);
  // now check for position/size messages and reposition as needed...
end;