Monday, July 14, 2008

Cross-thread operation not valid: Control 'name' accessed from a thread other than the thread it was created on

If you ever got the exception bellow and don't know how to avoid it, then this post is for you.
System.InvalidOperationException: Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on.


Lets take a look at some code which actually throws such exception:

using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        int _iProgress = 0;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            progressBar1.Minimum = 0;
            progressBar1.Maximum = 100;
            progressBar1.Step = 1;
            progressBar1.Value = 0;

            Thread thread = new Thread(new ThreadStart(SomeThread));
            thread.Start();
        }

        private void SomeThread()
        {
            // Some process
            while (_iProgress < 100)
            {
                _iProgress++;
                progressBar1.PerformStep();
                Thread.Sleep(1000);
            }

            // [..]            
        }
    }
}

Execution of line progressBar1.PerformStep() will produce an exception.
The problem is that you cannot access the control from threads other than on which it was created on.

This can be solved by adding a delegate and further invoke it from thread.

Download sample source (Visual Studio 2008) - 11 KB
using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        int _iProgress = 0;
        private delegate void ProgressHandler();
        ProgressHandler updateHandler = null;

        public Form1()
        {
            InitializeComponent();
            updateHandler = new ProgressHandler(UpdateData);
        }

        void UpdateData()
        {
            progressBar1.PerformStep();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            progressBar1.Minimum = 0;
            progressBar1.Maximum = 100;
            progressBar1.Step = 1;
            progressBar1.Value = 0;

            Thread thread = new Thread(new ThreadStart(SomeThread));
            thread.Start();
        }

        private void SomeThread()
        {
            // Some process
            while (_iProgress < 100)
            {
                _iProgress++;

                // call the UpdateData method via the updateDataHandler so that we
                // update the UI on the UI thread
                Invoke(updateHandler);

                Thread.Sleep(100);
            }

            // [..]            
        }
    }
}

No comments: