C# GUI Cross Thread Exceptions

It has been a while since I’ve posted anything here and I’ve been collecting thoughts and ideas that need to be spewed out over a few posts.

Any of you who have spent time programming in a GUI environment have undoubtedly ran across the dreaded illegal cross thread operation when modifying a control from a secondary thread. You simply cannot modify the properties of a control unless you are executing in the context of the thread that created it. This goes for .Net based languages such as C# as well as others such as Java.

Subtle bugs can be introduced into your software because it’s easy to forget about this or just flat out not know your violating the rule. Of course, an exception isn’t generated till after you have deployed your application and your boss pays you a visit.

The fix on the other hand is incredibly easy and it’s surprising how many developers I see doing this the hard way. You can simply pass an anonymous delegate to the Invoke method of the control you wish to modify.

Here is a simple example. It has a form that contains a button named button1 and a label called label1. When you click the button the text of the label will change.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // this is the thread that will change the label text
            Thread myThread = new Thread(ThreadFunction);
 
            myThread.Start();
        }
 
        private void ThreadFunction()
        {
            this.label1.Invoke((MethodInvoker)delegate()
            {
                // when we get here we are now in the context of the primary user interface
                // thread, hence we can modify all of it's controls
                this.label1.Text = "Hello New Label";
            });
        }
    }
}

The above code works great but it still puts the responsibility on the programmer to understand ahead of time if he will be updating a control from a secondary thread. A more efficient pattern would place the code that modifies the control into its own method and then let the method determine if an Invoke is required.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // this is the thread that will change the label text
            Thread myThread = new Thread(ThreadFunction);
 
            myThread.Start();
        }
 
        private void ThreadFunction()
        {
            UpdateLabel("Hello New Label");
        }
 
        private void UpdateLabel(string text)
        {
            // this accomplishes the same goal as in the last example only this time
            // we have packaged the functionality into a method that can be called without 
            // regard to what thread context we are in
 
            // if we are not on the same thread that create the label we will call the method again
            // this time using Invoke()
            if (this.label1.InvokeRequired == true)
                this.label1.Invoke((MethodInvoker)delegate()
                {
                    UpdateLabel(text);
                });
            else
                this.label1.Text = text;
        }
    }
}

This new technique allows the programmer to use UpdateLabel from anywhere in the code without concern as to what thread it’s being called from. A simple example like this can be wrapped up even more compactly.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // this is the thread that will change the label text
            Thread myThread = new Thread((ThreadStart)delegate()
            {
                UpdateLabel("Hello New Label!");
            });
 
            myThread.Start();
        }
 
        private void UpdateLabel(string text)
        {
            // this accomplishes the same goal as in the last example only this time
            // we have packaged the functionality into a method that can be called without 
            // regard to what thread context we are in
 
            // if we are not on the same thread that create the label we will call the method again
            // this time using Invoke()
            if (this.label1.InvokeRequired == true)
                this.label1.Invoke((MethodInvoker)delegate()
                {
                    UpdateLabel(text);
                });
            else
                this.label1.Text = text;
        }
    }
}

Hopefully this will prove useful to some people who where unaware of the issues regarding cross thread GUI operations or to those who haven’t devised a re-usable pattern for dealing with the issue.

March 30, 2009   Posted in: Programming

7 Responses

  1. Crows Programming » Introduction to Multithreading in C# - April 8, 2009

    [...] Along those same lines here’s a tip: If you create secondary threads they cannot modify controls on your GUI. Only the thread that created a control can modify its properties. You will generate the dreaded illegal cross thread exception. I’ve already posted a brief work around for this problem [...]

  2. Beisdilswet - April 9, 2009

    FANTASTIC!

  3. AndrewBoldman - June 4, 2009

    Great post! Just wanted to let you know you have a new subscriber- me!

  4. Anglea Smitlen - June 4, 2009

    Keep working on these posts – they’re great! can’t wait to read the next one.

  5. Kelly Brown - June 12, 2009

    Great post! I’ll subscribe right now wth my feedreader software!

  6. Adnan Mehmood - June 26, 2010

    Great code. I have exactly the same problem during my project.So you can solve my problem.. T

    Thankx.

  7. Adnan Mehmood - June 26, 2010

    keep working on these type of posts. So that many people can take benefits from that kinds of post…

Leave a Reply