How to fix my Background solution to make thread-safe calls? - c#

I tried to set Text property of TextBox from another thread. I got this exception below;
"Cross-thread operation not valid: Control 'recTpcTxt' accessed from a thread other than the thread it was created on."
Then, I used BackgroundWorker to solve this issue. However, I faced with the same exception message.
EDIT[1]:
Actually, I take a guide myself this link ; https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx. I can solve my problem by using invokeproperty. However, I cannot solve my problem with backgroundworker.
Is there something wrong in my solution? How do I fix my solution to set some property of UI variable?
EDIT[2]: More code to clarify the issue;
MqttManager.cs;
public partial class MqttManager : Form
{
MqttHandler mqttHandler = new MqttHandler();
public static MqttManager managerInst;
public MqttManager()
{
InitializeComponent();
managerInst = this;
...
}
...
private BackgroundWorker backgroundWorker;
public void NotifyUIForRecMsg(string topic, string message)
{
object[] objArr = { topic, message };
this.backgroundWorker.RunWorkerAsync(objArr);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = e.Argument;
}
private void backgroundWorker_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
object[] res = (object[])e.Result;
this.recTpcTxt.Text = (String)res[0];
}
}
MqttManager.Design.cs;
partial class MqttManager
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
...
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
}
#endregion
...
}
MqttHandler.cs;
class MqttHandler
{
MqttClient client;
...
/// <summary>
/// Publish received event handler.
/// </summary>
private void client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e)
{
MqttManager.managerInst.NotifyUIForRecMsg(e.Topic, Encoding.UTF8.GetString(e.Message));
}
}

check this:
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
Basically, to set a control propertiy you have to be in the same UI thread.
This simple solution move the call to textbox1.Text = someText in the UI thread
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
also, you can use textBox1.BeginInvoke instead of Invoke: it will run in UI thread, without locking the caller thread waiting for SetText delegate to be completed
[Edit] to do it in your backgroundWorker:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] arg = (object[])e.Argument;
SetTextToTextBox(recTpcTxt, (string)arg[0]);
SetTextToTextBox(recMsgTxt, (string)arg[1]);
}
private void SetTextToTextBox(TextBox toSet, string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (toSet.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
toSet.Invoke(d, new object[] { text });
}
else
{
toSet.Text = text;
}
}
[Edit 2]
To properly use backgroundworker
Register for events DoWork and RunWorkerCompleted
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
Before exiting backgroundWorker1_DoWork, set result property of eventArgs, and read them in backgroundWorker1_RunWorkerCompleted
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = new string[] { "one", "two" };
}
private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
string[] res = (string[])e.Result;
this.textBox1.Text = res[0];
}

Related

Update TextBox from second class with asynchronous method

I have read through a few threads here, but have not come to any conclusion.
An asynchronous method in another class is to be executed by button click.
In this method the current state should be added again and again in a text field of the form:
public interface MainForm {
string LogText { get; set; }
}
public partial class KatalogForm : Form, MainForm {
public string LogText {
get { return rtbxLog.Text; }
set { rtbxLog.Text += value; }
}
private void btnCreateCatalogues_Click(object sender, EventArgs e) {
Task.Run(() => catalogues.Create());
}
}
Excerpt from the second class:
private static MainForm mainForm;
public async void Create() {
//Stuff
//Update TextBox
}
Since the method is asynchronous, I can't access the textbox directly here.
I know that I have to work with Invoke here, but I can't implement this properly.
What is the best way to solve this?
Usually, when I have to asynchronously update a TextBox with data from external sources (ex: an operations log) I use a queue and a timer (Windows.Forms).
The method called by external objects adds the new data.
The timer clears pending data at a reasonable rate and updates the text box.
The advantages are:
Fast requests: The update method invoked by external sources only add items to the queue.
Thread safe: The text box is only updated within its own UI thread, so no cross-thread errors can occur.
Less allocations: No Task or BeginInvoke calls are required, avoiding allocation of temporary objects.
Few UI updates: The items are applied to the text box as batches (multiple items at the same time) based on the timer frequency.
First-in/First-out: No risk of overlap of items due to asynchronous operations scheduled and executed in the wrong order.
See the sample class (LogBox user control) below, split into LogBox.cs and LogBox.Designer.cs:
Other objects would call Log method.
The default rate is 100ms; that is, 10 times a second (for a human reader should be enough).
Sample code (LogBox.cs):
using System;
using System.Collections.Concurrent;
using System.Windows.Forms;
namespace SAMPLE
{
[Docking(DockingBehavior.Ask)]
public partial class LogBox : UserControl
{
private readonly ConcurrentQueue<string> PendingLog = new ConcurrentQueue<string>();
public LogBox()
{
InitializeComponent();
}
private void tmrLog_Tick(object sender, EventArgs e) { this.ProcessPendingLog(); }
private void ProcessPendingLog()
{
if (!this.Disposing && !this.IsDisposed && this.IsHandleCreated)
try
{
if (!this.PendingLog.IsEmpty)
{
string item;
while (this.PendingLog.TryDequeue(out item))
{
txtLog.AppendText(item);
}
}
}
catch (Exception ex) { /* ... */ }
}
public void Log(string text) { this.PendingLog.Enqueue(text); }
}
}
Sample code (LogBox.Designer.cs):
namespace SAMPLE
{
partial class LogBox
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tmrLog = new System.Windows.Forms.Timer(this.components);
this.txtLog = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// tmrLog
//
this.tmrLog.Enabled = true;
this.tmrLog.Tick += new System.EventHandler(this.tmrLog_Tick);
//
// txtLog
//
this.txtLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtLog.Location = new System.Drawing.Point(0, 0);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.txtLog.Size = new System.Drawing.Size(200, 200);
this.txtLog.TabIndex = 0;
//
// LogBox
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.txtLog);
this.Name = "LogBox";
this.Size = new System.Drawing.Size(200, 200);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Timer tmrLog;
private System.Windows.Forms.TextBox txtLog;
}
}
I would recommend use IProgress<string> for catalogues.Create()
Task.Run(async () =>
{
var createProgress = new Progress<string>(ReportProgress);
await catalogues.Create(createProgress);
});
void ReportProgress(string reportMessage)
{
//update log here
}
usage inside Create
async Task Create(IProgress<string> progress)
{
foreach (var category in categories)
{
// some staff
progress.Report($"{category} - completed");
}
}
IProgress example

