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

Как я могу смоделировать событие 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?

Смотрите также


person Ian Boyd    schedule 20.10.2010    source источник
comment
У этих фреймов нет OnCreate, а OnDestroy создан специально. Не могу найти публикацию / статью / информацию прямо сейчас (так что комментарий, а не ответ), но я помню, как давно видел ответы от участников Borland и / или Teamb (о чем свидетельствует номер QC), в которых говорилось об этом. IIRC причина, по которой, очевидно, не было подходящего момента для запуска этих событий, можно определить, поскольку простота создания и уничтожения форм затрудняется потоковой передачей и визуальным наследованием формы / кадра.   -  person Marjan Venema    schedule 20.10.2010
comment
Почему нет OnCreate (от участника отдела исследований и разработок Borland, как передал Джефф Оверкаш из TeamB): codenewsfast.com/isapi/isapi.dll/. То же самое, вероятно, применимо и к событию OnDestroy. Так что я бы учел эти предостережения при разработке вашего решения.   -  person Marjan Venema    schedule 20.10.2010


Ответы (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 формы) .

person Zoë Peterson    schedule 20.10.2010
comment
Это действительно элегантный ответ. Иногда я забываю, что наши приложения существуют на платформе Windows, которая уже имеет богатую систему для выполнения задач. - person Ian Boyd; 31.01.2016

Похоже больше на OnClose, чем на OnDestroy.

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

person Marco van de Voort    schedule 20.10.2010
comment
Это работает, если кто-то, кто хочет использовать мой автономный фрейм, желает полностью изменить архитектуру своего приложения. Кроме того, это не столько OnClose, потому что форму можно снова открыть. Он действительно создает / уничтожает, но совместим с фреймами. - person Ian Boyd; 20.10.2010

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

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

person Stijn Sanders    schedule 20.10.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;
person Daniel Maurić    schedule 20.10.2010
comment
я пробовал переопределить BeforeDestruction. Это еще не так скоро - органы управления уже получили свое WM_DESTROY сообщение и ответили на него. - person Ian Boyd; 21.10.2010
comment
Вы можете проверить это, чтобы заполнить поле со списком элементами во время constructor и наблюдать во время destructor, что поле со списком уже пусто (и все объекты, хранящиеся в ComboBox1.Items.Objects, просочились) - person Ian Boyd; 21.10.2010
comment
Если вы освобождаете фрейм напрямую, все его компоненты по-прежнему доступны, проблема, с которой вы сталкиваетесь, заключается в освобождении родительской формы. В этом случае вы должны использовать WM_DESTROY, как предложил Крейг Петерсон, или просто уведомлять кадры вручную из обработчика FormDestroy родительской формы - person Daniel Maurić; 21.10.2010