Cross-threading operations in .NET (C# / VB)

Hello everyone,

Recently I was asked in class how to solve the whole “Unable to access control created on another thread” exception.
The teachers apperantly didn’t know the solution so my classmates turned to me.

It’s pretty simple actually.
What we basicly have to do, is delegate the task.

Let’s say we have 2 threads.

The main thread being our application when it starts, and a worker thread that’s created when we press a button.
When the worker thread has been started, we want it to update a TextBox value and increment it by 1.

Now, if we do not delegate this task to the main thread when it’s being called from the worker thread, it will raise an exception saying it can’t access controls created by / on a different thread.

So the only thing we have to do, is create a delegate for this:

VB.NET:

'Delegate
Private Delegate Sub MyDelegate()

How to declare a delegate in VB.NET

C#.NET

// Delegate
public delegate void MyDelegate();

How to declare a delegate in C#.NET

That’s about it when it comes down to actually creating the delegates.
Now it’s time to use them.

So we will create a new thread when we click a button, and that thread will execute a method called “DoIncrement()”
This method will loop untill our counter is 10 and it will sleep for 1000 miliseconds (= 1 second) after it has incremented the value.
Once the counter has been incremented, a method called “ShowValue()” will be called to display the current value in a TextBox

VB.NET:

Private Sub DoIncrement()
	While (Me.m_nCounter < 10)
		'Increment the counter
		Me.m_nCounter += 1
		Me.ShowValue()

		'Let the worker thread sleep
		Thread.Sleep(1000)
	End While

	Me.m_nCounter = 0
	MessageBox.Show("Worker thread is done!")
End Sub

The DoIncrement() procedure in VB.NET

C#.NET

///

/// Increments the counter by 1
/// 

private void DoIncrement()
{
	while (m_nCounter < 10)
	{
		// Increment the counter
		this.m_nCounter++;
		this.ShowValue();

		// Pause the worker thread for 1 second
		Thread.Sleep(1000);
	}

	this.m_nCounter = 0;
	MessageBox.Show("Worker thread has finished!");
}

The DoIncrement() procedure in C#.NET

Now, in the “ShowValue()” method we will call upon the delegate we’ve declared.
What we’ll do is check if the TextBox we’re trying to show the value in belongs to another thread and if it does, delegate the task “ShowValue” to the thread that owns the control.

VB.NET:

Private Sub ShowValue()
	If (Me.txtValue.InvokeRequired) Then
		'Declare a new instance of our delegate
		Dim Show As MyDelegate

		'Setup the delegate
		Show = AddressOf ShowValue

		'Invoke the delegate for the control
		Me.txtValue.Invoke(Show)
	Else
		'Update the value of the textbox
		txtValue.Text = m_nCounter.ToString()
	End If
End Sub

The ShowValue() procedure in VB.NET

C#.NET:

///

/// Shows the current value of m_nCounter in txtValue
/// 

private void ShowValue()
{
	// Check to which thread the control belongs
	if (this.txtValue.InvokeRequired)
	{
		// Fire the delegate
		this.txtValue.Invoke(new MyDelegate(ShowValue));
	}
	else
	{
		// Show the current value
		this.txtValue.Text = this.m_nCounter.ToString();
	}
}

The ShowValue() procedure in C#.NET

Here is the complete source code for the VB.NET and C#.NET projects.
All you need is a small form with 1 button called btnStart and a TextBox called txtValue

VB.NET

Imports System.Threading

Public Class Form1

    'Delegate
    Private Delegate Sub MyDelegate()

    'Variables
    Private m_nCounter As Integer
    Private m_trdCounter As Thread

    '''

    ''' Occurs when the user clicks the start button
    ''' 

    '''
System.Object : The object calling this procedure
    '''
System.EventArgs : The event arguments
    ''' 
    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        'Setup the thread
        Me.m_trdCounter = New Thread(AddressOf Me.DoIncrement)
        Me.m_trdCounter.IsBackground = True

        'Start the new thread
        Me.m_trdCounter.Start()
    End Sub

    '''

    ''' Increment the value of m_nCounter by 1 and call ShowValue()
    ''' 

    ''' 
    Private Sub DoIncrement()
        While (Me.m_nCounter < 10)
            'Increment the counter
            Me.m_nCounter += 1
            Me.ShowValue()

            'Let the worker thread sleep
            Thread.Sleep(1000)
        End While

        Me.m_nCounter = 0
        MessageBox.Show("Worker thread is done!")
    End Sub

    '''

    ''' Show the current value of m_nCounter in the TextBox
    ''' 

    ''' 
    Private Sub ShowValue()
        If (Me.txtValue.InvokeRequired) Then
            'Declare a new instance of our delegate
            Dim Show As MyDelegate

            'Setup the delegate
            Show = AddressOf ShowValue

            'Invoke the delegate for the control
            Me.txtValue.Invoke(Show)
        Else
            'Update the value of the textbox
            txtValue.Text = m_nCounter.ToString()
        End If
    End Sub
End Class

The source code in VB.NET

C#.NET

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Crossthreading_Example
{
    // Delegate
    public delegate void MyDelegate();

    public partial class Form1 : Form
    {
        // Variables
        private int m_nCounter;
        private Thread m_trdCounter;

        ///

        /// Default constructor
        /// 

        public Form1()
        {
            InitializeComponent();

            // Initialize
            m_nCounter = 0;
        }

        ///

        /// Occurs when the user clicks the start button
        /// 

        ///
System.Object : The object calling this method
        ///
System.EventArgs : The event arguments
        private void btnStart_Click(object sender, EventArgs e)
        {
            // Setup a new thread
            this.m_trdCounter = new Thread(DoIncrement);
            this.m_trdCounter.IsBackground = true;

            // Start the thread
            this.m_trdCounter.Start();
        }

        ///

        /// Increments the counter by 1
        /// 

        private void DoIncrement()
        {
            while (m_nCounter < 10)
            {
                // Increment the counter
                this.m_nCounter++;
                this.ShowValue();

                // Pause the worker thread for 1 second
                Thread.Sleep(1000);
            }

            this.m_nCounter = 0;
            MessageBox.Show("Worker thread has finished!");
        }

        ///

        /// Shows the current value of m_nCounter in txtValue
        /// 

        private void ShowValue()
        {
            // Check to which thread the control belongs
            if (this.txtValue.InvokeRequired)
            {
                // Fire the delegate
                this.txtValue.Invoke(new MyDelegate(ShowValue));
            }
            else
            {
                // Show the current value
                this.txtValue.Text = this.m_nCounter.ToString();
            }
        }
    }
}

The source code in C#

There you go, access controls from other threads ;P

If you have any questions regarding this topic, feel free to ask and I’ll do my best to answer them.

Happy coding!

– Dirk

4 thoughts on “Cross-threading operations in .NET (C# / VB)”

  1. I don’t see the point in creating a new Delegete for those small tasks.

    You can just use the lambda (o, e) => { } ; syntax for that.

    Simplicity for the win.

  2. What about accessing data accross threads? I would like to access a dataset object from another thread. Meaning I click a button that runs some long running data query in the background and when it finally completes I would like to pass or grant access to this dataset on the main thread. Can you tell me how to do this?

    1. This could be accomplished by using the BackGroundWorker in .NET
      When a BGW has finished working it allows you to return the data to the main thread without much hassle. Without any more info on how you’re planning on doing it I can’t really help you much more 😉

  3. dear dirk, can you please let me know how I can do it in asp.net webform. my textbox is not showing updated values except very first.

Leave a Reply

Your email address will not be published.

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.