Threading is one of the most important thing in application development on Windows platform. Almost none of the serious applications can run in single thread. You will always need to perform some operations on background. The only problem with threads is, when they try to share some critical resources. One of the typical case are UI elements. Whenever you try to change some UI control from different thread than UI thread you will end up with the exception.

Changing UI from thread

Before we look at the events, lets have a look at possibilities for changing UI from thread. All UI controls has methods Invoke and/or BeginInvoke. These methods accepts delegates as parameters. Invoking means, that the delegate given as the parameter will be exceuted in thread that is owner of the UI control. This will not cause parallel access to the UI resource, so no exception will be thrown. The first option for invoking uses BeginInvoke method and WaitCallback delegate.

public partial class Form1 : Form
{
    Thread thr;
    
    public Form1()
    {
        InitializeComponent();
        thr = new Thread(new ThreadStart(ThreadWorker2));
        thr.Start();
    }

    private void ThreadWorker()
    {
        int i = 0;

        while(true)
        {
            string message = string.Format("Value of i={0}", i++);
            UpdateStatus(message);

            Thread.Sleep(1000);
        }
    }

    private void UpdateStatus(object status)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke(new WaitCallback(UpdateStatus), status);
            return;
        }

        label1.Text = (string)status;
    }
}

This solution is suitable for most situations where simple status reporting is required. Disadvantage of this solution is the only one argument of WaitCallback delegate. In case of multiple informations to be given, you will need to pass structure or array as the argument. Anyway, I’am still using this solution a lot.

Second option offers more scalability and more arguments to be passed, because of custom delegate instead of the WaitCallback. Following code is also part of the previous Form1 class.

public delegate void StatusEventHandler(DateTime date, string message);

private void ThreadWorker2()
{
    int i = 0;

    StatusEventHandler statusHandler = UpdateStatus2;

    while (true)
    {
        string message = string.Format("Value of i={0}",i++);
        this.Invoke(statusHandler, new object[] { DateTime.Now, message});
        Thread.Sleep(1000);
    }
}

private void UpdateStatus2(DateTime date, string message)
{
    label1.Text = date.ToShortTimeString();
    label1.Text += " " + message;
}

Firing thread safe events

Applications are usually splited into two or three layers, where UI and business logic are different layers. When event comes from business layer to UI layer we need to update some status or information. Problem is, if logic in business layer is running in thread. In that case, we need to catch the event and pass the status into thread safe Invokation described above. Such a design is really lousy, because UI logic must in fact rethrow the event to itself and layers are no more strictly separated. It is necessary to fire event in business layer thread safe way, so that UI layer can immidiately show the status. Since delegates and events in Compact Framework has no Target property as it’s in full .NET Framework, we need some workaround. Let’s have a look at following class. It is simple worker class representing business layer. Class has UIControl property which represent the UI control that will be Invoked. So FireStatusUpdate method Invokes the control and fires the event. This means, the event will be fired in thread which owns the UIControl.

using System;
using System.Windows.Forms;
using System.Threading;

namespace ThreadTest
{
    public class WorkerClass
    {
        private Thread thr;

        // UI control for update
        public Control UIControl { get; set; }

        public delegate void StatusUpdate(DateTime dateTime, string message);
        public event StatusUpdate OnStatusUpdate;

        // Starts thread
        public void Start()
        {
            thr = new Thread(new ThreadStart(MainWorker));
            thr.Start();
        }

        // Main thread worker
        public void MainWorker()
        {
            int i = 0;

            while (true)
            {
                string message = string.Format("Value of i={0}", i++);
                FireStatusUpdate(DateTime.Now, message);

                Thread.Sleep(1000);
            }
        }

        // Fire thread safe event
        private void FireStatusUpdate(DateTime dateTime, string message)
        {
            // UIControl is set and OnStatusUpdate has subscriber
            if (UIControl != null && OnStatusUpdate != null)
            {
                if (UIControl.InvokeRequired)
                {
                    UIControl.Invoke(new StatusUpdate(FireStatusUpdate), 
                                            new object[] { dateTime, message });
                    return;
                }

                OnStatusUpdate(dateTime, message);
            }
        }  
    }
}

Following code is simple UI layer for WorkingClass.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ThreadTest
{
    public partial class Form1 : Form
    {        
        public Form1()
        {
            InitializeComponent();
            
            WorkerClass worker = new WorkerClass();
            // add event handler
            worker.OnStatusUpdate += new WorkerClass.StatusUpdate(worker_OnStatusUpdate);
            // add UI control to invoke
            worker.UIControl = this;
            worker.Start();
        }

        void worker_OnStatusUpdate(DateTime dateTime, string message)
        {
            label1.Text = dateTime.ToLongTimeString();
            label1.Text += " " + message;
        }
}

Thread safe events in full framework

Following snippet shows the modification of the FireStatusUpdate method, to be used in full .NET Framework. There is no need for UIControl property, because delegates contains automaticaly filled property Target.

// Fire thread safe event
private void FireStatusUpdate(DateTime dateTime, string message)
{
    // if OnStatusUpdate has subscriber
    if (OnStatusUpdate != null)
    {
        // List all handlers
        foreach (StatusUpdate handler in OnStatusUpdate.GetInvocationList())
        {
            // if handler target is control then Invoke it
            if (handler.Target is Control)
                (handler.Target as Control).Invoke(handler, new object[] { dateTime, message });
            else
                handler(dateTime, message);
        }
    }
}