7

How you update textboxes and labels in the main thread from a new thread running a different class.

MainForm.cs (Main thread)

public partial class MainForm : Form
{
    public MainForm()
    {
        Test t = new Test();

        Thread testThread = new Thread(new ThreadStart(t.HelloWorld));
        testThread.IsBackground = true;
        testThread.Start();
    }

    private void UpdateTextBox(string text)
    {
        textBox1.AppendText(text + "\r\n");
    }

}

public class Test
{
    public void HelloWorld()
    {
        MainForm.UpdateTextBox("Hello World"); 
        // How do I execute this on the main thread ???
    }
}

I have looked at the examples on here but cant seem to get it right. Please could someone give some good links.

I have started again fresh so I don't mess up my code. If anyone would like to put up a working example with my example that would be great.

Also if I had to update multiple objects like textboxes and labels etc (not all at the same time) what would be the best way to go about it, having a method for each textbox or is there a way to do this with one method?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
tiptopjones
  • 71
  • 1
  • 1
  • 2
  • welcome to SO. You can edit your question to add additional information, and you can leave comments on answers to a question you have asked (and on anything after you hit 50 rep). –  Dec 16 '10 at 14:31

3 Answers3

8

Invoke or BeginInvoke, e.g.

Invoke((MethodInvoker)delegate {
    MainForm.UpdateTextBox("Hello World"); 
});

@tiptopjones I guess you're asking also how to get a reference to the form. You could make your HelloWorld method take an object parameter, use the ParameterizedThreadStart delegate, and then pass a reference to the form as a parameter to the Thread.Start method. But I would suggest reading about anonymous methods which makes it a lot easier and keeps everything strongly typed.

public class MainForm : Form {
    public MainForm() {
        Test t = new Test();

        Thread testThread = new Thread((ThreadStart)delegate { t.HelloWorld(this); });
        testThread.IsBackground = true;
        testThread.Start();
    }

    public void UpdateTextBox(string text) {
        Invoke((MethodInvoker)delegate {
            textBox1.AppendText(text + "\r\n");
        });
    }
}

public class Test {
    public void HelloWorld(MainForm form) {
        form.UpdateTextBox("Hello World"); 
    }
}

When you get comfortable with that you could read up on lambda expressions and do it like:

Thread testThread = new Thread(() => t.HelloWorld(this));
J.D.
  • 2,114
  • 17
  • 13
  • Hey, its preferable to edit your question to add more information rather than provide another answer. –  Dec 16 '10 at 14:33
5

You can call the BeginInvoke method, which will queue a delegate to be executed asynchronously on the UI thread.

If you need the background thread to wait until the function finishes on the UI thread, you can call Invoke instead.

Note that you will need a reference to the instance of your form; you should probably pass that to the Test constructor and store it in a private field.


The BackgroundWorker component will do all of this automatically using the ReportProgress method; you should consider using it.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Thanks for the answer but im new to c# and would need an example to work off so I can understand how it works. – tiptopjones Dec 15 '10 at 02:22
  • 1
    @tiptop: Then you should probably use a BackgroundWorker. [MSDN has examples](http://msdn.microsoft.com/en-us/library/waw3xexc.aspx). – SLaks Dec 15 '10 at 02:50
  • Any WinForms developer should understand how to update the UI in a threadsafe manner, and both approaches are valid options to understand – STW Dec 15 '10 at 03:27
  • ...also worth warning that the `SynchronizationContext.BeginInvoke()` used to switch to the UI thread is different from `Delegate.BeginInvoke()`, the same for `Invoke() on each Type`. If you're doing multithreaded WinForms then that ambiguity can mess with you – STW Dec 15 '10 at 03:30
1

The prefered way in WinForms is to use the SynchronizationContext

public partial class MainForm : Form
{
    SynchronizationContext ctx;

    public MainForm()
    {
        ctx = SynchronizationContext.Current;

        Test t = new Test();

        Thread testThread = new Thread(new ThreadStart(t.HelloWorld));
        testThread.IsBackground = true;
        testThread.Start();
    }

    private void UpdateTextBox(string text)
    {
        ctx.Send(delegate(object state)
        {
            textBox1.AppendText(text + "\r\n");

        },null);
    }    
}

public class Test
{
    public void HelloWorld()
    {
        MainForm.UpdateTextBox("Hello World"); 
        // How do I excute this on the main thread ???
    }
}
Arvin Baccay
  • 162
  • 1
  • 5