Как смоделировать событие OnDestroy на TFrame в Delphi? - PullRequest
6 голосов
/ 20 октября 2010

Как я могу смоделировать событие OnDestroy для TFrame в Delphi?


я с легкостью добавил 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 запускается, я пытаюсь получить доступ к элементам управления, которые больше не находятся в допустимом состоянии.


Как я могу имитировать событие OnDestroy для TFrame в Delphi?

См. Также

Ответы [ 4 ]

9 голосов
/ 20 октября 2010

Вам необходимо добавить обработчик 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 формы).

1 голос
/ 20 октября 2010

Звучит больше похоже на OnClose, чем OnDestroy.

Во всяком случае, я просто унаследовал все свои фреймы и формы от базового предка, и onclose формы вызывает все кадры в иерархии компонентов.

0 голосов
/ 21 октября 2010

Другим вариантом является переопределение 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;
0 голосов
/ 20 октября 2010

(Это просто идея, но у меня сейчас нет времени, чтобы создать доказательство концепции, но я поделюсь ею тем не менее:)

Если это проблема с дескриптором Windows(s), вы должны проверить, можете ли вы прикрепить указатель обратного вызова события Windows, который вызывается, когда дескриптор Windows фрейма перестает существовать.Возможно, с такой функцией, как RegisterWaitForSingleObject

...