Как имитировать событие 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;