In diesem Artikel möchte ich Ihnen eine Methode vorstellen, mit der Sie Oberflächenereignisse innerhalb von CONZEPT 16-Applikationen mit dem Entwurfsmuster (engl. design pattern) "Observer" (Beobachter, Listener) verarbeiten können.
Damit haben Sie die Möglichkeit mehrere Ereignisfunktionen für dasselbe Ereignis aufrufen zu können und so Applikationskomponenten, die zur gleichen Zeit die gleichen Oberflächenobjekte verwenden, unabhängig voneinander zu implementieren.
Beispielsweise können Sie das Ereignis EvtLstSelect
eines DataList-Objektes in zwei unabhängigen Komponenten "A" und "B" verwenden. Komponente A könnte sich um die Aktualisierung von Steuerobjekten, wie Werkzeugleiste und Schaltflächen kümmern und Komponente B um die Anzeige zusätzlicher Informationen.
Über die nativen Funktionen WinEvtProcNameSet()
und WinEvtProcNameGet()
ist es bereits möglich zur Laufzeit der Applikation Ereignisfunktionen zu "registrieren" und zu ermitteln. Dabei kann allerdings pro Objekt und Ereignis immer nur eine Funktion angegeben werden.
In der am Ende des Artikels als Download bereit gestellten Prozedur befindet sich eine Implementierung des Observer/Listener-Entwurfsmusters für die CONZEPT 16-Ereignisverarbeitung bei Oberflächenobjekten.
Dabei wird beim Auslösen eines Ereignisses durch ein Oberflächenobjekt nicht nur eine Ereignisfunktion aufgerufen, sondern alle die zu diesem Zeitpunkt für dieses Ereignis in diesem Objekt registriert sind.
Die Prozedur stellt unter anderem zwei wesentliche Funktionen zur Verfügung:
-
Evt.Add( aWinObject : handle; // Oberflächenobjekt aEvtID : int; // Ereignis-ID aCall : alpha(61); // Ereignisfunktion ) : logic // Erfolg
-
Evt.Remove( aWinObject : handle; // Oberflächenobjekt aEvtID : int; // Ereignis-ID aCall : alpha(61); // Ereignisfunktion ) : logic // Erfolg
Mit der Funktion Evt.Add()
kann eine Ereignisfunktion zu einem Objekt und einem Ereignis hinzugefügt und mit Evt.Remove()
wieder entfernt werden.
Die beiden Funktionen haben die gleiche Signatur, also die gleichen Argument- und Rückgabetypen, wie die Funktion WinEvtProcNameSet()
. Im Gegensatz zu dieser Funktion, werden die Ereignisfunktionen bei Evt.Add()
und Evt.Remove()
dem Oberflächenobjekt nicht direkt als Ereignisfunktion zugewiesen, sondern in einer Liste im Speicher vorgehalten und zu gegebenem Zeitpunkt aufgerufen.
Die Aufrufreihenfolge der registrierten Ereignisfunktionen hängt dabei von der Reihenfolge ihrer Registrierung ab.
Im folgenden Beispiel sind zwei unabhängige Komponenten "A" und "B" realisiert:
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Prozedur "Component" +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
@A+
@C+
@I:SysEvtInc // Ereignisverarbeitung einbinden
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Komponente A initialisieren +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub ComponentAInit
(
aWinDataList : handle; // DataList
)
{
// Ereignis hinzufügen
aWinDataList->Evt.Add(_WinEvtLstSelect,
'Component:EvtLstSelectA');
// weitere Initialisierungen
// ...
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Komponente initialisieren +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub ComponentBInit
(
aWinDataList : handle; // DataList
)
{
// Ereignis hinzufügen
aWinDataList->Evt.Add(_WinEvtLstSelect,
'Component:EvtLstSelectB');
// weitere Initialisierungen
// ...
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Komponente A terminieren +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub ComponentATerm
(
aWinDataList : handle; // DataList
)
{
// Ereignis entfernen
aWinDataList->Evt.Remove(_WinEvtLstSelect,
'Component:EvtLstSelectA');
// weitere Terminierungen
// ...
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Komponente B terminieren +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub ComponentBTerm
(
aWinDataList : handle; // DataList
)
{
// Ereignis entfernen
aWinDataList->Evt.Remove(_WinEvtLstSelect,
'Component:EvtLstSelectB');
// weitere Terminierungen
// ...
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Ereignis Komponente A +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub EvtLstSelectA
(
aEvt : event; // Ereignis
aID : int; // Zeile
)
: logic; // ohne Bedeutung
{
WinDialogBox(0, 'Komponente A',
'Zeile ' + CnvAI(aID) + ' selektiert.',
_WinIcoInformation, _WinDialogOK, 1);
// Verarbeitung
// ...
return(true);
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Ereignis Komponente B +
// +++++++++++++++++++++++++++++++++++++++++++++++++++++
sub EvtLstSelectB
(
aEvt : event; // Ereignis
aID : int; // Zeile
)
: logic; // ohne Bedeutung
{
WinDialogBox(0, 'Komponente B',
'Zeile ' + CnvAI(aID) + ' selektiert.',
_WinIcoInformation, _WinDialogOK, 1);
// Verarbeitung
// ...
return(true);
}
Beide Komponenten verwenden ein DataList-Objekt und beide registrieren sich für das Ereignis EvtLstSelect
.
Innerhalb der Applikation könnten die beiden Komponenten zum Beispiel wie folgt verwendet werden:
main
local
{
tWinFrame : handle;
}
{
Evt.Init();
tWinFrame # WinOpen('ComponentFrame', _WinOpenDialog);
with ComponentFrame
{
$:dlData->WinLstDatLineAdd('1');
$:dlData->WinLstDatLineAdd('2');
$:dlData->WinLstDatLineAdd('3');
$:dlData->WinLstDatLineAdd('4');
// Komponenten initialisieren
ComponentAInit($:dlData);
ComponentBInit($:dlData);
tWinFrame->WinDialogRun(_WinDialogCenterScreen);
// Komponenten terminieren
ComponentBTerm($:dlData);
ComponentATerm($:dlData);
}
tWinFrame->WinClose();
Evt.Term();
}
Durch die Reihenfolge der Initialisierung der Komponenten ergibt sich auch die Reihenfolge der Aufrufe der Ereignisfunktionen: Wird eine Zeile in dem DataList-Objekt selektiert, wird zuerst die Funtion EvtLstSelectA
und anschließend die Funktion EvtLstSelectB
aufgerufen.
Ich konnte mit diesem Verfahren bereits in mehreren Anwendungen die Erweiterbarkeit und Wartbarkeit von unabhängigen Applikationskomponenten gewährleisten beziehnungsweise verbessern. Und vielleicht haben Sie auch die ein oder andere Idee, für die diese Technik eine mögliche Lösung darstellt.
2 Antworten
Ich habe die Prozedur "SysEvt" im Download aktualisiert: Es sind jetzt auch die mit der Version 5.6 hinzugekommenen Ereignisse enthalten.
Sehr interessanter Artikel!