Ereignissteuerung 5

Wir werfen auch jetzt zuerst  einen Blick auf die bisherige Klassenstruktur und Aufgabenverteilung:

Wir stellen fest, dass keine Reaktion auf Knopf-Ereignisse vorgesehen ist. Wer das Programm in der jetzigen Version einmal hat laufen lassen, stellt fest, dass die Knöpfe durchaus in der Lage sind festzustellen, wann sie gedrückt, angeklickt oder doppelgeklickt wurden. Wie entscheiden uns daher, dass die Knöpfe selbst die entsprechenden Reaktionen auslösen sollen.  Wir verdeutlichen das Vorgehen am Beispiel des Stop-Knopfes, der die Mühlen alle anhalten soll. 

  1. Der Knopf löst an passender Stelle seiner bearbeiteMausLosgelassen Methode eine Aktion aus! Hierfür ergänzen wir in der Prozedur einfach die Zeile:

        MeineAngeklicktAktion(self).

    Es handelt sich hierbei um einen Prozedursaufruf. Im aktuellen Parameter self steht dabei der auslösende Knopf. 
  2. Da jeder Knopf anders auf sein Anklicken reagieren soll, benötigen wir eine Zustandsvariable für den Knopf, in welcher steht, welche konkrete Prozedur denn nun ausgeführt werden soll. Da es sich um eine Zustandsvariable handelt, sollten die Knöpfe über ein entsprechendes Attribut verfügen. 
    Dies ist in Delphi möglich! Die entsprechenden Zeilen in der Typdeklaration von TEreignisknopf lauten:

        public
            meineDoppelklickAktion : procedure ( Sender : TObject ) of object;
            MeineGedruecktAktion : procedure ( Sender : TObject ) of object;
            MeineAngeklicktAktion : procedure ( Sender : TObject ) of object;
            MeineLosgelassenAktion : procedure ( Sender : TObject ) of object;

    Interessant ist der 'Datenyp' dieser Attribute : procedure of Object. Frei übersetzt heißt dies: Eine spezielle Prozedur dieses Objektes. 
    Die hier gewählte Form mit einem formalen Parameter procedure ( Sender : TObject) of Object  bedeutet, dass beim Prozeduraufruf ein aktueller Parameter eines beliebigen Objekttyps übergeben werden muss.
  3. Um dem Attribut MeineAngeklicktAktion des Stop Knopfes eine konkrete Prozedur zuweisen zu können, muss in irgendeiner Klasse eine entdprechende Prozedur deklariert worden sein.  Wir deklarieren in der Muehlenanwendung die Prozedur, in der steht, was passieren soll, wenn der Stop-Knopf gedrückt worden ist. 

        procedure TMuehlenAnwendung.KStopAngeklickt (Sender: TObject);
        begin
            Muehle1.stop;
            KStop.deaktiviere;
            KLangsamer.deaktiviere;
            KneueMuehle.aktiviere;
        end;
  4. Nun muss dem Stop Knopf nach seinem Erzeugen noch mitgeteilt werden, dass diese Prozedur seine Angeklickt-Aktion ist. Dies geschieht in der Mühlenanwendung in der init-Methode durch :

            KStop := TKreisknopf.init(150,400,10,'|||');
            .......
            KStop.MeineAngeklicktAktion := KStopAngeklickt;

    Man beachte die Schreibweise! Da einem Attribut ein neuer Wert zugewiesen wird, werden nur die Prozedurnamen ohne Parameter verwendet. 
    Aber Vorsicht!! : Diese Zuweisung funktioniert nur, wenn beide Parameterlisten von Typ und Reihenfolge her übereinstimmen!
     

Zum besseren Verständnis zuerst ein Klassendiagramm mit allen wichtigen Attributen und Diensten:


Und nun einige Hinweise zum Code:

Klasse TEreignisknopf:  Die genaue Formulierung des Aufrufes des Prozedurattributes:

 
Aufrufen des Prozedurattributes in der MausDoppelklick-Bearbeitung der Ereignisknöpfe:






Die Anfrage 'assigned' gibt an, ob dem 
Prozedurattribut auch eine wirkliche 
Prozedur zugeordnet ist.





