I need to pass an event as a parameter to a function. Is there a way of doing this?
The reason is that I have a sequence of two lines of code that is littered all over my program, where I dynamically remove the handler to an event, and then set the handler again. I'm doing this for several different events and event handlers, so I've decided to write a function that does this.
As an example, let's say I have a combobox in my code called combobox1, and I have the handler called indexChangedHandler. In several places of my code, I have the following two lines:
RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler
Now, I don't want to keep on repeating the above two lines of code (or similar) all over my program, so I'm looking for a way to do this:
Private Sub setHandler(evt As Event, hndler As eventhandler)
RemoveHandler evt, hndler
AddHandler evt, hndler
End Sub
so that everywhere where those two lines of code(or similar) occur in my program, I can just replace them with:
setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler)
So far, the "evt as Event" part of the argument of the setHandler function is giving an error.
P.S: I've asked this question on a couple of other forums and keep getting asked why I would want to set the handler immediately after removing it. The reason is because dynamically adding an event handler n-times causes the handler to be executed n-times when the event occurs. To avoid this, that is, to ensure that the handler is executed just once when the event occurs, I first remove the handler each time I want to add the handler dynamically.
You might be asking why the handler would be added several times in the first place... The reason is because I add the handler only after a particular event, say E1, in my form has occurred (I add the handler within the handler of event E1). And event E1 can occur several times within my form. If I do not remove the handler each time before adding it again, the handler gets added and thus executed several times.
Whatever the case, the processing occurring within the function is not of ultimate importance to me at this time, but rather just the means of passing an event as a parameter.
Of course you can. (If it was the event you define yourself, you would need to create you own event arguments class where you would put all the information you need.
Of course you can pass events around... Well you can pass Action(Of EventHandler)
which can do what you want.
If you have a class that defines an event like so:
Public Class ComboBox
Public Event SelectedIndexChanged As EventHandler
End Class
Given an instance of ComboBox
you can then create add & remove handler actions like so:
Dim combobox1 = New ComboBox()
Dim ah As Action(Of EventHandler)
= Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As Action(Of EventHandler)
= Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h
(Now, this is VB.NET 4.0 code, but you can do this in 3.5 using AddressOf
and a little more mucking about.)
So if I have a handler Foo
:
Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs)
Console.WriteLine("Over here with Foo!")
End Sub
And a Raise
method on ComboBox
I can now do this:
ah(AddressOf Foo)
combobox1.Raise()
rh(AddressOf Foo)
This writes the message "Over here with Foo!" as expected.
I can also create this method:
Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler))
h(AddressOf Foo)
End Sub
I can pass around the event handler actions like so:
PassActionOfEventHandler(ah)
combobox1.Raise()
PassActionOfEventHandler(rh)
Which again writes the message "Over here with Foo!".
Now, one issue that might be a problem is that you can accidentally swap the add and remove event handler delegates in code - after all they are the same type. So it is easy to just define strongly-typed delegates for the add and remove actions like so:
Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T)
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T)
The code to define the delegate instances doesn't change except for the delegate types:
Dim ah As AddHandlerDelegate(Of EventHandler)
= Sub (h) AddHandler combobox1.SelectedIndexChanged, h
Dim rh As RemoveHandlerDelegate(Of EventHandler)
= Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h
So this now lets you be very creative. You can define a function that will take the add handler delegate, the remove handler delegate and a event handler, which will wire-up an event handler, and then return to you an IDisposable
that you can use later to remove the handler without needing to retain a reference to the event handler. This is handy for using Using
statements.
Here's the signature:
Function SubscribeToEventHandler(
ByVal h as EventHandler,
ByVal ah As AddHandlerDelegate(Of EventHandler),
ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable
So given this function I can now do this:
combobox1.Raise()
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh)
combobox1.Raise()
combobox1.Raise()
End Using
combobox1.Raise()
And this writes the message "Over here with Foo!" only twice. The first and last Raise
calls are outside of the subscription to the event handler.
Enjoy!
You cannot pass events around. Event
isn't a type, it's a keyword that defines a pair of methods, add and remove, used to change the state of the event member of the class. They are very much like properties in this respect (which have get and set methods).
Like properties, the add and remove methods can do whatever you want. Typically, these will do nothing more than maintain a delegate instance, which itself is a MulticastDelegate
or in other words, a list of delegates which are getting called one by one if the event is raised.
You can clearly see this structure in C#, where it is not masked with AddHandler
/RemoveHandler
, but you directly edit the list of associated handlers: myObject.Event += new Delegate(...);
.
And, again like properties, being a member of a class that actually abstracts two different methods, it's not possible to literally pass an event as an object.
There are a number of different meanings for the term "event", depending upon context. In the context you are seeking, an Event is a matched pair of methods that will effectively add or remove a delegate from a subscription list. Unfortunately, there's no class in VB.NET to represent a method address without an attached object. One could probably use reflection to fetch the appropriate methods, and then pass those methods to a routine that would call them both, but in your particular situation that would probably be silly.
The only situation where I can see that it might be useful to pass an event in the sense you describe would be for something like an IDisposable object which subscribes to events from longer-lived objects, and which needs to unsubscribe all of its events when it is disposed. In that case, it may be helpful to use reflection to fetch the remove-delegate method for each event, and then call all of the remove-delegate methods when an object is disposed. Unfortunately, I know of no way to represent the events to be retrieved except as string literals, whose validity could not be checked until run-time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With