HSG |
|
Ein Ereignis ist eine Zustandsänderung (bzw. das Ausführen eines Kommandos) in einem Objekt, die mit einer Ereignisbehandlungsroutine verknüpft werden kann.
Implementierungsdetails dieser abstrakte Definition sollen im Folgenden aufgezeigt werden.
Wer sich für einen Alltagsvergleich interessiert, der soll hier klicken.
Ein Ereignis in diesem Sinne ist z.B. das Drücken eines Buttons. Die Verknüpfung mit einer Ereignisbehandlungsroutine erfolgt in Delphi üblicherweise mit Doppelklick ( Beispiel 1) oder durch den Objektinspektor (Beispiel 2). Sie ist aber auch problemlos durch eine direkte Zuweisung möglich (Beispiel 3).
Zu einem Ereignis gehört in Delphi immer eine Referenzvariable, die auf die Ereignisbehandlungsroutine zeigt. Ist keine Ereignisbehandlungsroutine zugeordnet, so enthält die Referenzvariable den Wert NIL.
Ereignisse können auch selbstdefiniert werden (Beispiel 4). Dadurch wird eine elegante Realisierung des MVC-Konzepts möglich.
Im ersten Beispiel wurden die Ereignisbehandlungsroutinen durch Doppelklick erzeugt und zugeordnet.
Der folgende Quelltext wurde bis auf die rot gekennzeichneten Teile automatisch erzeugt.
unit uEreignis1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) lAusgabe: TLabel; bLoeseAus: TButton; bReset: TButton; procedure bLoeseAusClick(Sender: TObject); procedure bResetClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.bLoeseAusClick(Sender: TObject); begin lAusgabe.Caption := 'Ereignis eingetreten'; end; procedure TForm1.bResetClick(Sender: TObject); begin lAusgabe.Caption := ''; end; end.
Das zweite Beispiel ist von der Oberfläche und der Funktionalität identisch zu ersten.
Die Unterschiede liegen in der Erzeugung und Zuordnung der Ereignisbehandlungsroutine. Die Routine wurde ganz von Hand geschrieben. Dabei ist der Parameter vom Typ TObjekt wichtig, hier setzt das aufrufende Objekt seinen Namen ein. Wie man sieht, muss der Parameter nicht Sender heißen. Siedelt man die Routine, die hier zur Abwechslung gibaus heißt, wie von Delphi vorgemacht vor den Sichtbarkeitsdirektiven (private, public,..) an, so läßt sich die Verbindung zum Ereignis im Objektinspektor durch Auswahl einer zugeordneten Routine erreichen.
unit uEreignis2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) lAusgabe: TLabel; bLoeseAus: TButton; bReset: TButton; procedure gibAus(obj : TObject); procedure bResetClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.gibAus(obj : TObject); begin lAusgabe.Caption := 'Ereignis eingetreten'; end; procedure TForm1.bResetClick(Sender: TObject); begin lAusgabe.Caption := ''; end; end.
Das dritte Beispiel zeigt die direkte Zuweisung der Ereignisbehandlungsroutinen zu einer entsprechenden Referenvariablen, hier bLoeseAus.OnClick . Diese Zuweisung kann natürlich auch während der Laufzeit erfolgen. Möchte man keine Ereignisbehandlungsroutine zuweisen, so weist man NIL zu.
unit uEreignis3; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) bLoeseAus: TButton; lAusgabe: TLabel; bRoutine1: TButton; bRoutine2: TButton; bNil: TButton; bReset: TButton; lZuordnung: TLabel; procedure bRoutine1Click(Sender: TObject); procedure bRoutine2Click(Sender: TObject); procedure bNilClick(Sender: TObject); procedure bResetClick(Sender: TObject); private procedure gibaus1(sender : TObject); procedure gibaus2(sender : TObject); public end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.gibaus1(sender : TObject); begin Form1.lAusgabe.caption := 'Routine 1'; end; procedure TForm1.gibaus2(sender : TObject); begin Form1.lAusgabe.caption := 'Routine 2'; end; procedure TForm1.bRoutine1Click(Sender: TObject); begin bLoeseAus.OnClick := gibAus1; end; procedure TForm1.bRoutine2Click(Sender: TObject); begin bLoeseAus.OnClick := gibAus2; end; procedure TForm1.bNilClick(Sender: TObject); begin bLoeseAus.OnClick := nil; end; procedure TForm1.bResetClick(Sender: TObject); begin lAusgabe.caption := ''; end; end.
Im Beispiel 4 soll nun Schritt für Schritt die Definition und die Verwendung eines selbstdefinierten Ereignisses erklärt werden:
Der erste Schritt besteht in der Vereinbarung eines Methodenzeigertyps
type
TEreignis = procedure of object; { 1. Typ eines Methodenzeigers }
Man sieht, dass dieses erste selbstdefinierte Ereignis der Einfachheit halber keine Parameter haben soll. Aber was bedeutet of object ?
Auszug aus der Delphi-Hilfe (nach 'of object' suchen):
Diese Typen sind Methodenzeiger. Ein Methodenzeiger wird in Form zweier Zeiger codiert, von denen der erste die Adresse der Methode speichert, während der zweite eine Referenz auf das Objekt enthält, zu dem die Methode gehört.
Eigentlich klar, dass ein Ereignis zu einem konkreten Objekt gehören muss.
Im zweiten Schritt vereinbaren wir eine Referenzvariable für das Ereignis. Per Konvention sollen in Delphi die Namen von Ereignissen mit On beginnen, hier heißt es OnChange. Die Referenzvariable soll gesetzt werden können, daher erhält sie die Sichtbarkeit public.
TZahl = class(TObject)
private
FZahl : byte;
public
OnChange : TEreignis; { 2. Vereinbarung der Referenzvariablen }
procedure SetZahl(z : byte);
function GetZahl : byte;
end;
Im dritten Schritt muss das Ereignis ausgelöst werden. Dabei ist zu beachten, dass ein Aufruf der Ereignisbehandlungsroutine nur erfolgen darf, wenn der Zeiger auf die Routine nicht NIL ist. Man beachte, dass der Programmierer die Stelle des Autretens des Ereignisses bestimmt. Im Beispiel tritt nach einer Änderung des Attributs das Änderungsereignis auf.
procedure TZahl.SetZahl(z : byte);
begin
FZahl := z;
if Assigned(OnChange) then OnChange; { 3. Auslösen des Ereignisses }
end;
Im vierten und letzten Schritt muss der Referenzvariablen eine Ereignisbehandlungsroutine zugewiesen werden, sonst bleibt das Auftreten des Ereignisses ohne sichtbare Wirkung. Das geschieht wie in Beispiel 3 vorgeführt.
procedure TForm1.FormCreate(Sender: TObject);
begin
oZahl := TZahl.Create;
oZahl.OnChange := aktualisiereAusgabe; { 4. Zuweisung einer Ereignisbehandl. }
end;
Ein kleines Testprogramm, das den Wert einer ganzen Zahl setzt und die Aktualisierung des Views über ein selbstdefiniertes Ereignis regelt, zeigt der folgende Quelltext.
unit uEreignis4; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TEreignis = procedure of object; { 1. Typ eines Methodenzeigers } TZahl = class(TObject) private FZahl : byte; public OnChange : TEreignis; { 2. Vereinbarung der Referenzvariablen } procedure SetZahl(z : byte); function GetZahl : byte; end; TForm1 = class(TForm) bSetzeZahl: TButton; eEin: TEdit; lAus: TLabel; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure bSetzeZahlClick(Sender: TObject); private oZahl : TZahl; procedure aktualisiereAusgabe; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TZahl.SetZahl(z : byte); begin FZahl := z; if Assigned(OnChange) then OnChange; { 3. Auslösen des Ereignisses } end; function TZahl.GetZahl : byte; begin Result := FZahl; end; procedure TForm1.aktualisiereAusgabe; begin lAus.caption := IntToStr(oZahl.GetZahl); end; procedure TForm1.FormCreate(Sender: TObject); begin oZahl := TZahl.Create; oZahl.OnChange := aktualisiereAusgabe; { 4. Zuweisung einer Ereignisbehandl. } end; procedure TForm1.FormDestroy(Sender: TObject); begin oZahl.Free; end; procedure TForm1.bSetzeZahlClick(Sender: TObject); begin oZahl.SetZahl(StrToInt(eEin.text)); end; end.
Schreibe das Programm aus Beispiel 4 so um, dass es dem MVC-Konzept genügt. Verwende insbesondere zwei Units für GUI und Model.