Как имитировать событие OnDestroy на TFrame в 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;
деструктор объекта my frame вызывается при запуске деструктора формы. Проблема в том, что уже слишком поздно. Форма вызывает DestroyWindowHandle
, который просит Windows уничтожить дескриптор окна формы. Это рекурсивно уничтожает все дочерние окна-в том числе на моем фрейме.
когда работает, я пытаюсь получить доступ к элементам управления, которые больше не находятся в допустимом состоянии.
как я могу имитировать OnDestroy
события TFrame
в Делфи?
см. также
4 ответов
вам нужно добавить обработчик WM_DESTROY и проверить csDestroying в ComponentState, чтобы он был пойман только при фактическом уничтожении, а не при воссоздании дескриптора.
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 формы).
звучит как OnClose
чем OnDestroy
.
в любом случае, я только что унаследовал все мои фреймы и формы от базового предка, и onclose формы вызывает все фреймы в иерархии компонентов.
(Это просто идея, но у меня нет времени сейчас, чтобы построить доказательство концепции, но я поделюсь тем не менее:)
Если это проблема с дескриптором (- АМИ) Windows, вы должны проверить, можете ли вы прикрепить указатель обратного вызова события Windows, который вызывается, когда дескриптор Windows фрейма перестает существовать. Возможно, функции RegisterWaitForSingleObject
другой вариант-переопределить 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;