| 1 | = Events = |
| 2 | |
| 3 | An event is a message sent by an object to signal the occurrence of an action. |
| 4 | |
| 5 | The object that raises (triggers) the event is called the event sender or event source.[[BR]] |
| 6 | The object that receives the event and responds to it is called the event receiver or Listener |
| 7 | |
| 8 | An event is declared with a type corresponding to a method signature ('''sig''') |
| 9 | |
| 10 | Objects that have an interest in the event register to '''listen''' to the event[[BR]] |
| 11 | (more specifically provide to the event a reference to one of their methods matching the [[BR]] |
| 12 | event signature). |
| 13 | |
| 14 | When the event is '''raised''' (or fires) any and all objects that registered an interest in [[BR]] |
| 15 | the event (listeners) get their (registered) method called with the arguments provided[[BR]] |
| 16 | when raising the event. |
| 17 | |
| 18 | Objects may unregister a prior interest in an event by subsequently '''ignoring''' the event[[BR]] |
| 19 | specifying the same method reference they previously listened with. |
| 20 | |
| 21 | On these constructs decoupled notification and handling can be built.[[BR]] |
| 22 | All that is required is that an objects events of interest are exposed and[[BR]] |
| 23 | the signature of the events are known to interested parties |
| 24 | (or a registration interface is available) . |
| 25 | |
| 26 | == Grammar == |
| 27 | {{{ |
| 28 | |
| 29 | # specify the signature of an event |
| 30 | sig SIG_TYPENAME(ARGS) as TYPE |
| 31 | |
| 32 | # declare an event |
| 33 | event EVENT_NAME as SIG_TYPENAME |
| 34 | |
| 35 | #register handler/listener method for an event |
| 36 | listen EVENT_NAME, ref METHODNAME |
| 37 | |
| 38 | #unregister handler/listener for an event |
| 39 | ignore EVENT_NAME, ref METHODNAME |
| 40 | |
| 41 | # raise/fire an event |
| 42 | raise EVENT_NAME, [sender,] arg [, arg ...] |
| 43 | }}} |
| 44 | |
| 45 | == Platform == |
| 46 | |
| 47 | == Examples == |
| 48 | This (large) example does it all.[[BR]] |
| 49 | Declare Event arguments, declare signature and events[[BR]] |
| 50 | Raise events in response to something happening, define receivers/listeners that are[[BR]] |
| 51 | interested in the event ('something happening'), bind event senders and listeners together. |
| 52 | |
| 53 | (FWIW its the C# example at [http://msdn.microsoft.com/en-us/library/9aackb16%28v=VS.71%29.aspx Event Sample] converted to cobra.) |
| 54 | {{{ |
| 55 | namespace EventSample |
| 56 | |
| 57 | # Data for the alarm event. Derives from System.EventArgs. |
| 58 | class AlarmEventArgs inherits EventArgs |
| 59 | |
| 60 | var __snoozePressed as bool #is private #,readonly |
| 61 | var _nrings as int is private #,readonly |
| 62 | |
| 63 | cue init(snoozePressed as bool, nrings as int) |
| 64 | base.init |
| 65 | __snoozePressed = snoozePressed |
| 66 | _nrings = nrings |
| 67 | |
| 68 | # number of rings that the alarm clock has sounded when the alarm event is generated. |
| 69 | get numRings from _nrings |
| 70 | |
| 71 | # indicates whether the snooze button is pressed on the alarm when the alarm event is generated. |
| 72 | get snoozePressed from __snoozePressed |
| 73 | |
| 74 | # alarmText property that contains the wake-up message. |
| 75 | get alarmText |
| 76 | return if(__snoozePressed, 'Wake Up!!! Snooze time is over.', 'Wake Up!') |
| 77 | |
| 78 | |
| 79 | # Delegate declaration. |
| 80 | sig AlarmEventHandler(sender as Object, e as AlarmEventArgs ) |
| 81 | |
| 82 | # The Alarm class that raises the alarm event. |
| 83 | class AlarmClock |
| 84 | var _snoozePressed = false |
| 85 | is private |
| 86 | var _nrings = 0i32 # int32 |
| 87 | is private |
| 88 | var _stop = false |
| 89 | is private |
| 90 | |
| 91 | # The Stop property indicates whether the alarm should be turned off. |
| 92 | pro stop from _stop |
| 93 | |
| 94 | # The SnoozePressed property indicates whether the snooze |
| 95 | # button is pressed on the alarm when the alarm event is generated. |
| 96 | pro snoozePressed from var |
| 97 | |
| 98 | # The event member that is of type AlarmEventHandler. |
| 99 | event alarm as AlarmEventHandler |
| 100 | |
| 101 | # The protected OnAlarm method raises the event by invoking the delegate. |
| 102 | # The sender is always .this, the current instance of the class. |
| 103 | def onAlarm(e as AlarmEventArgs) is protected |
| 104 | # Invokes the delegate. |
| 105 | raise .alarm, e |
| 106 | |
| 107 | # Alarm clock does not have a user interface. |
| 108 | # To simulate the alarm mechanism it has a loop that raises the alarm event at every iteration |
| 109 | # with a time delay of 300 milliseconds, if snooze is not pressed. |
| 110 | # If snooze is pressed, the time delay is 1000 milliseconds. |
| 111 | def start |
| 112 | while true |
| 113 | _nrings += 1 |
| 114 | if _stop |
| 115 | break |
| 116 | else if _snoozePressed |
| 117 | System.Threading.Thread.sleep(1000) |
| 118 | e = AlarmEventArgs(_snoozePressed, _nrings) |
| 119 | .onAlarm(e) |
| 120 | else |
| 121 | System.Threading.Thread.sleep(300) |
| 122 | e = AlarmEventArgs(_snoozePressed, _nrings) |
| 123 | .onAlarm(e) |
| 124 | |
| 125 | # The WakeMeUp class that has a method AlarmRang that handles the alarm event. |
| 126 | class WakeMeUp |
| 127 | def alarmRang(sender as Object, e as AlarmEventArgs) |
| 128 | print e.alarmText |
| 129 | if not e.snoozePressed |
| 130 | if e.numRings % 10 == 0 |
| 131 | print ' Let alarm ring? Enter Y' |
| 132 | print ' Press Snooze? Enter N' |
| 133 | print ' Stop Alarm? Enter Q' |
| 134 | input = Console.readLine |
| 135 | if input.equals('Y') or input.equals('y') |
| 136 | return |
| 137 | else if input.equals('N') or input.equals('n') |
| 138 | (sender to AlarmClock).snoozePressed = true |
| 139 | return |
| 140 | else |
| 141 | (sender to AlarmClock).stop = true |
| 142 | return |
| 143 | else |
| 144 | print ' Let alarm ring? Enter Y' |
| 145 | print ' Stop Alarm? Enter Q' |
| 146 | input = Console.readLine |
| 147 | if input.equals('y') or input.equals('y') |
| 148 | return |
| 149 | else |
| 150 | (sender to AlarmClock).stop = true |
| 151 | return |
| 152 | |
| 153 | # The driver class that hooks up the event handling method of |
| 154 | # WakeMeUp to the alarm event of an Alarm object using a delegate. |
| 155 | class AlarmDriver |
| 156 | def main is shared |
| 157 | # args = CobraCore.commandLineArgs |
| 158 | w = WakeMeUp() # Instantiates the event receiver. |
| 159 | |
| 160 | clock = AlarmClock() # Instantiates the event source. |
| 161 | |
| 162 | # Wires the AlarmRang method to the Alarm event. |
| 163 | listen clock.alarm, ref w.alarmRang |
| 164 | # clock.alarm += new AlarmEventHandler(w.AlarmRang) |
| 165 | |
| 166 | clock.start |
| 167 | |
| 168 | }}} |
| 169 | |
| 170 | Perhaps more commonly the sigs, events and senders are already defined (in a library or framework)[[BR]] |
| 171 | and we are only interested in registering for an event and reacting when the event is raised. |
| 172 | |
| 173 | Heres an example of that around some GUI components:[[BR]] |
| 174 | The buttons and form have Keypress and !MouseClick events and associated machinery already defined for them.[[BR]] |
| 175 | The example just sets up binding to the listener methods and 'does something' when the events fire [[BR]] |
| 176 | (print some of the args fields in this case). |
| 177 | {{{ |
| 178 | %% args -target:exe |
| 179 | |
| 180 | use System.Windows.Forms |
| 181 | use System.Drawing |
| 182 | |
| 183 | class MyForm |
| 184 | inherits Form |
| 185 | |
| 186 | cue init |
| 187 | base.init |
| 188 | |
| 189 | b = Button() |
| 190 | b.text="press me" |
| 191 | .controls.add(b) |
| 192 | |
| 193 | listen b.mouseClick, ref .handleMouseClick |
| 194 | listen b.keyPress, ref .handleKey |
| 195 | listen .keyPress, ref .handleKey |
| 196 | |
| 197 | def handleClick(sender as Object, args as EventArgs) |
| 198 | m = args to MouseEventArgs |
| 199 | print 'btn=[m.button]' |
| 200 | print "Locn=[m.location]" |
| 201 | print m |
| 202 | |
| 203 | def handleMouseClick(sender as Object, m as MouseEventArgs) |
| 204 | print 'btn =[m.button]' |
| 205 | print 'mb=[Control.mouseButtons]' |
| 206 | print 'delta =[m.delta]' |
| 207 | print 'clicks =[m.clicks]' |
| 208 | print "Locn =[m.location]" |
| 209 | print m |
| 210 | |
| 211 | def handleKey(sender as Object, kp as KeyPressEventArgs) |
| 212 | print 'KeyPressed', 'key=', kp.keyChar |
| 213 | |
| 214 | class Program |
| 215 | |
| 216 | def main is shared has STAThread |
| 217 | Application.run(MyForm()) |
| 218 | }}} |
| 219 | |
| 220 | == Notes == |
| 221 | |
| 222 | With reference to C# nomenclature |
| 223 | * a cobra '''sig''' is roughly comparable to a C# delegate |
| 224 | * An event is an event in both |
| 225 | * 'wiring method to event' or 'instantiating an event delegate' is done in cobra via a '''listen''' statement |
| 226 | * 'Invoking a delegate' is done in cobra via the '''raise''' statement. |
| 227 | |
| 228 | Declaration/execution flow is |
| 229 | '''sig''' - '''event''' - '''listen''' - '''raise''' - '''(ignore)''' |
| 230 | |
| 231 | |
| 232 | == See Also == |
| 233 | [http://msdn.microsoft.com/en-us/library/edzehd2t%28v=VS.71%29.aspx Handling and raising events] |
| 234 | in .Net Framework Developers Guide |
| 235 | |
| 236 | |
| 237 | [wiki:LanguageTopics Back to LanguageTopics] |