Wie kann ich in meiner Formular-Anwendung auf bestimmte Ereignisse warten, ohne CPU-Last zu erzeugen oder die Anwendung einzufrieren?
Kurz: gar nicht!
Das ist nämlich nicht der Sinn einer ereignisorientierten Anwendung.
Die Lösung ist eine andere Denkweise:
Programmzustände definieren und auf Ereignisse reagieren.
Lösungs-Beispiel A: Buttons exclusiv aktivieren
Wir wollen eine ganz simple Anwendung schreiben, ein Formular, das drei Buttons enthält. Allerdings sollen diese drei Buttons nicht beliebig anklickbar sein, sondern nur nacheinander. Es soll also zunächst nur Button1 anklickbar sein und erst wenn der Benutzer diesen angeklickt hat, darf Button2 anwählbar sein, usw. usf., Button3 soll dann wieder Button1 aktivieren.
Wir erstellen also ein neues Formular und legen drei Buttons darauf ab. Dann ergänzen wir im Quelltext der Anwendung folgendes (hervorgehoben):
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:
| type TAppState = ( asButton1, asButton2, asButton3 );
TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure UpdateGUI; private public AppState: TAppState; end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.UpdateGUI; begin Button1.Enabled := (AppState = asButton1); Button2.Enabled := (AppState = asButton2); Button3.Enabled := (AppState = asButton3); end; |
Erläuterung:
Zunächst zählen wir die Anwendungszustände auf, also in unserem Beispiel den Fall, dass nur Button1 anklickbar ist, dann nur Button2, usw. Dabei ist der gerade definierte Zustand nur eine (praktisch beliebige) Zahl, die stellvertretend für den Anwendungszustand steht. Wir müssen in einer entsprechenden Methode später dafür sorgen, dass bei einem Wechsel dieses Zustands auch entsprechende Aktionen folgen. In einer realen Anwendung kann das auch schon mal etwas mehr sein, als hier bei dem einfachen Beispiel.
Wir müssen in der Anwendung den aktuellen Zustand speichern, deshalb deklarieren wir im
public-Abschnitt eine entsprechende Variable des gerade definierten Aufzählungstyps:
AppState: TAppState.
Schließlich benötigen wir noch eine Methode, um die Bedienungselemente der Anwendung passend zum aktuellen Zustand einzustellen. Das erledigen wir mit der
UpdateGUI-Prozedur (für die wir eine entsprechende Deklaration in der Formularklasse einfügen müssen).
Wir können unsere Testanwendung bereits starten und ausprobieren, aber es wird sich nicht das gewünschte Resultat zeigen, da wir bisher auch gar nichts mit dem Anwendungszustand machen. Deshalb erstellen wir einen
FormCreate-Ereignishandler (durch Doppelklick auf dem Formularhintergrund) und fügen folgenden Code ein:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| procedure TForm1.FormCreate(Sender: TObject); begin AppState := asButton1; UpdateGUI; end; |
Beim Start des Programms stellen wir einen Start-Zustand ein und passen die Oberfläche entsprechend an. Wenn wir die Anwendung jetzt starten, sind die Buttons korrekt de-/aktiviert. Allerdings "passiert" noch nichts, da wir nicht auf die Button-Klicks reagieren. Deshalb erstellen wir durch je einen Doppelklick auf die Buttons drei entsprechende Ereignis-Handler und ergänzen den folgenden Code:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure TForm1.Button1Click(Sender: TObject); begin AppState := asButton2; UpdateGUI; end;
procedure TForm1.Button2Click(Sender: TObject); begin AppState := asButton3; UpdateGUI; end;
procedure TForm1.Button3Click(Sender: TObject); begin AppState := asButton1; UpdateGUI; end; |
Jetzt reagieren wir auf die Button-Klicks und setzen den entsprechenden (neuen!) Anwendungszustand, worauf hin wir natürlich auch noch die Oberfläche anpassen müssen (der erfahrenere Leser sieht hier schon die Optimierung: es bietet sich an, den Formularzustand als Eigenschaft der Formularklasse anzulegen und mit einem entsprechenden Setter die Aktionen zu verbinden).
Wenn wir jetzt unsere Beispiel-Anwendung starten, reagieren die Buttons wie gewünscht und lassen sich jeweils immer nur nacheinander anklicken.
Lösungs-Beispiel B: ein Button mit unterschiedlichen Funktionen
Im zweiten Beispiel wollen wir uns anschauen, wie man einem Button unterschiedliche Funktionen geben kann:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private FButtonState: Integer; public end;
implementation
procedure TForm1.Button1Click(Sender: TObject); begin case FButtonState of 0: ShowMessage('Hallo!'); 1: ShowMessage('Du bist aber neugierig... :)'); 2: ShowMessage('Jetzt reicht´s aber!'); 3: ShowMessage('Tüß!'); end; FButtonState := (FButtonState +1) mod 4; end; |
In diesem Zusammenhang:
Hier geht es weiter mit der
AppState-FAQ Teil 2: Auf Threads in Formularanwendungen warten.
cu
Narses
There are 10 types of people - those who understand binary and those who don´t.