How to show an animated loading form while executing code in Windows Forms C#

I want to display an animated loading form while executing some code in the main form. The animated form is used only to show the user that an operation is executing and I want to close it once the operation finishes. The code that I'm using is:
public partial class Form_main_admin : Form
{
private Thread loadingThread;
private string loadingText;
public Form_main_admin()
{
InitializeComponent();
}
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
switch (main_tabControl.SelectedIndex)
{
case 0:
// ...
break;
case 1:
showLoadingForm("Loading");
// Load a datagridview (load data, adjust column widths) in Form_main_admin
closeLoadingForm();
break;
}
}
private void showLoadingForm(string text)
{
loadingText = text;
loadingThread = new Thread(new ThreadStart(openLoadingForm));
loadingThread.Start();
}
private void openLoadingForm()
{
try
{
Form_loading loadingForm = new Form_loading(loadingText);
loadingForm.ShowDialog();
}
catch
{
Thread.ResetAbort();
}
}
private void closeLoadingForm()
{
try
{
loadingThread.Abort();
}
catch
{
Thread.ResetAbort();
}
}
}
The problem is that I get a "Thread was being aborted" exception when I quickly change between tabs (see image in link below).
http://postimg.org/image/bvre2bmi5/
I do not want the user to see this exception if he chages tabs too fast. After reading other posts on this forum I realized that my implementation is not recommended. Could someone please show me how to properly implement this functionality?
If you need an animated progress form, try to use BackgroundWorker class to perform loading in an additional thread:
public partial class MainForm : Form
{
/// <summary>
/// Some progress form
/// </summary>
WaitForm waitForm = new WaitForm();
/// <summary>
/// https://msdn.microsoft.com/library/cc221403(v=vs.95).aspx
/// </summary>
BackgroundWorker worker = new BackgroundWorker();
public MainForm()
{
InitializeComponent();
worker.DoWork += (sender, args) => PerformReading();
worker.RunWorkerCompleted += (sender, args) => ReadingCompleted();
}
/// <summary>
/// This method will be executed in an additional thread
/// </summary>
void PerformReading()
{
//some long operation here
Thread.Sleep(5000);
}
/// <summary>
/// This method will be executed in a main thread after BackgroundWorker has finished
/// </summary>
void ReadingCompleted()
{
waitForm.Close();
}
private void button1_Click(object sender, EventArgs e)
{
//Run reading in an additional thread
worker.RunWorkerAsync();
//Show progress form in a main thread
waitForm.ShowDialog();
}
}

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on [duplicate]

