Event Handlers That Do Thread-Safe Invokes
While this can be done using manual delegates and other methods, they don’t work very cleanly like an actual event handler does for simple development. The fix to this problem is to actually write a custom event handler. The custom event handler implements all of the back end handling that is normally hidden by VB. Here is the example code from the barcode scanner class:
Private _barcodeScannedList As List(Of EventHandler(Of BarcodeScannedEventArgs))
Public Custom Event BarcodeScanned As EventHandler(Of BarcodeScannedEventArgs)AddHandler(ByVal value As EventHandler(Of BarcodeScannedEventArgs))If _barcodeScannedList Is Nothing Then_barcodeScannedList = New List(Of EventHandler(Of BarcodeScannedEventArgs))End If
_barcodeScannedList.Add(value)End AddHandler
RemoveHandler(ByVal value As EventHandler(Of BarcodeScannedEventArgs))
If Not _barcodeScannedList Is Nothing Then_barcodeScannedList.Remove(value)End If
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As BarcodeScannedEventArgs)
If Not _barcodeScannedList Is Nothing ThenFor Each handler As EventHandler(Of BarcodeScannedEventArgs) In _barcodeScannedListDim safeInvoker As ISynchronizeInvoke = TryCast(handler.Target, ISynchronizeInvoke)
If safeInvoker Is Nothing Thenhandler.Invoke(sender, e)Else
safeInvoker.Invoke(handler, New Object() {sender, e})End If
Next
End If
End RaiseEvent
End Event
The _barcodeScannedList simply stores a list of all of the event handlers that have been registered with the class. AddHandler and RemoveHandler do the process of actually adding and removing those event handlers from the list. The real meat is in the RaiseEvent handler. Instead of simply iterating through the list and firing all of the event delegates directly like the standard handler does, this handler tests to see if the target class of the delegate implements the ISynchronizeInvoke interface. System.Windows.Forms.Control does implement this interface, meaning that all Forms implement it. If this interface is found, then the interface’s Invoke method is used instead of the delegate’s Invoke method. This will synchronize the call onto the delegate’s target’s thread.