Event-Handling (wie in DotNET)
Verfasst: 15.04.2008 18:02
Ich habe hier mal versucht, EventHandling in PureBasic im Styl von DotNET zu verwirklichen. Das Beispiel hat evtl. in PureBasic nicht richtig den Sinn getroffen, aber man kann verstehen, was der Sinn von EventHandling ist. Wenn man noch spaß hat, kann man sich eine Window-Lib basteln (sollte nicht allzu schwer sein, wenn man sich in der API auskennt).
Beispiel:
Code: Alles auswählen
;-{ Definition von Macros, die verwendet werden
Macro false : 0 : EndMacro
Macro true : 1 : EndMacro
Macro nil : 0 : EndMacro
Macro null : 0 : EndMacro
Macro Is(expression)
( false Or (expression) )
EndMacro
;}
;-{ EventArg (für den *e - Parameter)
;/ Diese Struktur dient dazu, wenn Default-Argumente für Events
;/ definiert werden sollen. Bis jetzt war dies nicht nötig.
;/ (bitte bei Veröffendlichten Projekten mit dieser Lib nicht diese
;/ Struktur verändern !!! - dies führt zu Komplikationen)
Structure EventArgAtom
EndStructure
;# <description>
;# Gibt die Struktur des Event-Argument zurück.
;# </description>
;# <param name="EventArgName" type="identifier">
;# Der Name des Event-Argument.
;# </param>
;# <return type="identifier<structure>">
;# Die Struktur des Event-Argument.
;# </return>
Macro eArg(EventArgName)
EventArg_#EventArgName
EndMacro
;# <description>
;# Definiert ein neues Event-Argument.
;# </description>
;# <param name="EventArgName" type="identifier">
;# Der Name des neuen Event-Attributes.
;# </param>
;# <param name="m_Extends" type="identifier">
;# Name des EventArgument, von dem die Argumente "geerbt" werden.
;# </param>
Macro EventArg(EventArgName, m_Extends = EventArgAtom)
CompilerIf Defined(eArg(EventArgName), #PB_Structure) = 0
Structure eArg(EventArgName) Extends m_Extends
EndMacro
Macro EndEventArg
EndStructure
CompilerEndIf
EndMacro
;}
;-{ Event
;/ die Event-Call-Funktion
Prototype Event_Callback(*sender, *e)
;/ Ein Element aus der Liste der Call-Funktionen
Structure EventItem
;// Listen-Verknüpfung
*Prev.EventItem ;/ das vorherige Element
*Next.EventItem ;/ das nächste Element
;// Listen-Inhalt
Call.Event_Callback ;/ die Call-Funktion dieses Elementes
EndStructure
;/ Die Event-Struktur, die pro Event abgespeichert wird
Structure Event
;/ für Debug-Zwecke ist es hier, wenn gewollt, möglich, den Namen des Eventes abzulegen
CompilerIf #PB_Compiler_Debugger
EventName.s
CompilerEndIf
*list.EventItem ;/ Die Liste der Call-Funktionen
EndStructure
;# <description>
;# Erstellt eine neue Event-Liste.
;# </description>
Macro NewEvent()
AllocateMemory(SizeOf(Event))
EndMacro
Procedure.l NextEventCall(*event.Event) ;/ springt (wenn möglich) zum nächsten Element der Liste
If *event\list\Next
*event\list = *event\list\Next
ProcedureReturn true
EndIf
EndProcedure
Procedure.l PreviousEventCall(*event.Event) ;/ springt (wenn möglich) zum vorherigen Element der Liste
If *event\list\Prev
*event\list = *event\list\Prev
ProcedureReturn true
EndIf
EndProcedure
Procedure.l FirstEventCall(*event.Event) ;/ springt (wenn möglich) zum ersten Element der Liste
If *event\list
While PreviousEventCall(*event)
Wend
ProcedureReturn true
EndIf
EndProcedure
Procedure.l LastEventCall(*event.Event) ;/ springt (wenn möglich) zum letzten Element der Liste
If *event\list
While NextEventCall(*event)
Wend
ProcedureReturn true
EndIf
EndProcedure
Procedure AddEventCall(*event.Event, *call.Event_Callback) ;/ fügt eine neue Call-Funktion zur Event-Liste hinzu
If *event
;/ erstelle neues Listen-Element
Protected *newItem.EventItem = AllocateMemory(SizeOf(EventItem))
*newItem\Call = *call
;/ verknüpfe Listen-Element
If *event\list <> null
If *event\list\Next
*newItem\Next = *event\list\Next
*event\list\Next\Prev = *newItem
EndIf
*newItem\Prev = *event\list
*event\list\Next = *newItem
EndIf
;/ setzte das neue Listen-Elemet als aktuelles
*event\list = *newItem
EndIf
EndProcedure
Procedure DeleteEventCall(*event.Event) ;/ entfernt die akturelle Call-Funktion der Event-Liste
If *event And *event\list
Protected *tmpItem.EventItem = *event\list
If *event\list\Next
If *event\list\Prev
;/ verfollständige Verknüpfung
*event\list\Next\Prev = *event\list\Prev
*event\list\Prev\Next = *event\list\Next
;/ setzte neues aktuelles Element
*event\list = *event\list\Prev
Else
;/ verfollständige Verknüpfung
*event\list\Next\Prev = null
;/ setzte neues aktuelles Element
*event\list = *event\list\Next
EndIf
ElseIf *event\list\Prev
;/ verfollständige Verknüpfung
*event\list\Prev\Next = null
;/ setzte neues aktuelles Element
*event\list = *event\list\Prev
Else
;/ setzte neues aktuelles Element
*event\list = null
EndIf
;/ gebe das aktuelle Element frei
FreeMemory(*tmpItem)
EndIf
EndProcedure
;# <description>
;# Dient zum Durchlauf der Event-Aufrufe in der Event-Call-Liste.
;# </description>
Macro ForEach_Event(m_Event)
If m_Event And m_Event\list
FirstEventCall(m_Event)
While (m_Event\list)
EndMacro
Macro Next_Event(m_Event)
NextEventCall(m_Event)
Wend
EndIf
EndMacro
;# <description>
;# Gibt alle angegebenen Event-Aufrufe für dieses Event frei.
;# </description>
;# <param attr="(pointer)" name="event" type="Event">
;# Der Pointer des Events.
;# </param>
Procedure ClearEventCallList(*event.Event)
If *event And *event\list
;/ springe zum ersten Element der Liste
FirstEventCall(*event)
;/ durchlaufe die Liste und lösche die Call-Aufrufe
While (Not *event\list = null)
NextEventCall(*event)
FreeMemory(*event\list\Prev)
Wend
EndIf
EndProcedure
;# <description>
;# Gibt das angegebene Event wieder frei.
;# </description>
;# <param attr="(pointer)" name="event" type="Event">
;# Der Pointer des Events.
;# </param>
Macro FreeEvent(m_Event)
ClearEventCallList(m_Event)
FreeMemory(m_Event)
EndMacro
;}
;# <description>
;# Mit dieser Funktion ruft man einen Event auf (alle Call-Proceduren, die in die Liste eingeschrieben wurden).
;# </description>
;# <param attr="(pointer)" name="event" type="Event">
;# Der Pointer des Events.
;# </param>
;# <param attr="(sender)" name="sender" type="void">
;# Das Objekt, dem die Event-Liste angehört.
;# </param>
;# <param attr="(pointer)" name="e" type="EventArgAtom">
;# Der Event-Argumen-Parameter.
;# </param>
Procedure CallEvent(*event.Event, *sender, *e.EventArgAtom)
If *event And *event\list
;/ Springe zum ersten Element der Liste
While *event\list\Prev <> null
*event\list = *event\list\Prev
Wend
;/ durchlaufe die Liste des Eventes und rufe jeden Funktion auf
While *event\list
;/ rufe den Event auf
*event\list\Call(*sender, *e)
;/ nächstes Element der Liste
If *event\list\Next = null : Break : EndIf
*event\list = *event\list\Next
Wend
EndIf
EndProcedure
;-/ Default-Typen
;# <description>
;# Dies ist das Default-Event. Es wird dann verwendet, wenn keine Event-Attribute angegeben werden.
;# </description>
EventArg(Empty)
EndEventArg
Code: Alles auswählen
;{ Application
Enumeration ;#Application_Close_Reason_*
#Application_Close_Reason_Unknow = 0;/ unbekannter Schließe-Grund
#Application_Close_Reason_CloseWindow
;[...]
EndEnumeration
EventArg(Application_Close)
Reason.b ;/ der Grund, warum das Fenster beschlossen werden soll
BreakClose.b ;/ ob das Schließen des Programms abgebrochen werden soll.
EndEventArg
EventArg(Application_Closing)
Reason.b ;/ der Grund, warum das Fenster beschlossen werden soll
EndEventArg
Structure Application
*Close.Event ;*e.eArg(Application_Close) ;/ ob die Application geschlossen werden soll
*Closing.Event ;*e.eArg(Application_Closing) ;/ die Application wird geschlossen
EndStructure
;/ der lSize - Parameter ist für die "Vererbung" gedacht
Procedure NewApplication(lSize.l = SizeOf(Application))
If lSize < SizeOf(Application) : lSize = SizeOf(Application) : EndIf
Protected *this.Application = AllocateMemory(lSize)
With *this
\Close = NewEvent()
\Closing = NewEvent()
EndWith
ProcedureReturn *this
EndProcedure
Procedure FreeApplication(*this.Application)
If *this
With *this
FreeEvent(\Close)
FreeEvent(\Closing)
EndWith
FreeMemory(*this)
EndIf
EndProcedure
;}
Procedure WaitWindow(*Application.Application)
Protected lEvent.l
Protected *EventArg_Close.eArg(Application_Close)
Protected EventArg_Closing.eArg(Application_Closing)
Repeat
lEvent = WaitWindowEvent()
Select lEvent
Case #PB_Event_CloseWindow ;{
;/ definiere die Event-Argument-Struktur für Event-Aufruf vor
*EventArg_Close = AllocateMemory(SizeOf(eArg(Application_Close)))
*EventArg_Close\Reason = #Application_Close_Reason_CloseWindow
*EventArg_Close\BreakClose = false
;/ rufe ab, ob das Programm beendet werden soll
CallEvent(*Application\Close, *Application, *EventArg_Close)
;/ rufe die "Rückgabe" der Events zurück
If *EventArg_Close\BreakClose = false
EventArg_Closing\Reason = *EventArg_Close\Reason
FreeMemory(*EventArg_Close)
Break
EndIf
FreeMemory(*EventArg_Close)
;}
EndSelect
ForEver
CallEvent(*Application\Closing, *Application, @EventArg_Closing)
EndProcedure
;[...] - noch mehr Libs z.B. eine Window-Lib
Enumeration ;#Window_*
#Window_Main
EndEnumeration
Procedure Application_Close(*sender.Application, *e.eArg(Application_Close))
If *e\Reason = #Application_Close_Reason_CloseWindow
If MessageRequester("Programm beenden", "Soll das Programm wirklich beendet werden?", #PB_MessageRequester_YesNoCancel) <> #PB_MessageRequester_Yes
*e\BreakClose = true
EndIf
EndIf
EndProcedure
Procedure Application_Closing(*sender.Application, *e.eArg(Application_Closing))
CloseWindow(#Window_Main)
EndProcedure
;/ definiere Programm-Struktur
Define *MyApplication.Application = NewApplication()
AddEventCall(*MyApplication\Close, @Application_Close())
AddEventCall(*MyApplication\Closing, @Application_Closing())
;/ erstelle Fenster
OpenWindow(#Window_Main, #PB_Ignore, #PB_Ignore, 150, 100, "<title>", #PB_Window_SystemMenu)
;/ warte, bis Windows geschlossen wird (alle Aktionen sollten per Event-Handling geregelt werden)
WaitWindow(*MyApplication)
End