Writing to a TextBox from another thread? [duplicate]
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);
}
}
}
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
.
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 );
}
}
}
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";
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);
}
}