如何在Delphi中模拟TFrame上的OnDestroy事件?

问题描述 投票:0回答:4

如何在 Delphi 中模拟

OnDestroy
TFrame
事件?


我天真地在我的框架中添加了

constructor
destructor
,认为这就是
TForm
的作用:

TframeEditCustomer = class(TFrame)
...
public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
   ...
end;

constructor TframeEditCustomer.Create(AOwner: TComponent)
begin
    inherited Create(AOwner);
    
    //allocate stuff
end;

destructor TframeEditCustomer.Destroy;
begin
   //cleanup stuff

   inherited Destroy;
end;

问题在于,当我的析构函数运行时,框架上的控件已被销毁并且不再有效。

其原因在于包含表单的析构函数,它用来触发

OnDestroy
事件:

destructor TCustomForm.Destroy;
begin
   ...
   if OldCreateOrder then DoDestroy; //-->fires Form's OnDestroy event; while controls are still valid
   ...
   if HandleAllocated then DestroyWindowHandle; //-->destroys all controls on the form, and child frames
   ...
   inherited Destroy; //--> calls destructor of my frame
   ...
end;

当窗体的析构函数运行时,我的框架对象的析构函数被调用。问题是为时已晚。窗体调用

DestroyWindowHandle
,它要求 Windows 销毁窗体的窗口句柄。这会递归地销毁所有子窗口 - 包括我框架上的子窗口。

因此,当我的框架的

destructor
运行时,我尝试访问不再处于有效状态的控件。


如何在 Delphi 中模拟

OnDestroy
TFrame
事件?

delphi destructor delphi-5 tframe
4个回答
11
投票

您需要添加 WM_DESTROY 处理程序并检查 ComponentState 中的 csDestroying,以便仅在实际销毁时捕获它,而不是在重新创建句柄时捕获。

type
  TCpFrame = class(TFrame)
  private
    FOnDestroy: TNotifyEvent;
    procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
  published
    property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
  end;

procedure TCpFrame.WMDestroy(var Msg: TWMDestroy);
begin
  if (csDestroying in ComponentState) and Assigned(FOnDestroy) then
    FOnDestroy(Self);
  inherited; 
end;

只有当框架的窗口句柄实际上已经创建时,这才有效。没有其他好的挂钩点,所以如果你想确保它总是被调用,你需要在 WMDestroy 中设置一个标志,如果没有命中,则回退到在析构函数中调用它。

窗口句柄本身在 WM_NCDESTROY 中全部清除,该窗口句柄在所有后代 WM_DESTROY 消息返回后调用,因此此时窗体及其所有子句柄应该仍然有效(忽略在窗体的 OnDestroy 中释放的任何句柄) ).


1
投票

听起来更像

OnClose
而不是
OnDestroy

无论如何,我只是从基础祖先继承了所有框架和表单,并且表单的 onclose 调用了组件层次结构中的所有框架。


0
投票

(这只是一个想法,但我现在没有时间构建概念证明,但我仍然会分享它:)

如果是 Windows 句柄问题,您应该检查是否能够附加 Windows 事件回调指针,当框架的 Windows 句柄不再存在时会调用该指针。也许使用像 RegisterWaitForSingleObject

这样的函数

0
投票

另一个选项是覆盖

AfterConstruction
BeforeDestruction

类似这样的:

  TMyFrame = class(TFrame)
  private
    FOnCreate: TNotifyEvent;
    FOnDestroy: TNotifyEvent;
  protected
    procedure DoCreate; virtual;
    procedure DoDestroy; virtual;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    property OnCreate: TNotifyEvent read FOnCreate write FOnCreate;
    property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
  end;

  implementation

  procedure TMyFrame.AfterConstruction;
  begin
    inherited;
    DoCreate;
  end;

  procedure TMyFrame.BeforeDestruction;
  begin
    inherited;
    DoDestroy;
  end;

  procedure TMyFrame.DoCreate;
  begin
    if Assigned(FOnCreate) then
      FOnCreate(Self);
  end;

  procedure TMyFrame.DoDestroy;
  begin
    if Assigned(FOnDestroy) then
      FOnDestroy(Self);
  end;
© www.soinside.com 2019 - 2024. All rights reserved.