Writing to a TextBox from another thread?

C#WinformsMultithreading

C# Problem Overview


I cannot figure out how to make a C# Windows Form application write to a textbox from a thread. For example in the Program.cs we have the standard main() that draws the form:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

Then we have in the Form1.cs:

public Form1()
{
    InitializeComponent();

    new Thread(SampleFunction).Start();
}

public static void SampleFunction()
{
    while(true)
        WindowsFormsApplication1.Form1.ActiveForm.Text += "hi. ";
}

Am I going about this completely wrong?

UPDATE

Here is the working code sample provided from bendewey:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        new Thread(SampleFunction).Start();
    }

    public void AppendTextBox(string value)
    {
        if (InvokeRequired)
        {
            this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
            return;
        }
        textBox1.Text += value;
    }

    void SampleFunction()
    {
        // Gets executed on a seperate thread and 
        // doesn't block the UI while sleeping
        for(int i = 0; i<5; i++)
        {
            AppendTextBox("hi.  ");
            Thread.Sleep(1000);
        }
    }
}

C# Solutions


Solution 1 - C#

On your MainForm make a function to set the textbox the checks the InvokeRequired

public void AppendTextBox(string value)
{
	if (InvokeRequired)
	{
		this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
		return;
	}
	ActiveForm.Text += value;
}

although in your static method you can't just call.

WindowsFormsApplication1.Form1.AppendTextBox("hi. ");

you have to have a static reference to the Form1 somewhere, but this isn't really recommended or necessary, can you just make your SampleFunction not static if so then you can just call

AppendTextBox("hi. ");

It will append on a differnt thread and get marshalled to the UI using the Invoke call if required.

Full Sample

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        new Thread(SampleFunction).Start();
    }

    public void AppendTextBox(string value)
    {
        if (InvokeRequired)
        {
            this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
            return;
        }
        textBox1.Text += value;
    }

    void SampleFunction()
    {
        // Gets executed on a seperate thread and 
        // doesn't block the UI while sleeping
        for(int i = 0; i<5; i++)
        {
            AppendTextBox("hi.  ");
            Thread.Sleep(1000);
        }
    }
}

Solution 2 - C#

I would use BeginInvoke instead of Invoke as often as possible, unless you are really required to wait until your control has been updated (which in your example is not the case). BeginInvoke posts the delegate on the WinForms message queue and lets the calling code proceed immediately (in your case the for-loop in the SampleFunction). Invoke not only posts the delegate, but also waits until it has been completed.

So in the method AppendTextBox from your example you would replace Invoke with BeginInvoke like that:

public void AppendTextBox(string value)
{
    if (InvokeRequired)
    {
        this.BeginInvoke(new Action<string>(AppendTextBox), new object[] {value});
        return;
    }
    textBox1.Text += value;
}

Well and if you want to get even more fancy, there is also the SynchronizationContext class, which lets you basically do the same as Control.Invoke/Control.BeginInvoke, but with the advantage of not needing a WinForms control reference to be known. Here is a small tutorial on SynchronizationContext.

Solution 3 - C#

or you can do like

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        new Thread( SampleFunction ).Start();
    }

    void SampleFunction()
    {
        // Gets executed on a seperate thread and 
        // doesn't block the UI while sleeping
        for ( int i = 0; i < 5; i++ )
        {
            this.Invoke( ( MethodInvoker )delegate()
            {
                textBox1.Text += "hi";
            } );
            Thread.Sleep( 1000 );
        }
    }
}

Solution 4 - C#

Quite simply, without worrying about delegates:

if(textBox1.InvokeRequired == true)
    textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";});

else
    textBox1.Text = "Invoke was NOT needed"; 

Solution 5 - C#

You need to perform the action from the thread that owns the control.

That's how I'm doing that without adding too much code noise:

control.Invoke(() => textBox1.Text += "hi");

Where Invoke overload is a simple extension from Lokad Shared Libraries:

/// <summary>
/// Invokes the specified <paramref name="action"/> on the thread that owns     
/// the <paramref name="control"/>.</summary>
/// <typeparam name="TControl">type of the control to work with</typeparam>
/// <param name="control">The control to execute action against.</param>
/// <param name="action">The action to on the thread of the control.</param>
public static void Invoke<TControl>(this TControl control, Action action) 
  where TControl : Control
{
  if (!control.InvokeRequired)
  {
    action();
  }
  else
  {
    control.Invoke(action);
  }
}

Solution 6 - C#

Have a look at Control.BeginInvoke method. The point is to never update UI controls from another thread. BeginInvoke will dispatch the call to the UI thread of the control (in your case, the Form).

To grab the form, remove the static modifier from the sample function and use this.BeginInvoke() as shown in the examples from MSDN.

Solution 7 - C#

What's even easier is to just use the BackgroundWorker control...

Solution 8 - C#

Here is the what I have done to avoid CrossThreadException and writing to the textbox from another thread.

Here is my Button.Click function- I want to generate a random number of threads and then get their IDs by calling the getID() method and the TextBox value while being in that worker thread.

private void btnAppend_Click(object sender, EventArgs e) 
{
    Random n = new Random();

    for (int i = 0; i < n.Next(1,5); i++)
    {
        label2.Text = "UI Id" + ((Thread.CurrentThread.ManagedThreadId).ToString());
        Thread t = new Thread(getId);
        t.Start();
    }
}

Here is getId (workerThread) code:

public void getId()
{
    int id = Thread.CurrentThread.ManagedThreadId;
    //Note that, I have collected threadId just before calling this.Invoke
    //method else it would be same as of UI thread inside the below code block 
    this.Invoke((MethodInvoker)delegate ()
    {
        inpTxt.Text += "My id is" +"--"+id+Environment.NewLine; 
    });
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavidView Question on Stackoverflow
Solution 1 - C#bendeweyView Answer on Stackoverflow
Solution 2 - C#MikeView Answer on Stackoverflow
Solution 3 - C#ChrisView Answer on Stackoverflow
Solution 4 - C#marsh-wiggleView Answer on Stackoverflow
Solution 5 - C#Rinat AbdullinView Answer on Stackoverflow
Solution 6 - C#Dan C.View Answer on Stackoverflow
Solution 7 - C#Chris KLView Answer on Stackoverflow
Solution 8 - C#Iqra.View Answer on Stackoverflow