Autor Beitrag
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 25.12.06 20:13 
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. :idea: 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):
ausblenden volle Höhe Delphi-Quelltext
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
  // mögliche Anwendungszustände aufzählen
  TAppState = (
    asButton1, // Button1 anklickbar
    asButton2, // Button2...
    asButton3  // Überraschung! ;)
    );

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure UpdateGUI; // Anwendungszustand umsetzen
  private
    { Private-Deklarationen }
  public
    AppState: TAppState;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// Anwendungszustand umsetzen: Buttons anpassen
procedure TForm1.UpdateGUI;
begin
  // jeder Button ist nur in seinem Zustand anklickbar
  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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
// bei Programmstart ausführen
procedure TForm1.FormCreate(Sender: TObject);
begin
  AppState := asButton1; // Startzustand: Button1 anklicken
  UpdateGUI; // Änderung übernehmen
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:
ausblenden 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; // neuer Zustand: Button2 anklickbar
  UpdateGUI; // Änderung übernehmen
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  AppState := asButton3; // neuer Zustand: Button3 anklickbar
  UpdateGUI; // Änderung übernehmen
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  AppState := asButton1; // neuer Zustand: Button1 anklickbar
  UpdateGUI; // Änderung übernehmen
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:
ausblenden 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; // Button-Zustand
  public
    { Public-Deklarationen }
  end;

implementation

procedure TForm1.Button1Click(Sender: TObject);
begin
  case FButtonState of // abhängig vom aktuellen Zustand...
    0: ShowMessage('Hallo!');
    1: ShowMessage('Du bist aber neugierig... :)');
    2: ShowMessage('Jetzt reicht´s aber!');
    3: ShowMessage('Tüß!');
  end;
  FButtonState := (FButtonState +1mod 4// nächster Zustand, 4 -> 0
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.