Wiki
Version 2 (modified by todd.a, 7 years ago)

--

Events

An event is a message sent by an object to signal the occurrence of an action.

The object that raises (triggers) the event is called the event sender or event source.
The object that receives the event and responds to it is called the event receiver or Listener

An event is declared with a type corresponding to a method signature (sig)

Objects that have an interest in the event register to listen to the event
(more specifically provide to the event a reference to one of their methods matching the
event signature).

When the event is raised (or fires) any and all objects that registered an interest in
the event (listeners) get their (registered) method called with the arguments provided
when raising the event.

Objects may unregister a prior interest in an event by subsequently ignoring the event
specifying the same method reference they previously listened with.

On these constructs decoupled notification and handling can be built.
All that is required is that an objects events of interest are exposed and
the signature of the events are known to interested parties (or a registration interface is available) .

Grammar


# specify the signature of an event
sig SIG_TYPENAME(ARGS) as TYPE

# declare an event
event EVENT_NAME as SIG_TYPENAME

#register handler/listener method for an event
listen EVENT_NAME, ref METHODNAME

#unregister handler/listener for an event
ignore EVENT_NAME, ref METHODNAME

# raise/fire an event
raise EVENT_NAME, [sender,] arg [, arg ...]

Platform

Examples

This (large) example does it all.
Declare Event arguments, declare signature and events
Raise events in response to something happening, define receivers/listeners that are
interested in the event ('something happening'), bind event senders and listeners together.

(FWIW its the C# example at  Event Sample converted to cobra.)

namespace EventSample

    # Data for the alarm event. Derives from System.EventArgs.
    class AlarmEventArgs inherits EventArgs 
     
        var __snoozePressed as bool #is private #,readonly
    var _nrings        as int is private    #,readonly
      
    cue init(snoozePressed as bool, nrings as int) 
        base.init
        __snoozePressed = snoozePressed
        _nrings = nrings
      
        # number of rings that the alarm clock has sounded when the alarm event is generated.
    get numRings from _nrings
      
        # indicates whether the snooze button is pressed on the alarm when the alarm event is generated.
    get snoozePressed from __snoozePressed
      
        # alarmText property that contains the wake-up message.
    get alarmText 
        return if(__snoozePressed, 'Wake Up!!! Snooze time is over.', 'Wake Up!')
   
   
    # Delegate declaration.
    sig AlarmEventHandler(sender as Object, e as AlarmEventArgs )

    # The Alarm class that raises the alarm event.
    class AlarmClock 
    var _snoozePressed = false 
        is private
    var _nrings = 0i32  # int32
        is private 
    var _stop = false
        is private
    
    # The Stop property indicates whether the alarm should be turned off.
    pro stop from _stop
      
    # The SnoozePressed property indicates whether the snooze
    # button is pressed on the alarm when the alarm event is generated.
    pro snoozePressed from var 
      
    # The event member that is of type AlarmEventHandler.
    event alarm as AlarmEventHandler 

    # The protected OnAlarm method raises the event by invoking the delegate. 
    # The sender is always .this, the current instance of the class.
    def onAlarm(e as AlarmEventArgs) is protected
            # Invokes the delegate. 
        raise .alarm, e
      
    # Alarm clock does not have a user interface. 
    # To simulate the alarm mechanism it has a loop that raises the alarm event at every iteration
    # with a time delay of 300 milliseconds, if snooze is not pressed. 
    # If snooze is pressed, the time delay is 1000 milliseconds.
    def start
        while true
            _nrings += 1
        if _stop
            break
        else if _snoozePressed
            System.Threading.Thread.sleep(1000)
            e = AlarmEventArgs(_snoozePressed, _nrings)
            .onAlarm(e)
        else
            System.Threading.Thread.sleep(300)
            e = AlarmEventArgs(_snoozePressed, _nrings)
            .onAlarm(e)
   
    # The WakeMeUp class that has a method AlarmRang that handles the alarm event.
    class WakeMeUp
        def alarmRang(sender as Object, e as AlarmEventArgs)
            print e.alarmText
        if not e.snoozePressed
            if e.numRings % 10 == 0
                print ' Let alarm ring? Enter Y'
            print ' Press Snooze? Enter N'
            print ' Stop Alarm? Enter Q'
            input = Console.readLine
            if input.equals('Y') or input.equals('y')
                return
            else if input.equals('N') or input.equals('n')
                (sender to AlarmClock).snoozePressed = true
                return
            else
                (sender to AlarmClock).stop = true
                return
        else
            print ' Let alarm ring? Enter Y'
            print ' Stop Alarm? Enter Q'
            input = Console.readLine
            if input.equals('y') or input.equals('y')
                return
            else 
            (sender to AlarmClock).stop = true
            return
   
    # The driver class that hooks up the event handling method of
    # WakeMeUp to the alarm event of an Alarm object using a delegate.
    class AlarmDriver
        def main is shared
        # args = CobraCore.commandLineArgs
        w = WakeMeUp()  # Instantiates the event receiver.

        clock = AlarmClock() # Instantiates the event source.

        # Wires the AlarmRang method to the Alarm event.
        listen clock.alarm, ref w.alarmRang
        # clock.alarm += new AlarmEventHandler(w.AlarmRang)

        clock.start
      

Perhaps more commonly the sigs, events and senders are already defined (in a library or framework)
and we are only interested in registering for an event and reacting when the event is raised.

Heres an example of that around some GUI components:
The buttons and form have Keypress and MouseClick events and associated machinery already defined for them.
The example just sets up binding to the listener methods and 'does something' when the events fire
(print some of the args fields in this case).

%% args -target:exe

use System.Windows.Forms
use System.Drawing

class MyForm
    inherits Form

    cue init
        base.init

        b = Button()
        b.text="press me"
        .controls.add(b)

        listen b.mouseClick, ref .handleMouseClick
        listen b.keyPress, ref .handleKey
        listen .keyPress, ref .handleKey

    def handleClick(sender as Object, args as EventArgs)
        m = args to MouseEventArgs
        print 'btn=[m.button]'
        print "Locn=[m.location]"
        print m

    def handleMouseClick(sender as Object, m as MouseEventArgs)
        print 'btn    =[m.button]'
        print 'mb=[Control.mouseButtons]'
        print 'delta =[m.delta]'
        print 'clicks =[m.clicks]'
        print "Locn   =[m.location]"
        print m

    def handleKey(sender as Object, kp as KeyPressEventArgs)
        print 'KeyPressed', 'key=', kp.keyChar
        
class Program

    def main is shared has STAThread
        Application.run(MyForm())

Notes

With reference to C# nomenclature

  • a cobra sig is roughly comparable to a C# delegate
  • An event is an event in both
  • 'wiring method to event' or 'instantiating an event delegate' is done in cobra via a listen statement
  • 'Invoking a delegate' is done in cobra via the raise statement.

Declaration/execution flow is

sig - event - listen - raise - (ignore)

See Also

 Handling and raising events in .Net Framework Developers Guide

Back to LanguageTopics