How to create non-blocking custom events in my class

m_elias

Member
Joined
Jan 3, 2012
Messages
11
Location
Canada
Programming Experience
1-3
I'm trying to implement this, How to: Declare Custom Events To Avoid Blocking, with more than one custom event. If I duplicate the Public Custom Event section and just change the event's name, then both events are called even if my code only calls one of them. I have made my own modification so that it works with more than one custom event but I think most likely someone else will have a better way of doing.

This is my class' code
VB.NET:
Public Class TestClass
    Private EventHandlerList As New ArrayList
    Private EventHandlerIndex As New ArrayList

    Public Sub TriggerMultipleEvents()
        RaiseEvent TestEvent1()
        RaiseEvent TestEvent2()
    End Sub

    Public Custom Event TestEvent1 As EventHandler
        AddHandler(value As EventHandler)
            EventHandlerList.Add(value)
            EventHandlerIndex.Add("TestEvent1")
        End AddHandler
        RemoveHandler(value As EventHandler)
            EventHandlerList.Remove(value)
            EventHandlerIndex.Remove("TestEvent1")
        End RemoveHandler
        RaiseEvent()
            For i As Integer = 0 To EventHandlerList.Count - 1
                If EventHandlerList(i) IsNot Nothing Then
                    If EventHandlerIndex(i) = "TestEvent1" Then
                        EventHandlerList(i).BeginInvoke(Nothing, Nothing, Nothing, Nothing)
                    End If
                End If
            Next
        End RaiseEvent
    End Event

    Public Custom Event TestEvent2 As EventHandler
        AddHandler(value As EventHandler)
            EventHandlerList.Add(value)
            EventHandlerIndex.Add("TestEvent2")
        End AddHandler
        RemoveHandler(value As EventHandler)
            EventHandlerList.Remove(value)
            EventHandlerIndex.Remove("TestEvent2")
        End RemoveHandler
        RaiseEvent()
            For i As Integer = 0 To EventHandlerList.Count - 1
                If EventHandlerList(i) IsNot Nothing Then
                    If EventHandlerIndex(i) = "TestEvent2" Then
                        EventHandlerList(i).BeginInvoke(Nothing, Nothing, Nothing, Nothing)
                    End If
                End If
            Next
        End RaiseEvent
    End Event

End Class

Here is the code on form1
VB.NET:
Imports System.Threading
Public Class MainForm
    Public WithEvents Test As New TestClass

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Test.TriggerMultipleEvents()
    End Sub

    Private Sub Test_TestEvent1() Handles Test.TestEvent1
        Console.WriteLine("testevent1 started")
        Thread.Sleep(1000)
        Console.WriteLine("testevent1 finished")
    End Sub

    Private Sub Test_TestEvent2() Handles Test.TestEvent2
        Console.WriteLine("testevent2 started")
        Thread.Sleep(100)
        Console.WriteLine("testevent2 finished")
    End Sub

End Class

The console output when button1 is clicked should be as follows because Test_TestEvent2() has a shorter thread.sleep in it.
VB.NET:
testevent1 started
testevent2 started
testevent2 finished
testevent1 finished
 
You don't add the handlers for both events to both ArrayLists. Each ArrayList is for the handlers for one event. If you have two events, TestEvent1 and TestEvent2, then you have two ArrayLists, TestEvent1HandlerList and TestEvent2HandlerList. Within TestEvent1 you add, remove and invoke only TestEvent1HandlerList items and within TestEvent2 you add, remove and invoke only TestEvent2HandlerList items.
 
Alternatively you use a dictionary-like collection as described in the related article: How to: Declare Custom Events To Conserve Memory
In this example, the class uses one instance of the EventHandlerList class, stored in the Events field, to store information about the events in use.
Each handler can be retrieved with Delegate.GetInvocationList Method.

Also note that the EventHandlerList used in this example is a specialized .Net class, someone funny has used that as variable name in the other example. That class makes it easier to work with event handler delegates, than a simple List(Of System.Delegate) or a Dictionary(Of String, System.Delegate) for instance. You shouldn't be using ArrayList class at all since .Net 1, look to generics as mentioned here for those uses.
 
@jmcilhinney, I was hoping to minimize the amount of code that needed to be replicated for each event.

@JohnH, I tried an exact copy of the example you mentioned, it does not raise the events asynchronously either and I'm too daft with this kind of thing to understand how to merge the two together. Can you explain how to modify that example's RaiseEvent code accordingly?

I'm not even sure if it's necessary to have non-blocking events. The other events in my class I just declare with Public Event EventName(). I was hoping there would be some one line command that would tell VB to run the events asynchronously.
 
EventHandlerList(i).BeginInvoke(Nothing, Nothing, Nothing, Nothing)
The above quote from your post is the code that calls the delegate asynchronous using BeginInvoke method. A few causes that prevent you from understanding this is that you have Option Strict off and use the typeless ArrayList class, so you don't get any help from compiler with using correct types in your code.
If you use the EventHandlerList object as in other article you can see there how they retrieve an EventHandler delegate. This is a multicast delegate, meaning multiple event handlers may be attached to this delegate. What I said is that you can use the GetInvocationList method of that delegate to get one delegate for each handler, cast this to the specific delegate type (EventHandler delegate for example), and call BeginInvoke on that. A quick example:
For Each handler In Events("SomeEvent").GetInvocationList()
    CType(handler, EventHandler).BeginInvoke(Me, EventArgs.Empty, Nothing, Nothing)
Next

I'm not even sure if it's necessary to have non-blocking events. The other events in my class I just declare with Public Event EventName().
Normally it is not necessary, it will also complicate things for event handler. For UI programming it is pointless.
I was hoping there would be some one line command that would tell VB to run the events asynchronously.
Raising an event from a secondary thread will also invoke the handlers from that thread, so you would get an asynchronous event. This different from the proposed solution to create custom events to avoid blocking, where each handler is invoked on its own background thread regardless.
 
Back
Top