Wir haben viele Formulare in unserer Anwendung und ich brauche einen globalen Event-Handler, um zu erkennen, wenn eines der Formulare zerstört wird (und dann etwas unternehmen).
ps: Ich möchte vermeiden, Code zu jedem Formular hinzuzufügen, das eine Nachricht an das Hauptformular senden muss, wenn es zu zerstören ist. Außerdem werden die meisten Formulare zur Laufzeit dynamisch erstellt und gelöscht.
Ich habe überlegt, vielleicht ein globales TApplicationEvents zu verwenden.
Was ist der beste Ansatz dafür?
Eine Einschränkung beim Ändern von Code in vorhandenen Formularen oder beim Erstellen von Formularen, wie aus anderen Antworten und Kommentaren hervorgeht, hinterlässt Hacks und Hooks. Ein lokaler CBT-Hook, z. B., wäre ein wenig Arbeit, aber wahrscheinlich gut funktionieren. Unten ist eine der einfacheren Hacky-Lösungen.
Screen
globales Objekt enthält zu jeder Zeit eine Liste von Formularen über eine normale TList
. TList
hat eine virtuelle Prozedur Notify
, die jedes Mal aufgerufen wird, wenn ein Element hinzugefügt / entfernt wird. Die Idee ist, eine TList
-Derivative zu verwenden, die diese Methode überschreibt und sie im Objekt Screen
verwendet.
Das Testen auf csDestroying
ist erforderlich, weil Screen
der Liste Formulare nicht nur beim Erstellen / Löschen von Formularen hinzufügt / entfernt, sondern auch, wenn sie aktiviert sind usw.
Dann mach% ce_de% diese Liste. Dies erfordert einen "Zugriff auf private Felder" Hack, da die Screen
-Liste privat ist. Sie können über diesen Hack auf Hallvard Vassbotns Blog nachlesen. Es erfordert auch "Ändern der Klasse eines Objekts zur Laufzeit" hacken. Sie können über diesen Hack auf Hallvard Vassbotns Blog nachlesen .
Beachten Sie, dass die Benachrichtigung für jede Formularzerstörung aufgerufen wird. Dies gilt auch für Formulare, die mit FForms
, MessageDlg
usw. erstellt wurden.
Im Gegensatz zu Davids Antwort gibt es einen geeigneten Rahmen. Es ist höher in der Klassenhierarchie bei TComponent
eingebaut. Sir Rufo ist auf dem richtigen Weg, aber Sie müssen nicht zwingen, dass Ihre Formulare diesem Objekt gehören.
Sie können gerne eine beliebige Anzahl von Klassen schreiben, die spezielle Maßnahmen ergreifen können, wenn ein Formular (oder eine andere Komponente) zerstört wird. ZB
%Vor%Wenn Sie nun ein Formular erstellen, setzen Sie einfach eine Benachrichtigung wie folgt (vorausgesetzt, Sie haben sich Zugriff auf geeignete Instanzen der oben genannten Objekte gegeben):
%Vor% Dieser Ansatz ist besser als die Verwendung des OnDestroy
-Ereignisses, da dies nur 1 Beobachter zulässt, während FreeNotification
eine beliebige Anzahl von Beobachtern zulässt.
HINWEIS : Passen Sie ein Problem wie bei jeder anderen geeigneten Technik nicht an. Möglicherweise gibt es eine geeignetere Technik für Ihr spezifisches Problem. Z.B. Die Idee MenuManager
könnte besser gelöst werden, wenn das globale Objekt Screen
verwendet wird, um die Formulare OnPopup
zu iterieren.
Der Benachrichtigungsmechanismus TComponent
ist eine integrierte Implementierung des Beobachtermusters für den Fall, dass eine Komponente zerstört wird. FreeNotification
(vielleicht nicht ideal benannt) ist das Äquivalent von registerObserver
und RemoveNotification
das Äquivalent von unregisterObserver
.
Der springende Punkt des Beobachtermusters ist, dass das beobachtete Objekt (manchmal Herausgeber genannt) keine typspezifische Kenntnis der Objekte hat, die es beobachten (manchmal angerufene Teilnehmer). Publisher wissen nur, dass sie eine generische Benachrichtigungsmethode für jeden registrierten Abonnenten (Beobachter) aufrufen können. Dadurch können Objekte lose mit denen verbunden werden, die sie beobachten. Tatsächlich muss der Publisher überhaupt nicht beobachtet werden . Offensichtlich muss die Registrierungsmethode entweder von den Teilnehmern selbst oder von einem Dritten aufgerufen werden - andernfalls wird das Entkopplungsziel unterlaufen.
Beobachter können mit unterschiedlichen Komplexitätsgraden implementiert werden. Am einfachsten ist ein Event oder Callback. Am komplexesten ist ein Dispatcher, der Anmeldungen zwischen unabhängigen Verlagen und Abonnenten verwaltet. Der Dispatcher könnte sogar eine Thread-Umstellung implementieren, sodass Publisher nicht einmal von den Leistungsnebenwirkungen langsamer Abonnenten betroffen sind.
Die Beobachterimplementierung von TComponent
hat eine Einschränkung, die sowohl der Herausgeber als auch der Abonnent von TComponent
erben müssen. Grundsätzlich kann sich jede Komponente bei einer anderen Komponente registrieren, um über deren Zerstörung informiert zu werden.
Vielleicht ist die am häufigsten verwendete Verwendung dieses Features in Delphi: Wenn Komponente A einen Verweis auf Komponente B hat; Wenn Komponente B zerstört wird, wird Komponente A benachrichtigt, so dass sie ihre Referenz auf Null setzen kann.
Dies ist nicht die beste Vorgehensweise (werfen Sie einen Blick auf Davids Antwort), aber ein Weg zu gehen.
Da jedes Formular einen Eigentümer haben kann (Typ TComponent
) und dieser Besitzer benachrichtigt wird, wenn eine untergeordnete Komponente zerstört wird, erstellen Sie einfach einen globalen Formularbesitzer und übergeben diesen als Besitzer jedes erstellten Formulars, das benachrichtigt werden soll auf zerstören.
Sie müssen die Methode TComponent.Notification überschreiben und tun, was Sie tun muss (zB ein Ereignis auslösen)
%Vor% Der Hauptformularbesitzer ist immer Application
, aber es ist nicht notwendig, dies zu verfolgen.
Was Sie wollen, ist, dass der Rahmen ein Ereignis auslöst, wenn ein Formular zerstört wird. Wenn ein Formular zerstört wird, wird sein Destruktor ausgeführt. Damit das Framework ein solches Ereignis auslösen kann, müsste es daher innerhalb des Destruktors des Formulars implementiert werden. Wenn Sie in TCustomForm.Destroy
hineinschauen, werden Sie feststellen, dass es kein solches Ereignis gibt.
Daraus können wir schließen, dass es kein anwendungsweites Ereignis geben kann, wenn ein Formular zerstört wird. Dies bedeutet, dass Sie selbst eine Lösung implementieren müssen. Eine naheliegende Möglichkeit, dies zu erreichen, ist die Einführung einer gemeinsamen Basisklasse für alle Ihre Formulare. Stellen Sie sicher, dass jedes Formular in Ihrem Programm letztendlich von dieser allgemeinen Basisklasse abgeleitet wird. Ordnen Sie dann für die Basisklasse ein Ereignis an, das ausgelöst wird, sobald eine Instanz zerstört wird.
Es scheint ein Missverständnis darüber zu bestehen, was ich oben gesagt habe. Craig demonstriert, wie man Benachrichtigungen über die Zerstörung eines einzelnen Formulars abonnieren kann. Die Fähigkeit, das zu tun, widerspricht nicht dem, was ich sage. Mein Punkt ist, dass es keinen Mechanismus gibt, der es Ihnen ermöglicht, Benachrichtigungen zu abonnieren, wenn das any Formular zerstört wird.
Persönlich würde ich David Heffernans Lösung vorziehen, da alle meine Formulare immer auf einer Vorlage basieren und es die sauberste und am einfachsten zu implementierende Methode wäre
Aber von dir kommt Nachfrage nach p.s: I want to avoid adding code to each form that will need to send a message to the main form when it's about to destroy. also most of the forms are created and destroyed dynamicaly at run-time.
Du könntest Destroy auf eine eigene Methode patchen.
Ich würde den neuesten aufgerufenen Destruktor in der Kette nehmen und TObject.Destroy zu TMyClass.Destroy patchen. Der Ort für die Umsetzung sollte das Projekt sein.
Der Code für das Patchen stammt aus David Heffernans Antwort auf den Patch-Routineaufruf in Delphi und nur enthalten, um die Antwort vollständig zu halten , Credits bezüglich dieses Codes gehen dorthin.
Wie in Vlads Anfrage erweitert dies auf meine ursprüngliche Antwort , indem erklärt wird, wie man alle Formulare registriert, die zu% co_de gehören % ohne irgendwelche Änderungen an der Konstruktion jeder Form. I.e. Formulare, die mit Application
und implizit auch mit TMyForm.Create(Application);
erstellt wurden.
In der ursprünglichen Antwort wird kein spezielles Registrierungsverfahren für Application.CreateForm(TMyForm, MyForm);
angegeben, da die Optionen abhängig davon variieren, wie Formulare erstellt werden. Da die beantwortete Frage die Erstellung der Formulare nicht einschränkte, ist die ursprüngliche Antwort im allgemeinen Fall angemessener.
Wenn wir sicherstellen könnten, dass sich FreeNotification
auf eine benutzerdefinierte Unterklasse von Application
bezieht, wäre das Problem relativ einfach zu lösen, indem TApplication
überschrieben wird. Das ist nicht möglich, daher nutzt dieser Sonderfall die Tatsache, dass das Komponentenbesitz-Framework alle zugehörigen Komponenten benachrichtigt, wenn eine andere Komponente hinzugefügt oder entfernt wird. Im Grunde brauchen wir also nur einen Komponenten-Tracker, der auch TApplication.Notification;
gehört, und wir können auf seine "Geschwister" -Benachrichtigungen reagieren.
Der folgende Testfall wird zeigen, dass neue Benachrichtigungen funktionieren.
%Vor% Application
wird wie folgt implementiert:
Sie können alles, was Sie ausgewählt haben, im TComponentTracker
Event-Handler implementieren. Dazu gehört die Protokollierung, dass das Formular " zerstört " ist. Ein solcher simplifizierter Ansatz wäre jedoch tatsächlich fehlerhaft , da OnComponentNotification
den Eigentümer einer Komponente ändern kann, ohne ihn zu zerstören.
Um die Vernichtung genau zu melden, müssten Sie dies mit TComponent.InsertComponent
kombinieren, wie in meiner ersten Antwort .
Dies ist sehr einfach, indem Sie FreeNotification
setzen, wobei LComponentTracker.OnComponentNotification := FDestructionLogger.RegisterFreeNotification;
wie folgt implementiert wird:
Ein sehr einfacher Ansatz könnte darin bestehen, die Anzahl der Formulare zu verfolgen. Wenn es sinkt, wird eine Form zerstört. Überprüfen Sie Application.OnIdle:
%Vor% Je nachdem, welche Aktion ausgeführt werden soll, können Sie Screen.CustomForms
durchlaufen, um festzustellen, welches Formular zerstört wurde.