Quick-Tipp: Und Action!

Preisfrage: Wie verwendet man einen PushButton?

Man nutzt seinen Action-Event, richtig? Wenn sein Gedrücktwerden nun mehrere Aktionen auslösen soll, die gar nicht zwangsläufig in bestimmter Reihenfolge ablaufen müssen, hat man recht schnell einen ansehnlichen Eventhandler zusammenprogrammiert. Was kein Problem ist, nur: Will man später eine Aktion verändern oder sie deaktivieren, sucht man mitunter erst einmal im Code herum, um die entsprechende Stelle zu finden. Ich weiß nicht, wie’s Ihnen dabei geht, aber mir gefällt das nie so recht. Ich halte meine Methoden gerne so kurz wie möglich, um von Xojos Navigator-Darstellung zu profitieren, und um Code schnell refaktorierbar zu halten – soll heißen: Muss ich mal was ändern, dann bevorzugt an einer zentralen Stelle, die ich gerne ohne langes Suchen finde.

Und irgendwie ist das Ansprechen von Controls via PushButton-Action Event ein bisschen das OOP-Pferd von hinten aufgezäumt. Ich mag es mehr, wenn meine Steuerelemente eigenverantwortlich sind und nicht von außen getriggert werden.

Kommt noch dazu: Soll der PushButton dynamisch bestimmte Aktionen auslösen, habe ich auch noch jede Menge Abfragen im Code, die Bedingungen bestimmen, nach denen das eine oder andere Control angestupst werden soll oder eben nicht. Das trägt wieder nicht zur Lesbarkeit bei. Außerdem bringt langer Code bei 64 Bit-Builds den LLVM zum Glühen: Seine Optimierungsstrategien mögen kurze, knackige Methoden.

Tja nun, das hier soll ja ein Quick-Tipp sein. Also mal schnell rein in die Lösung – für die es nicht mehr braucht als eine winzigkleine Unterklasse eines beliebigen Controls. Ich exerziere das mal an einem Canvas durch:

  • Ich erstelle eine neue Klasse (in diesem Fall in einem Desktop-Projekt), nenne sie ActionCanvas und lasse sie auf der Canvas-Klasse basieren.
  • Ich klicke auf „Choose“ neben „Interfaces“ im Inspector und setze ein Häkchen bei „ActionNotificationReceiver“.ActionCanvas Interface.png
  • Damit wird eine neue Methode in der ActionCanvas-Klasse angelegt – die einzige, die zum ActionNotificationReceiver-Interface gehört: PerformAction. PerformAction wird immer dann aufgerufen, wenn eine ActionSource den Action-Event auslöst.
  • Mit dem Button muss ich gar nichts machen, denn ein PushButton beinhaltet standardmäßig das ActionSource-Interface. ActionSource kennt zwei Methoden:
    addActionNotificationReceiver (Receiver As ActionNotificationReceiver)

    und

    removeActionNotificationReceiver (Receiver As ActionNotificationReceiver)
  • Leicht zu erraten, was die machen, oder? Erstere fügt einen Receiver zum PushButton hinzu, letztere entfernt ihn wieder. Bei allen Receivern wird die PerformAction-Methode aufgerufen, wenn die Action des Senders ausgelöst wird.
  • ActionCanvas bekommt eine Boolesche Property spendiert:
    ActionPressed As Boolean

    und in die PerformAction-Methode kommt dieser Code:

    Public Sub PerformAction()
    // Part of the actionNotificationReceiver interface.
       me.ActionPressed = true
       me.Invalidate
       xojo.core.timer.CallLater 100, Addressof ResetAction
    End Sub
  • Fehlen noch ResetAction:
    Public Sub ResetAction()
       me.ActionPressed = false
       me.Invalidate
    End Sub
  • und der Paint-Event:
    Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
       g.ForeColor = if (me.ActionPressed, color.Red, Color.Green)
       g.FillRect 0, 0, g.Width, g.Height
    End Sub
  • Einen PushButton und einen ActionCanvas auf einem Fenster platziert:

    actioncanvas-window

  • und dem Open-Eventhandler von ActionCanvas1 diesen Code gegeben:
    Sub Open() Handles Open
       PushButton1.addActionNotificationReceiver me
    End Sub