This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 6 years ago.
I want to send temperature value from a microcontroller using UART to C# interface and Display temperature on Label.Content. Here is my microcontroller code:
while(1) {
key_scan(); // get value of temp
if (Usart_Data_Ready())
{
while(temperature[i]!=0)
{
if(temperature[i]!=' ')
{
Usart_Write(temperature[i]);
Delay_ms(1000);
}
i = i + 1;
}
i =0;
Delay_ms(2000);
}
}
and my C# code is:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
textBox1.Text = txt.ToString();
}
but exception arises there "Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on"
Please tell me how to get temperature string from my microcontroller and remove this Error!
The data received in your serialPort1_DataReceived method is coming from another thread context than the UI thread, and that's the reason you see this error.
To remedy this, you will have to use a dispatcher as descibed in the MSDN article:
How to: Make Thread-Safe Calls to Windows Forms Controls
So instead of setting the text property directly in the serialport1_DataReceived method, use this pattern:
delegate void SetTextCallback(string text);
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
So in your case:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
SetText(txt.ToString());
}
I don't know if this is good enough but I made a static ThreadHelperClass class and implemented it as following .Now I can easily set text property of various controls without much coding .
public static class ThreadHelperClass
{
delegate void SetTextCallback(Form f, Control ctrl, string text);
/// <summary>
/// Set text property of various controls
/// </summary>
/// <param name="form">The calling form</param>
/// <param name="ctrl"></param>
/// <param name="text"></param>
public static void SetText(Form form, Control ctrl, string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (ctrl.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
form.Invoke(d, new object[] { form, ctrl, text });
}
else
{
ctrl.Text = text;
}
}
}
Using the code:
private void btnTestThread_Click(object sender, EventArgs e)
{
Thread demoThread =
new Thread(new ThreadStart(this.ThreadProcSafe));
demoThread.Start();
}
// This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
ThreadHelperClass.SetText(this, textBox1, "This text was set safely.");
ThreadHelperClass.SetText(this, textBox2, "another text was set safely.");
}
you can simply do this.
TextBox.CheckForIllegalCrossThreadCalls = false;
Use the following extensions and just pass the action like:
_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
Extension class:
public static class CrossThreadExtensions
{
public static void PerformSafely(this Control target, Action action)
{
if (target.InvokeRequired)
{
target.Invoke(action);
}
else
{
action();
}
}
public static void PerformSafely<T1>(this Control target, Action<T1> action,T1 parameter)
{
if (target.InvokeRequired)
{
target.Invoke(action, parameter);
}
else
{
action(parameter);
}
}
public static void PerformSafely<T1,T2>(this Control target, Action<T1,T2> action, T1 p1,T2 p2)
{
if (target.InvokeRequired)
{
target.Invoke(action, p1,p2);
}
else
{
action(p1,p2);
}
}
}
Along the same lines as previous answers,
but a very short addition that Allows to use all Control properties without having cross thread invokation exception.
Helper Method
/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c,Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
else return false;
return true;
}
Sample Usage
// usage on textbox
public void UpdateTextBox1(String text)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
textBox1.Text = ellapsed;
}
//Or any control
public void UpdateControl(Color c,String s)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(myControl, () => UpdateControl(c,s))) return;
myControl.Text = s;
myControl.BackColor = c;
}
Use a shared container to transfer data between threads.

Update progress bar in MainWindow from a different Thread

I know this question has been asked several times an I spent all day trying to understand other answers, but since I am very new to C# and WPF nothing helped me so far. I will try to explain my exact problem as much as I can so it will directly help me.
In my MainWindow.xaml I have a progress bar and some button starting a new thread and a long calculation:
<ProgressBar Height="....... Name="progressBar1"/>
<Button Content="Button" Name="button1" Click="button1_Click" />
Now within my MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ParameterizedThreadStart(MyLongCalculation));
ParameterClass myParameters = new ParameterClass();
thread.Start(myParameters);
}
public void MyLongCalculations(object myvalues)
{
ParameterClass values = (ParameterClass)myvalues;
//some calculations
}
}
public class ParameterClass
{
//public variables...
}
Now somehow I have to include somethign in my method MyLongCalculations that will keep updating progressBar1. However, I just can't manage to get it working.
I know all this is very simple, but unfortunately it is the level I am at the moment on with C# so I hope an answer not too complicated and as detailed as possible would be great.
Background worker is well suited for this.
try this:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Process data
ProcessDataAsync(new ParameterClass { Value = 20 });
}
/// <summary>
/// Processes data asynchronously
/// </summary>
/// <param name="myClass"></param>
private void ProcessDataAsync(ParameterClass myClass)
{
// Background worker
var myWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
};
// Do Work
myWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
// Set result
e.Result = MyLongCalculations(myClass);
// Update progress (50 is just an example percent value out of 100)
myWorker.ReportProgress(50);
};
// Progress Changed
myWorker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
};
// Work has been completed
myWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
// Work completed, you are back in the UI thread.
TextBox1.Text = (int) e.Result;
};
// Run Worker
myWorker.RunWorkerAsync();
}
/// <summary>
/// Performs calculations
/// </summary>
/// <param name="myvalues"></param>
/// <returns></returns>
public int MyLongCalculations(ParameterClass myvalues)
{
//some calculations
return (myvalues.Value*2);
}
}
/// <summary>
/// Custom class
/// </summary>
public class ParameterClass
{
public int Value { get; set; }
}
You can use Dispatcher.BeginInvoke() to push UI changes on the UI thread rather than worker thread. Most important thing - you need to access Dispatcher which is associated with UI thread not a worker thread you are creating manually. So I would suggest cache Dispatcher.Current and then use in a worker thread, you can do this via ParametersClass or just declaring a dispatchr field on a class level.
public partial class MainWindow
{
private Dispatcher uiDispatcher;
public MainWindow()
{
InitializeComponents();
// cache and then use in worker thread method
this.uiDispatcher = uiDispatcher;
}
public void MyLongCalculations(object myvalues)
{
ParameterObject values = (ParameterObject)myvalues;
this.uiDispatcher.BeginInvoke(/*a calculations delegate*/);
}
}
Also if you need to pass a UI dispatcher in some service/class (like ParametersClass) I would suggest take a look at this nice SO post which show how you can abstract it by an interfaces with ability to push UI changes synchronously/asynchronously so it would be up to a caller (basically use Invoke() or BeginInvoke() to queue a delegate in the UI messages pipeline).

