0

Possible Duplicate:
How to update the GUI from another thread in C#?

How can I update my UI from a threaded class? Unable to access update form Controls from threads. I'd like to update my UI on the progress of each specific thread.

class MyClass
    {
        public delegate void CountChangedEventHandler(int vCount, int vIndex);
        public event CountChangedEventHandler CountChanged;

        private int count;
        private int index;

        public MyClass(int index)
        {
            this.index = index;
        }

        public int Count
        {
            get { return this.count; }
            set
            {
                this.count = value;
                if (this.CountChanged != null)
                    this.CountChanged(value, this.index);
            }
        }

        public void DoWork(object o)
        {
            for (int i = 0; i < 10000; i++)
            {
                this.Count = i;
            }
        }
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 3; i++)
            {
                MyClass myCount = new MyClass(i);
                myCount.CountChanged += new MyClass.CountChangedEventHandler(UpdateUI);

                ListViewItem lvi = new ListViewItem();
                lvi.Text = "Thread #" + i.ToString();
                lvi.SubItems.Add("");
                listView1.Items.Add(lvi);

                ThreadPool.QueueUserWorkItem(new WaitCallback(myCount.DoWork));

            }
        }

        private void UpdateUI (int index, int count)
        {
            listView1.Items[index].SubItems[0].Text = count.ToString();
        }
    }
Community
  • 1
  • 1
Phil
  • 81
  • 7
  • Depends on whether or not you are using WPF or WinForms – JeremyK Jan 14 '13 at 16:31
  • 1
    You have to update from the UI thread, as you have noticed. See http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c – flup Jan 14 '13 at 16:32

2 Answers2

1

You need to use Invoke or BeginInvoke to marshal the UI access back to the UI thread.

I recommend using Invoke in this instance as this will stop your loop getting too far ahead of itself:

private void UpdateUI (int index, int count)
{
  Invoke( ( MethodInvoker ) (
    () => listView1.Items[index].SubItems[0].Text = count.ToString()
  ) );
}
Nick Butler
  • 24,045
  • 4
  • 49
  • 70
0

There are multiple ways to achieve this. Here are a couple of options:

1) BackgroundWorker - you can find plenty examples online of how to use this and will be acceptable so long as you only need to pass the progress (integer) back to the UI during the process.

2) Message Queue with a Timer - gives you the option to pass more than just progress back to the UI.

Here is an example of option 2 in a WPF application I wrote which passes a string back to the UI, of course, you could replace string with any custom object you want. Also, requires .NET 3.0 or above:

private static List<string> MessageQueue = new List<string>();

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    DispatcherTimer messageQueueTimer = new DispatcherTimer();
    messageQueueTimer.Tick += messageQueueTimer_Elapsed;
    messageQueueTimer.Interval = new TimeSpan(1); //1 tick
    messageQueueTimer.Start();
}

protected void messageQueueTimer_Elapsed(object sender, EventArgs e)
{
    lock(MessageQueue)
    {
        //Read message from queue and remove it.
    }
}

//WriteLine can be called from any asyncronous method spawed from the UI.
public void WriteLine(string message)
{
    lock(MessageQueue)
    {
        //Insert message into the queue.        
    }
}

There are several other ways to achieve this, but this should get you started. Hope this helps!

Aaron Hawkins
  • 2,611
  • 1
  • 20
  • 24