Comment simuler un événement OnDestroy sur un TFrame à Delphes?
-
09-10-2019 - |
Question
Comment puis-je simuler un événement OnDestroy
pour un TFrame
à Delphes?
i nievely a ajouté un constructor
et destructor
à mon cadre, pensant que c'est ce que TForm
fait:
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;
Le problème est que le temps que mes courses destructor, les contrôles sur le châssis ont été détruits et ne sont plus valides.
La raison est dans la destructor de forme contenant, qu'il utilise pour déclencher un événement de 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;
Le destructor de mon objet cadre est appelé lors de l'exécution de destructor du formulaire. Problème est qu'il est trop tard. Le formulaire appels DestroyWindowHandle
, qui demande à Windows de détruire la poignée de fenêtre du formulaire. Cela détruit récursive toutes les fenêtres de l'enfant -. Y compris ceux sur mon cadre
Alors, quand mon runs de destructor
de cadre, je tente de contrôles d'accès qui ne sont plus dans un état valide.
Comment puis-je simuler un événement OnDestroy
pour un TFrame
à Delphes?
Voir aussi
La solution
Vous devez ajouter un gestionnaire WM_DESTROY et vérifier csDestroying dans le ComponentState il est donc pris alors qu'en réalité la destruction, et non lors de la recréation de la poignée.
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;
Cela ne fonctionnera que si la poignée de fenêtre du cadre a été effectivement créé. Il n'y a pas un autre bon point de crochet, donc si vous voulez vous assurer qu'il est toujours appelé, vous aurez besoin de mettre un drapeau dans WMDestroy et revenir à l'appeler dans le destructor si ce n'est pas touché.
Les poignées de fenêtre se sont effacés dans WM_NCDESTROY, qui est appelé après que tous les descendants des messages WM_DESTROY retour, de sorte que la forme et toutes les poignées de ses childens restent valides à ce stade (sans tenir compte qui ont été libérés dans la OnDestroy de forme).
Autres conseils
Ressemble plus OnClose
que OnDestroy
.
Quoi qu'il en soit, je viens tous mes cadres hérité et les formes d'un ancêtre de base, et les appels OnClose puis tous les cadres du formulaire dans la hiérarchie du composant.
(Il est juste une idée, mais je n'ai pas eu le temps en ce moment pour construire une preuve de concept, mais je vais le partager pas moins:)
Si c'est un problème avec la poignée de Windows (s), vous devriez vérifier Que vous êtes en mesure de joindre un pointeur de rappel d'événement Windows qui est appelée lorsque Windows poignée cesse de existe du cadre. Peut-être avec une fonction comme RegisterWaitForSingleObject
Une autre option consiste à remplacer AfterConstruction
et BeforeDestruction
Quelque chose comme ceci:
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;