Tuesday, September 24, 2013

VB.Net Cross Thread Events in WinForms

Today I quickly did a proof of concept on integrating a third part component into a winforms application and had to figure out how to raise events on the Windows Forms UI thread from a different one. There are plenty of examples of doing this in C# but they didn't quite translate to VB.Net.

Although a BackgroundWorker may have worked I had some more complex requirements. Therefore I slightly tweaked a C# cross thread example on stackoverflow and combined it with another example of building a custom event. Here is what I came out with. I have to investigate better ways of doing this but since there is a lack of any VB.Net examples I thought I would post one.

Imports System.ComponentModel
Imports System.Threading

Public Class CrossThreadingExample

    Private dummyThread As Thread

    Private ReadOnly crossThreadEventList As New List(Of CrossThreadEventHandler)

    Public Delegate Sub CrossThreadEventHandler(sender As Object, e As CrossThreadEventArgs)

    Public Custom Event CrossThreadNotification As CrossThreadEventHandler

        AddHandler(handler As CrossThreadEventHandler)
            Me.crossThreadEventList.Add(handler)
        End AddHandler

        RemoveHandler(handler As CrossThreadEventHandler)
            Me.crossThreadEventList.Remove(handler)
        End RemoveHandler

        RaiseEvent(sender As Object, e As CrossThreadEventArgs)
            For Each del In Me.crossThreadEventList
                Dim synchronizedInvoke As ISynchronizeInvoke = DirectCast(del.Target, ISynchronizeInvoke)

                If synchronizedInvoke IsNot Nothing AndAlso synchronizedInvoke.InvokeRequired Then
                    synchronizedInvoke.Invoke(del, New Object() {sender, e})
                Else
                    del.DynamicInvoke(New Object() {sender, e})
                End If

            Next
        End RaiseEvent

    End Event

    Public Sub Start()
        Me.dummyThread = New Thread(AddressOf DoWork)
        Me.dummyThread.IsBackground = True
        Me.dummyThread.Start()
    End Sub

    Public Sub Cancel()
        Me.dummyThread.Abort()
    End Sub

    Private Sub DoWork()
        Dim i As Integer = 0
        While i < Integer.MaxValue
            i += 1
            If i Mod 10 = 0 Then
                RaiseEvent CrossThreadNotification(Me, New CrossThreadEventArgs(i))
            End If

            Thread.Sleep(100)
        End While
    End Sub

End Class

Public Class CrossThreadEventArgs
    Inherits EventArgs

    Public Property NumberDivisibleBy10 As Integer

    Public Sub New(number As Integer)
        MyBase.New()
        Me.NumberDivisibleBy10 = number
    End Sub

End Class

And here is an example of using it on a Windows Form where a label gets updated with values divisible by 10.

Public Class ExampleForm

    Private WithEvents crossThreadingExample As CrossThreadingExample

    Public Sub New()
        Me.InitializeComponent()
        Me.crossThreadingExample = New CrossThreadingExample()
        Me.crossThreadingExample.Start()
    End Sub

    Public Sub ChangeLabel(sender As Object, e As CrossThreadEventArgs) Handles crossThreadingExample.CrossThreadNotification
        Me.LabelCounter.Text = e.NumberDivisibleBy10.ToString()
    End Sub

    Protected Overrides Sub OnClosing(e As System.ComponentModel.CancelEventArgs)
        MyBase.OnClosing(e)
        Me.crossThreadingExample.Cancel()
    End Sub

End Class