Nach Programmstart und Anzeige automatische Routine starten
Sicherlich sind schon viele Leute darauf hereingefallen, irgend etwas im
Form.OnCreate-Handler tun zu wollen, weil der nur einmal beim Programmstart aufgerufen wird, und dann festzustellen, dass zu diesem Zeitpunkt noch nicht das komplette Programm wirklich mit Inhalt existiert, insbesondere noch keine weiteren Fenster. Die Balance zu finden zwischen "nur beim Programmstart" und "erst wenn das Hauptfenster dargestellt wurde" ist nicht leicht ohne ausreichend Hintergrundwissen.
Ich habe mir mal ein paar Abläufe von Programmstarts protokollieren lassen und festgestellt, dass die wichtigsten Ereignisse von
TForm und
TApplicationEvents in der Reihenfolge
Delphi-Quelltext
Delphi-Quelltext
Delphi-Quelltext
Delphi-Quelltext
Delphi-Quelltext
1:
| TApplicationEvents.OnIdle |
gestartet werden, und daher folgender Ablauf im Allgemeinen zu funktionieren scheint:
- Es wird eine ApplicationEvents-Komponente in das Hauptformular eingefügt.
- Im TForm.OnCreate-Handler wird lediglich eine "Semaphore" (Boolean-Variable) initialisiert, die ausschließlich nur zum Programmstart true ist, und im Folgenden gleich false gesetzt wird...
- Im TApplicationEvents.OnIdle-Handler (weil erst da alle vorher wichtigen Ereignisse abgearbeitet wurden) wird die gewünschte Routine ausgeführt, falls die Semaphore true ist - nachdem die Semaphore schnellstmöglich wieder auf false gesetzt wurde.
Das könnte dann etwa so aussehen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51:
| unit MainUnit;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, AppEvnts;
type TfmMain = class(TForm) stbStatus: TStatusBar; aeAppEvents: TApplicationEvents; procedure FormCreate(Sender: TObject); procedure aeAppEventsIdle(Sender: TObject; var Done: Boolean); private initializing: Boolean; procedure AutoInit; public end;
var fmMain: TfmMain;
implementation
{$R *.dfm}
procedure TfmMain.FormCreate(Sender: TObject); begin initializing := true; end;
procedure TfmMain.AutoInit; begin stbStatus.SimpleText := 'Initialisierung'; end;
procedure TfmMain.aeAppEventsIdle(Sender: TObject; var Done: Boolean); begin if initializing then begin initializing := false; AutoInit; end; Done := true; end;
end. |
Alternativen dazu wären:
- Was ausgeführt werden muss, bevor das Hauptfenster sichtbar wird, kann man (mit Semaphore) auch in TForm.OnShow behandeln. Nutzer-Ereignishandler werden ja bekanntlich ausgeführt, bevor die Standardmethode für dieses Ereignis abgearbeitet wird (also OnShow direkt vor dem Anzeigen).
- Bei Programmen mit nur einem Hauptfenster wird das Ereignis TForm.OnActivate eventuell nur ein einziges Mal ausgelöst. Auch hier wäre eine Absicherung der Einmaligkeit des Aufrufes sinnvoll. Nachteil: Die Routine kollidiert dann zeitlich etwas mit dem ersten TForm.OnPaint, das gerade dabei ist, das Fenster zum ersten Mal darzustellen.
- Ebenfalls sinnvoll und näher am ereignisorientierten Konzept von Windows wäre es, anstatt der Semaphore einen Handler für eine selbst definierte Windows-Message zu schreiben, und der Applikation diese Nachricht im TForm.OnCreate-Ereignis selbst zu schicken. Allerdings erfordert das ein wenig mehr Arbeit mit dem Quelltext, und im Objektinspektor wäre dieser Handler dann wohl nicht unter den Ereignissen zu finden.