Invoke UI Changes Across Threads on VB .Net

I need to do this all the time and don’t have the best memory in the world. Today, I decided that I looked this up one too many times so here’s my solution to this multithreading problem:

The Problem:

You try to modify UI components created in one thread in another thread. In VB, you can only make UI changes on the same thread that created it so you get a nice “Cross thread operation not valid” exception.

Here’s the wrong code:

thread = New System.Threading.Thread(AddressOf DoStuff)
thread.Start()
Private Sub DoStuff()
    'error occurs here'
    Me.Text = "Stuff"
End Sub

The Solution:

If you’re super lazy or you’re very sure of what your code is going to do, you can simply disable the exception with:

Private Sub DoStuff()
    Me.CheckForIllegalCrossThreadCalls = False
    Me.Text = "Stuff"
End Sub

This, of course, doesn’t prevent your code from making a mess. To do it properly, you need a delegate:

thread = New System.Threading.Thread(AddressOf DoStuff)
thread.Start()
Private Delegate Sub DoStuffDelegate()
Private Sub DoStuff()
    If Me.InvokeRequired Then
        Me.Invoke(New DoStuffDelegate(AddressOf DoStuff))
    Else
        Me.Text = "Stuff"
    End If
End Sub

Wildly simple right?

To understand what the code does, you need to understand what Invoke() does. It can be confusing to google Invoke() because Delegates and Controls have different implementations of Invoke(). In this case, we’re calling Invoke on a subclass of Control. That causes the method on the second thread which is going through DoStuff() to run a method on the first thread (which created the UI element).

Now if you need parameters, here’s another example where “ReallyLongProcess” is a subroutine that raises an event “Done” with a parameter “success”

AddHandler Me.Done, AddressOf WorkFinished
thread = new System.Threading.Thread(AddressOf ReallyLongProcess)
thread.Start()
Private Delegate Sub DoStuffDelegate(ByRef success as Boolean)
Private Sub DoStuff(ByRef success as Boolean)
    If Me.InvokeRequired Then
        Me.Invoke(New DoStuffDelegate(AddressOf DoStuff), success)
    Else
        If success Then
            'do stuff
        Else
            'do stuff
        End If
    End If
End Sub

Vb.net 2015 working sample:
Add button,add progressbar

Imports System.Threading
Public Class Form1
    Private trd As Thread


    Private Sub ThreadTask()
        Dim stp As Integer
        Dim newval As Integer
        Dim rnd As New Random()
        Me.CheckForIllegalCrossThreadCalls = False
        Do
            stp = ProgressBar1.Step * rnd.Next(-1, 2)
            newval = ProgressBar1.Value + stp
            If newval > ProgressBar1.Maximum Then
                newval = ProgressBar1.Maximum
            ElseIf newval < ProgressBar1.Minimum Then
                newval = ProgressBar1.Minimum
            End If

            ProgressBar1.Value = newval

            Thread.Sleep(100)
        Loop
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
     

        trd = New Thread(AddressOf ThreadTask)
        trd.IsBackground = True
        trd.Start()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MessageBox.Show("This is the main thread")
    End Sub
End Class

How to format bytes to human readable file size in C# and VB.NET

Sample C#

public static String FormatToHumanReadableFileSize(object value)
{
    try
    {
        string[] suffixNames = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
        var counter = 0;
        decimal dValue = 0;
        Decimal.TryParse(value.ToString(), out dValue);
        while (Math.Round(dValue / 1024) >= 1)
        {
            dValue /= 1024;
            counter++;
        }
 
        return string.Format("{0:n1} {1}", dValue, suffixNames[counter]);
    }
    catch(Exception ex)
    {
        //catch and handle the exception
        return string.Empty;
    }
}

Sample Vb.Net

Public Shared Function FormatToHumanReadableFileSize(value As Object) As [String]
        Try
            Dim suffixNames As String() = {"bytes", "KB", "MB", "GB", "TB", "PB", _
                "EB", "ZB", "YB"}
            Dim counter = 0
            Dim dValue As Decimal = 0
            Decimal.TryParse(value.ToString(), dValue)
            While Math.Round(dValue / 1024) >= 1
                dValue /= 1024
                counter += 1
            End While
 
            Return String.Format("{0:n1} {1}", dValue, suffixNames(counter))
        Catch ex As Exception
            'catch and handle the exception
            Return String.Empty
        End Try
    End Function