如何在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
事件?
答案 0 :(得分:9)
您需要添加一个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_NCDESTROY在所有后代WM_DESTROY消息返回后调用,因此表单及其所有childens的句柄在此时仍应有效(忽略任何已释放的内容) form的OnDestroy)。
答案 1 :(得分:1)
听起来更像OnClose
而不是OnDestroy
。
无论如何,我只是从基础祖先继承了我的所有框架和表单,然后表单的onclose调用组件层次结构中的所有框架。
答案 2 :(得分:0)
(这只是一个想法,但我现在还没有时间构建一个概念验证,但我会分享它:)
如果Windows手柄出现问题,你应该检查一下,当框架的Windows句柄不再存在时,你可以附加一个Windows'事件回调指针。也许使用像RegisterWaitForSingleObject
这样的函数答案 3 :(得分: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;