Come per simulare un evento OnDestroy su un TFrame in Delphi?
-
09-10-2019 - |
Domanda
Come posso simulare un evento OnDestroy
per un TFrame
in Delphi?
i nievely aggiunto un constructor
e destructor
al mio telaio, pensando che è quello che fa 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;
Il problema è che per il momento le mie corse destructor, controlli sul telaio sono state distrutte e non sono più validi.
La ragione di questo è nel distruttore del modulo contenente, che utilizza per generare un evento 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;
Il distruttore della mia cornice oggetto viene chiamato quando distruttore del form viene eseguito. Problema con questo è che è troppo tardi. Le chiamate forma DestroyWindowHandle
, che chiede a Windows di distruggere handle della finestra del modulo. Questo distrugge in modo ricorsivo tutte le finestre figlio -. Comprese quelle sul mio telaio
Così, quando viene eseguito destructor
di mio telaio, tento di controlli di accesso che non sono più in uno stato valido.
Come posso simulare un evento OnDestroy
per un TFrame
in Delphi?
Vedi anche
Soluzione
È necessario aggiungere un gestore WM_DESTROY e controllare csDestroying nel ComponentState in modo che sia catturato solo quando effettivamente distruggere, e non quando ricreare la maniglia.
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;
Questo funziona solo se handle della finestra del telaio è stato effettivamente realizzato. Non c'è un altro punto di aggancio buono, quindi se si vuole garantire che è sempre chiamata è necessario impostare un flag in WMDestroy e ripiegare a chiamarlo nel distruttore se non è colpito.
Le maniglie delle finestre stesse vengono cancellate in WM_NCDESTROY, che si chiama dopo tutto il discendente messaggi WM_DESTROY ritorno, così la forma e tutte le maniglie suoi childens dovrebbero essere ancora valido, a questo punto (senza tenere conto che sono stati liberati nel del modulo OnDestroy).
Altri suggerimenti
suona più come OnClose
di OnDestroy
.
In ogni caso, ho appena ereditato tutti i miei fotogrammi e le forme da un antenato di base, e le chiamate OnClose del form poi tutti i fotogrammi del gerarchia dei componenti.
(E 'solo un'idea, ma non ho avuto il tempo adesso di costruire una prova di concetto, ma io condividere nondimeno:)
Se è un problema con il manico di Windows (s), si dovrebbe verificare wether siete in grado di collegare puntatore evento di callback un Windows' che viene chiamato quando la maniglia di Windows del telaio cessa di esiste. Forse con una funzione come RegisterWaitForSingleObject
Un'altra opzione è quella di ignorare AfterConstruction
e BeforeDestruction
Qualcosa di simile a questo:
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;