und Sie haben einen grünen Canvas, der bei jeden Klick auf den Button rot wird, ohne dass der Action-Eventhandler des Buttons benutzt würde.

Auf diese Art könnten sich jetzt weitere Controls dynamisch anmelden und auch bei Bedarf wieder abmelden. Das hab ich nur, wegen guten Stils, in den Close-Eventhandler von ActionCanvas1 eingetragen:

Sub Close() Handles Close
   PushButton1.removeActionNotificationReceiver me
End Sub

Das Projekt zum Experimentieren finden Sie hier.

Und wie das alles mit einem Timer funktioniert, habe ich vor einiger Zeit schon beschrieben.

2 Gedanken zu “Quick-Tipp: Und Action!

  1. Cool, schön wäre es, wenn du noch ein einfaches Beispiel für die Refactories „ListSelectionNotifierReceiver“, „DataCell“, „dataNotificationReceiver“ o.ä. hättest (oder nähere Infos dazu) …

    Ebenso erkenne ich nicht die Vorteile von „Get“ und „Set“, denn es geht ja auch ohne…

    Weiter so mit deinem Blog, finde ihn gut.

    Freundliche Grüße

    Gefällt mir

    1. Hallo Ralf,
      vielen Dank für die Anregungen! Bei den Interfaces tummelt sich vieles, was auch mir noch völlig unbekannt ist. Bei der Vielseitigkeit des ActionNotification-Interfaces lohnt vermutlich auch ein Blick in die anderen sehr. Merk ich mir vor!

      Get und Set meinst du in Bezug auf Computed Properties? Da steht im entstehenden Nichthandbuch einiges zu, aber in Kürze:
      Klar geht es auch ohne. Du kannst aber z.B. den Setter leer lassen und hast dann eine Property, die von außen nur gelesen werden kann. Oder eine Klasse soll andere benachrichtigen, wann immer eine ihrer Properties verändert wurde? Dann kann der Setter der Computed Property die private Property setzen und automatisch Benachrichtigungen abschicken, so ähnlich wie das Action-Interface.
      Weitere Möglichkeit ist eine Datenverifikation des neuen Property-Werts, anstatt diesen einfach ungeprüft zu übernehmen.

      Das lässt sich alles auch über Methoden realisieren, insofern braucht man Computed Properties in der Tat eigentlich nicht. Im Gegensatz zu Methodenergebnissen werden Computed Properties aber im Debugger angezeigt, was in meinen Augen ein großer Vorteil ist, wenn man bei der Fehlersuche dem Programm bei der Arbeit zuschauen will (oder muss).

      Es gibt aber auch jede Menge Programmierer, die auf diesen Komfort verzichten. Mitunter auch, weil die Inspector Behavior Properties, wenn man sie für eigene Controls anlegt, mitunter fies ins Konzept reinpfuschen und für unvorhergesehene Ergebnisse sorgen können. Ich zeige am liebsten meinen bevorzugten Programmierstil, aber ohne Exklusivitätsanspruch. Das Schöne an Xojo ist ja (auch), dass in der Regel immer mehrere Wege zur Lösung führen. Jede Computed Property führt immer zu einer kleinen Verlangsamung. Der Wert wird ja immer über eine Zwischenmethode durchgereicht. Insofern regieren da also auch Performance-Vorlieben, und womöglich die Frage, ob du nur für dich programmierst – dann liegt der verantwortungsvolle Umgang mit den Daten eh voll in deiner Hand –, oder ob du Code weitergeben magst. Dann dankt es der Drittprogrammierer, ordentlich gekapselte und selbstständige, komfortable Klassen zu erhalten. Also kein Muss, sie zu verwenden, wenn sie in deinem Anwendungsfall keine eindeutigen Vorteile bringen.

      Gefällt mir

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s