1

I have a method that has a for loop in it. In the for loop I want to update some label's text on the mainform, but the changes are only done after the loop ends.

I tried to do it on another thread like this:

Thread firstthread = new Thread(new ThreadStart(myMethod));
firstthread.Start();

When I did that I got an InvalidOperationException because of trying to access controls on another thread or something like that.

How should I update the labels(or other controls) on the mainform from a loop while the loop is in progress?

Rizier123
  • 58,877
  • 16
  • 101
  • 156
davidgereb
  • 119
  • 5
  • 13
  • This is an olsd favourite, here is one reference... http://osherove.com/blog/2006/3/1/the-3-ways-to-create-a-thread-safe-gui-with-net-20-with-one.html – Justin Harvey Oct 08 '12 at 13:41

3 Answers3

4

You should use a BackgroundWorker. Place your long running loop inside of the DoWork event handler; it will run in a background thread and not block the UI thread. You can set ReportProgress to true and then attach an event handler to that to allow you to update a label (or whatever else) periodically. The ProgressReported event runs in the UI thread. You can also add a handler to the Completed event which runs in the UI thread as well.

You can look at the MSDN page for BackgroundWorker for details and code samples.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Soon I'm going to deal with that solution, but it's not that simple. – davidgereb Oct 08 '12 at 19:42
  • Well, usually it really is that simple. If it's not, then you'll need to provide more details about what complicates your particular case. – Servy Oct 08 '12 at 19:49
1

You should check the Invoke and BeginInvoke methods on the Form (for Windows.Forms) or on the Dispatcher object of the window (for WPF).

For example:

this.BeginInvoke(new Action(() => this.Text = "ciao"));

changes the title bar of the form.

BeginInvoke is asynchronous - it doesn't wait for the change to happen - while Invoke is synchronous and blocks until the change is done. Unless you have specifically that need, I would suggest using BeginInvoke which reduces the chances of an accidental deadlock.

This will allow you to update UI from a concurrent thread - and works whatever threading mechanism you are using (TPL tasks, plain Thread, etc.).

Marco Mp
  • 422
  • 2
  • 6
1

As Servy said, you can use something like in this simple example:

public partial class Form1 : Form
{
    BackgroundWorker bgw;
    public Form1()
    {
        InitializeComponent();
        bgw = new BackgroundWorker();
        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
        bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
        bgw.WorkerReportsProgress = true;
    }

    void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        string text = (string)e.UserState;
        SetValue(text);//or do whatever you want with the received data
    }
    void SetValue(string text)
    {
        this.label1.Text = text;
    }

    void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 10000; i++)
        {
            string text = "Value is " + i.ToString();
            bgw.ReportProgress(1, text);
            Thread.Sleep(1000);
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bgw.RunWorkerAsync();
    }

}

Nikola Davidovic
  • 8,556
  • 1
  • 27
  • 33
  • I'm calling ReportProgress method many times in a for loop but it just do it once – davidgereb Oct 08 '12 at 19:41
  • I didn't tried your code, just something like that I found [link](http://www.dotnetperls.com/backgroundworker) before you answered and now it works but I think the problem is the way I want to update, but that's tomorrow problem, I think I'll sleep on it. – davidgereb Oct 08 '12 at 20:09