Working a textbox in a form from another class in C#

So I have
public class Form1 : Form {}
and
class Updater {}
And I have textBox1 on Form1, along with many other controls...
So here is my dilemma: I have a while(true) {} loop in Updater, and I couldn't stick it in the Form1 class, because it was preventing the form from loading. And I need to update a multi-lined textbox (textBox1) on Form1, from Updater. Updater is a TCP client, and when it receives information I need it to += its information into the textbox.. But how do I access the textbox from a thread different from the one it was created in?
Edit: I'm looking for code examples, please.
Why don't you declare an event in Updater class? Then you can raise this event when you get data from TCP.
public class Updater
{
public delegate void DataReceivedEventHandler(object sender,DataEventArgs e);
public event DataReceivedEventHandler DataReceived = delegate { };
public void ReadData()
{
//here you will get data from what ever you like
//upon recipt of data you will raise the event.
//THIS LOOP IS FOR TESTING ONLY
for (var i = 1; i < 101; i++)
{
//PASS REAL DATA TO new DataEventArgs
DataReceived(this, new DataEventArgs("Event " + i));
Thread.Sleep(500);
}
}
}
public class DataEventArgs : EventArgs
{
public string Data { get; set; }
public DataEventArgs(string data) : base()
{
Data = data;
}
}
In you form:
//you will setup "Updater" in some else way. I've written this function
//which I call on a button click for testing
private void Init()
{
var u = new Updater();
u.DataReceived += delegate(object sender, DataEventArgs e)
{ SetTextboxText(e.Data); };
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += delegate(object sender, DoWorkEventArgs e)
{ ((Updater)e.Argument).ReadData(); };
bw.RunWorkerAsync(u);
}
private void SetTextboxText(string s)
{
if (TEXT_BOX.InvokeRequired)
{
//This techniques is from answer by #sinperX1
BeginInvoke((MethodInvoker)(() => { SetTextboxText(s); }));
return;
}
TEXT_BOX.Text += Environment.NewLine + s;
}
If Form1 has a reference to Updater then you can put an event on the Updater Class that Form1 can subscribe to. When Updater has data (or whatever reason it needs to update the form) it sets the event, the form catches the event and updates the textbox.
Example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Updater textboxUpdater = new Updater();
textboxUpdater.Updated += s => {textBox1.Text = s;};
}
}
public class Updater
{
public delegate void UpdateEventHandler(string eventName);
public event UpdateEventHandler Updated = delegate { };
private bool needUpdating;
public void Process()
{
while (true)
{
//Processing
if (needUpdating)
{
Updated("something");
}
}
}
}
Cross-threading is caused when a thread is used to access a control that did not create the control. To get around it you Invoke.
http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
Example:
/// <summary>
/// This is a thread safe operation.
/// </summary>
/// <param name="text"></param>
public void SetTextBoxText(string text)
{
if (InvokeRequired)
{
Invoke((MethodInvoker)delegate { SetText(text); });
return;
}
// To get to this line the proper thread was used (by invoking)
myTextBoxt.Text += text;
}
/// <summary>
/// This is an alternative way. It uses a Lambda and BeginInvoke
/// which does not block the thread.
/// </summary>
/// <param name="text"></param>
public void SetTextBoxText(string text)
{
if (InvokeRequired)
{
BeginInvoke((MethodInvoker)(() => { SetText(text); }));
return;
}
// To get to this line the proper thread was used (by invoking)
myTextBox.Text += text;
}

Categories