Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Updating the UI from a thread - The simplest way

4.09/5 (8 votes)
23 Dec 2008CPOL4 min read 131.6K   4.2K  
A piece of reusable code that implements the functionality to update a UI element from a thread.

The screen print of the sample thread call back application

Introduction

Multi-threaded applications are heavily used these days. We create multiple threads for many different purposes. A thread may be working as a TCP/IP listener, another may be querying a database periodically, yet another may be trying to connect and download some stuff from the Internet. In a typical Windows environment, we can not let the UI thread deal with all these. Because, the UI thread is so expensive that it needs to attend user actions such as responding to mouse and keyboard events rather than spend time in doing the above mentioned tasks. If we let the UI thread to handle such tasks, there is also a great chance to let the user think that the application has got 'stuck'. To avoid this, many developers use timer controls or System.Timer objects to process something in the background. But, using multiple threads is the most preferred, or rather the professional way of handling such scenarios. Although it sounds good, there is a problem. We can not directly update any UI element from a different thread.

Problem

Let's take a very simple scenario. I want to run two threads which simply increment a variable from 0 to 9. Every time the variable is incremented, I want to update a rich text box in my form with the current value (as shown above). If we are to do this without another thread, it's simple.

The erroneous logic

The diagram above depicts the erroneous logic that fails to update the UI element from within another thread.

Solution

As briefed above in the introduction, there are several ways to handle this. The most easiest way would be to use a timer control or a System.Timer object which are special threads that can directly update the UI element. But, we can not always apply this technique for all scenarios. For example, I can not have a timer to 'listen' to a TCP/IP port, and should implement a thread to listen continuously. Now, how do I update the UI from this thread then?

It can be done in many different ways. There are several articles in this forum that explain many different techniques to update the UI from a thread. But, I've found an easier way to do this, using the Invoke method of the container Form or Control, a Delegate object, and a method that resembles the Delegate.

While browsing the definitions in the Object browser, the definition of the Invoke method struck my eyes, and helped me in achieving my task without much complexities.

VB
Public Function Invoke(ByVal method As System.Delegate) As Object

Member of: System.Windows.Forms.Control.

Summary: Executes the specified delegate on the thread that owns the control's underlying window handle.

Parameters: method - A delegate that contains a method to be called in the control's thread context.

Return values: The return value from the delegate being invoked, or null if the delegate has no return value.

The two delegates defined in the CallBackThread class:

VB
''' <summary>
''' Delegate for the call back function
''' </summary>
''' <param name="status">status message</param>
''' <remarks>Declare method with signature 
''' Sub [AnyMethodName](status as String)'</remarks>
Public Delegate Sub CallBackDelegate(ByVal status As String)

''' <summary>
''' The parameterless Thread function delegate
''' </summary>
''' <remarks></remarks>
Public Delegate Sub ThreadFunctionDelegate()

The code section that calls back the UI:

VB
''' <summary>
''' Sends the status to the Form/Control that implements the Call back method.
''' </summary>
''' <param name="msg">the message to send</param>
''' <remarks></remarks>
Public Sub UpdateUI(ByVal msg As String)
    If m_BaseControl IsNot Nothing AndAlso _
      m_CallBackFunction IsNot Nothing Then
        m_BaseControl.Invoke(m_CallBackFun/ction, New Object() {msg})
    End If
End Sub

The thread function and the way to call back the UI:

VB
Private Sub ThreadMethod1()
    'Thread 1 simply starts from 0 and counts up to 10
    For i As Integer = 1 To 10
        objThread1.UpdateUI("1st Thread Ticking " & i)
        Threading.Thread.Sleep(500)
    Next
End Sub

Using the Code

This project is a simple example which has a reusable class that can activate a thread and send the status as strings to the UI. It can be modified to cater any other scenario by merely changing the thread function and the callback function implementations alone. At the same time, any advanced functionality can be achieved by changing the Delegates and the UpdateUI method of the class.

  • Send a simple status text to the UI: No need to change the CallBackThread class. Change the thread function (e.g., Private Sub ThreadMethod1()) and the callback function (e.g., Private Sub CallBackMethod1(ByVal status As String)) to cater your needs.
  • Send multiple parameters back to the UI: Update the following things.
    1. Change the CallBackDelegate in the CallBackThread class (e.g., Public Delegate Sub CallBackDelegate(ByVal msg As String, progress as Integer))
    2. Change the UpdateUI function in the CallBackThread class
    3. VB
      'Eg.
      Public Sub UpdateUI(ByVal msg As String, progress as Integer)
          If m_BaseControl IsNot Nothing AndAlso _
            m_CallBackFunction IsNot Nothing Then
              m_BaseControl.Invoke(m_CallBackFunction, New Object() {msg, progress})
          End If
      End Sub
    4. Change the callback method implementation in the form/control to match the callback delegate change (e.g., Private Sub CallBackMethod1(ByVal msg As String, progrss as Integer))
  • Call a parameterized thread function: This needs changing the following areas of code.
    1. The constructor (Public Sub New(...)) of the CallBackThread class.
    2. The function invoker (Private Sub ThreadFunction(...)) in the CallBackThread class.
    3. The thread function implementation (Private Sub ThreadMethod1(...)) in the form/control.

    Please refer to the MSDN for how to call thread functions with a parameter.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)