Quote Originally Posted by jmcilhinney View Post
I actual fact, the code you posted won't work with Option Strict On, which it should always be, because the SendOrPostCallback has a single Object parameter, just like most methods related to multi-threading do. That's why it's preferable to use a Lambda. My example could look like this without the Lambda:
vb.net Code:
  1. Public Class SomeClass
  2.  
  3.     'This will get the context for the current thread so this instance must be created on the UI thread.
  4.     Private context As SynchronizationContext = SynchronizationContext.Current
  5.  
  6.     Public Event SomeEvent As EventHandler
  7.  
  8.     Private Sub RaiseSomeEvent()
  9.         If context Is Nothing Then
  10.             'This is not a GUI app so raise the event on the current thread.
  11.             RaiseSomeEvent(EventArgs.Empty)
  12.         Else
  13.             'This is a GUI app so raise the event on the UI thread.
  14.             context.Send(AddressOf RaiseSomeEvent, EventArgs.Empty)
  15.         End If
  16.     End Sub
  17.  
  18.     Private Sub RaiseSomeEvent(e As Object)
  19.         OnSomeEvent(DirectCast(e, EventArgs))
  20.     End Sub
  21.  
  22.     Protected Overridable Sub OnSomeEvent(e As EventArgs)
  23.         RaiseEvent SomeEvent(Me, e)
  24.     End Sub
  25.  
  26. End Class
All you need is the one extra method with an Object parameter that casts it as the appropriate type and then calls the final method. Three extra lines of code.
Oh, I almost forgot to post this up...wrote it and fell asleep after:-
vbnet Code:
  1. Option Strict On
  2. Imports System.Threading
  3. Imports System.Runtime.CompilerServices
  4.  
  5. 'Imagine this class to be a class used
  6. 'to represent each device retrieved from the database
  7. Public Class Device
  8.     Private _DeviceID As Integer
  9.     Private _DeviceName As String
  10.     Private _SomeDeviceProperty As Integer
  11.  
  12.     Private g_bPingingAsync As Boolean = False
  13.  
  14.     Private context As SynchronizationContext = SynchronizationContext.Current
  15.  
  16.     Public Event PingProgressChanged As EventHandler(Of PingProgressChangedEventArgs)
  17.     Protected Overridable Sub OnPingProgressChanged(ByVal e As PingProgressChangedEventArgs)
  18.         RaiseEvent PingProgressChanged(Me, e)
  19.     End Sub
  20.  
  21.     Public Event PingProgressCompleted As EventHandler
  22.     Protected Overridable Sub OnPingProgressCompleted()
  23.         RaiseEvent PingProgressCompleted(Me, New EventArgs)
  24.     End Sub
  25.  
  26.     Sub New()
  27.  
  28.     End Sub
  29.  
  30.     Sub New(ByVal devName As String, ByVal devID As Integer, ByVal someProp As Integer)
  31.         MyClass.New()
  32.  
  33.         Me.DeviceID = devID
  34.         Me.DeviceName = devName
  35.         Me.SomeDeviceProperty = someProp
  36.  
  37.     End Sub
  38.  
  39.     Public Property DeviceID() As Integer
  40.         Get
  41.             Return _DeviceID
  42.         End Get
  43.         Set(ByVal value As Integer)
  44.             _DeviceID = value
  45.         End Set
  46.     End Property
  47.     Public Property DeviceName() As String
  48.         Get
  49.             Return _DeviceName
  50.         End Get
  51.         Set(ByVal value As String)
  52.             _DeviceName = value
  53.         End Set
  54.     End Property
  55.     Public Property SomeDeviceProperty() As Integer
  56.         Get
  57.             Return _SomeDeviceProperty
  58.         End Get
  59.         Set(ByVal value As Integer)
  60.             _SomeDeviceProperty = value
  61.         End Set
  62.     End Property
  63.  
  64.     Public Sub PingDevice()
  65.         InternalPingDevice()
  66.     End Sub
  67.  
  68.     Public Sub PingDeviceAsync()
  69.         If g_bPingingAsync Then
  70.             Throw New Exception("Alreadying pinging asyncrounously")
  71.         End If
  72.  
  73.         g_bPingingAsync = True
  74.         ThreadPool.QueueUserWorkItem(AddressOf InternalPingDevice, Nothing)
  75.     End Sub
  76.  
  77.     Private Sub InternalPingDevice()
  78.         InternalPingDevice(Nothing)
  79.     End Sub
  80.  
  81.     Private Sub InternalPingDevice(ByVal state As Object)
  82.  
  83.         Dim i As Integer = 0
  84.         Dim e As PingProgressChangedEventArgs
  85.  
  86.         'Imagine this For...Next loop is your pinging process
  87.         '----------------------------------------------------
  88.         For i = 0 To Me.SomeDeviceProperty
  89.  
  90.             Thread.Sleep(200)
  91.  
  92.             e = New PingProgressChangedEventArgs(CInt((i / Me.SomeDeviceProperty) * 100))
  93.  
  94.             If context Is Nothing Then
  95.                 OnPingProgressChanged(e)
  96.             Else
  97.                 context.Invoke(New Action(Of PingProgressChangedEventArgs)(AddressOf OnPingProgressChanged), e)
  98.                 'context.Send(New SendOrPostCallback(AddressOf OnPingProgressChanged), e)
  99.             End If
  100.  
  101.         Next
  102.  
  103.         g_bPingingAsync = False
  104.  
  105.         If context Is Nothing Then
  106.             OnPingProgressCompleted()
  107.         Else
  108.             'context.Send(New SendOrPostCallback(AddressOf OnPingProgressCompleted), Nothing)
  109.             context.Invoke(New Action(AddressOf OnPingProgressCompleted))
  110.         End If
  111.  
  112.     End Sub
  113.  
  114.  
  115.  
  116. End Class
  117.  
  118. Public Class PingProgressChangedEventArgs
  119.     Inherits EventArgs
  120.  
  121.     Private _progress As Integer
  122.  
  123.     Sub New(ByVal progressPercent As Integer)
  124.         Me.Progress = progressPercent
  125.     End Sub
  126.  
  127.     Public Property Progress() As Integer
  128.         Get
  129.             Return _progress
  130.         End Get
  131.         Set(ByVal value As Integer)
  132.             _progress = value
  133.         End Set
  134.     End Property
  135.  
  136. End Class
  137.  
  138. Public Module Extensions
  139.  
  140.     <Extension()> _
  141.     Public Sub Invoke(ByVal sc As SynchronizationContext, ByVal del As [Delegate], ByVal ParamArray args() As Object)
  142.         sc.Send(New SendOrPostCallback(AddressOf SendOrPost), New SendOrPostState With {.Args = args, .DelegateToInvoke = del})
  143.     End Sub
  144.  
  145.     Private Sub SendOrPost(ByVal state As Object)
  146.         Dim st As SendOrPostState = DirectCast(state, SendOrPostState)
  147.  
  148.         st.DelegateToInvoke.DynamicInvoke(st.Args)
  149.     End Sub
  150.  
  151.  
  152.     Private Class SendOrPostState
  153.         Public Args() As Object
  154.         Public DelegateToInvoke As [Delegate]
  155.  
  156.     End Class
  157.  
  158. End Module

I'm using VS 2008 so I can't use Sub to create a lambda expression and I don't really like the idea of having to declare an extra OnEvent type procedure just to support Send so I created an Invoke extension method for the SynchronizationContext class which works like the Invoke method on the Control class. This way works with Option Strict.