Hier ist das Ereignis des Loslassen
eines Knopfes bearbeitet.
procedure TEreignisKnopf.bearbeiteMausDoppelKlick(pX,pY:Zahl);
begin
  if zAktiv then
      begin
         if innerhalb(pX,pY) then
            begin
               anzeige('Doppelklick : '+self.zAufschrift,600,300);
               if assigned(MeineDoppelKlickAktion)
                  then MeineDoppelKlickAktion( self );
            end;
         loesche;
         zWurdeGedrueckt := Falsch;
         zMausWarOben := Wahr;
         zeichne;
         anzeige('Gedrueckt : ---',600,100);
         if assigned(MeineLosgelassenAktion)
            then MeineLosgelassenAktion( self );
      end;
  if kenntNaechstenKnopf <> nil then
    kenntNaechstenKnopf.bearbeiteMausDoppelKlick(pX,pY);
end;

 

Klasse TMuehlenanwendung: Die init-Methode

 

Anmerkungen zum Code von TMuehlenanwendung:
In jeder konkreten Anwendungsklasse
sind die folgenden Schritte durchzuführen:




Erzeugen der für das konkrete 
Projekt benötigten Knöpfe.




Zuweisen derjenigen Methoden, die 
Ereignisse der Knöpfe bearbeiten. 
(Ereignisbehandlungsmethoden)



Verketten der Knöpfe





Aktivieren bzw. deaktivieren
der Knöpfe






Erzeugen aller anderen Objekte,
die bei der konkreten Anwendung
zu Beginn vorhanden sein müssen





Zur Erinnerung hier die init Methode
der Ereignisanwendung.
constructor TMuehlenAnwendung.init;
begin
  // Erzeugen durch den Code der Vorgängerklasse
  inherited init;

  // Erzeugen der Knöpfe
  KSchneller    := TKreisknopf.init(100,400,10,'+');
  KStop         := TKreisknopf.init(150,400,10,'|||');
  KLangsamer    := TKreisknopf.init(200,400,10,'-');
  KneueMuehle   := TRechteckknopf.init(300,400,100,'NEUE MÜHLE');

  // Was sollen die Knöpfe tun? (Methoden zuweisen)
  KNeueMuehle.MeineDoppelklickAktion := KNeueMuehleDoppelKlick;
  KStop.MeineAngeklicktAktion        := KStopAngeklickt;
  KSchneller.MeineGedruecktAktion    := KSchnellerGedrueckt;
  KLangsamer.MeineGedruecktAktion    := KLangsamerGedrueckt;

  // Anhängen der Knöpfe
  KEnde.haengeAn( KNeueMuehle );
  KEnde.haengeAn( KStop );
  KEnde.haengeAn( KSchneller );
  KEnde.haengeAn( KLangsamer );

  // De- bzw Aktivieren der Knöpfe
  KSchneller.aktiviere;
  KLangsamer.aktiviere;
  KStop.aktiviere;
  KNeueMuehle.deaktiviere;

  // Erzeugen und initialisieren der anderen zu Beginn
  // vorhandenen Objekte
  dieUhr        := Uhr.init;
  Muehle1       := TMuehle.init(150,300,150,3,rot);
  neueMuehle    := TMuehle.init(300,200,120,2,blau);
  Muehle1.haengeAn(neueMuehle);
end;
//-------- init (public) -----------------------------------------------
constructor TEreignisAnwendung.init;
begin
  derBildschirm := Bildschirm.init;
  dieMaus       := Maus.init;
  hatStift      := Stift.init;
  dieTastatur   := Tastatur.init;
  KEnde         := TRechteckknopf.init(310,450,80,'ENDE',dieMaus);
  KEnde.meineAngeklicktAktion:= KEndeAngeklickt;
  KEnde.aktiviere;
end;				


Zum jetzt erreichten Entwicklungsstand gibt es wieder ein Projekt : Knopf_Muehle_11_fertig.  Es kann später hier heruntergeladen werden.

Das genaue Zusammenspiel der Anwendung mit den Knöpfen und die Ereignisbehandlung folgen auf der nächsten Seite.