Delphi 如何检测其他控件何时更改其边界?

Delphi 如何检测其他控件何时更改其边界?,delphi,delphi-xe2,Delphi,Delphi Xe2,我有一个自定义的TLabel,原则上可以附加到表单中的任何其他可视组件。组件有一个属性position,该属性告诉它将朝向附加控件(左侧、上方等)放置的位置。如果附加了相关控件,并且组件根据position属性对其自身进行定位,则此功能可以正常工作 问题是,我无法让组件在相关控件改变其边界时进行检测,以便它能够正确地重新定位自身。我想这与WMMove和WMResize有关。如何使相关控件通知TLabel任何边界属性已更改?每当控件的位置和/或尺寸更改时,就会触发控件的OnResize事件。因此,

我有一个自定义的
TLabel
,原则上可以附加到表单中的任何其他可视组件。组件有一个属性
position
,该属性告诉它将朝向附加控件(左侧、上方等)放置的位置。如果附加了相关控件,并且组件根据
position
属性对其自身进行定位,则此功能可以正常工作


问题是,我无法让组件在相关控件改变其边界时进行检测,以便它能够正确地重新定位自身。我想这与
WMMove
WMResize
有关。如何使相关控件通知
TLabel
任何边界属性已更改?

每当控件的位置和/或尺寸更改时,就会触发控件的
OnResize
事件。因此,一个简单的解决方案是在将标签附加到控件时为该事件指定一个处理程序,例如:

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;

基于@RemyLebeau答案和
ExtCtrls.TLabeledEdit
中的一些概念想法,我编写了自己的控件。(正在进行的工作)。当然,雷米的回答应该被接受:)

这是一个TControl还是TWinControl?也就是说,它是否有窗口句柄?我认为您几乎需要考虑制作一个可以包含相关控件的TPanel。您的
tlabelothercontrol
可以通过
TSmartLayoutPanel
进行管理,它可以查看其中的所有控件,并知道它们何时移动。我真的不认为我会想要一个TLabel在拥有它的表单上做各种粗俗的钩子。看看Swing如何使用布局管理(Java)来获得一些灵感。如果
TLabel
组件的父组件将是
TControl
的后代,则可以在分配给它时截取它的
WindowProc
方法。在这种截获的
WindowProc
方法中,您可以查看
WM\u WINDOWPOSCHANGED
消息,例如。Hi-Remy。谢谢你的回答!我尝试了
子类
解决方案,但它遇到了
FControlWndProc(Message)问题。它似乎进入了一个循环。有什么建议吗?“似乎”并不意味着“是”。使用调试器,它实际上在做什么?什么信息被卡住了?代码只是将截获的消息传递给关联的控件进行默认处理,因此不应该存在循环,除非该控件本身递归地生成新消息,或者您自己的代码定位/调整标签的大小对控件产生影响,并使其递归地生成新消息。你能编辑你的问题来显示你处理截获消息的实际代码吗?@RemyLebeau,有件事我不明白:在
SetControl
中,你的第二个
如果FControl nil then。。end else FControl.WindowProc:=nil正确吗?它不应该是:
FControlWndProc:=nil?另外,您不应该在
TMyLabel.Notification
TMyLabel.Destroy
上同时调用
SetControl(nil)
吗?@kobik:这是一个输入错误。它应该是
FControlWndProc
而不是
FControl.WindowProc
。如果在控件释放之前标签被释放,那么在析构函数中调用
SetControl(nil)
是有意义的,但是在
Notification()中调用它是没有意义的,因为控件正在被释放,所以不需要将控件重置为其以前的状态,只需重置它的本地跟踪。@kobik:yes,如果希望标签在设计器中拖动时跟随控件,则必须这样做。
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;