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?
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));
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.
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 ???
}
}
Related
I have a form that sets up and displays the text box. In the form load method, I am starting a new thread from a completely separate class name Processing:
private void Form1_Load(object sender, EventArgs e)
{
Processing p = new Processing();
Thread processingThread = new Thread(p.run);
processingThread.Start();
}
Here is the processing class. What I would like to do is create a method in Utilities class that will allow me to update the text box from whatever class I would need to:
public class Processing
{
public void run()
{
Utilities u = new Utilities();
for (int i = 0; i < 10; i++)
{
u.updateTextBox("i");
}
}
}
Then finally the Utilites class:
class Utilities
{
public void updateTextBox(String text)
{
//Load up the form that is running to update the text box
//Example:
//Form1.textbox.appendTo("text"):
}
}
I have read about the Invoke methods, SynchronizationContext, Background threads and everything else, but almost all examples are using methods in the same class as the Form thread, and not from separate classes.
The Progress class is designed specifically for this.
In your form, before starting the background thread, create a Progress object:
Progress<string> progress = new Progress<string>(text => textbox.Text += text);
Then provide that progress object to your worker method:
Processing p = new Processing();
Thread processingThread = new Thread(() => p.run(progress));
processingThread.Start();
Then the processor can report the progress:
public class Processing
{
public void run(IProgress<string> progress)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);//placeholder for real work
progress.Report("i");
}
}
}
The Progress class, internally, will capture the synchronization context where it was first created and marshall all event handlers invoked as a result of the Report calls to that context, which is just a fancy way of saying it takes care of moving to the UI thread on your behalf. It also ensures that all of your UI code stays inside the definition of the Form, and all of the non-UI code it outside of the form, helping separate business code from UI code (which is a very good thing).
I would add a AppendText() method to your Form1 class, like this:
public void AppendText(String text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(AppendText), new object[] { text });
return;
}
this.Textbox.Text += text;
}
Then from your utility class, call it like this:
class Utilities
{
Form form1; // I assume you set this somewhere
public void UpdateTextBox(String text)
{
form1.AppendText(text);
}
}
There is a very good comprehensive review of threading in .NET that can be found here: Multi-Threading in .NET. It has a section on Threading in WinForms that would help you a lot.
I've been trying to learn delegates.I just created a button,label and checkbox. If I click checkbox, the time format changes. If i click the button , i print the date accordingly. However when trying to use asynchromous delegate i.e., to use another thread, i am stuck with an error
public delegate void AsyncDelegate(bool seconds);
public partial class Form1 : Form
{
AsyncDelegate ad;
TimeZ t = new TimeZ();
public Form1()
{
InitializeComponent();
}
private void btn_async_Click(object sender, EventArgs e)
{
ad = new AsyncDelegate(t.GetTime);
AsyncCallback acb = new AsyncCallback(CB);
if (chk_sec.Checked)
{
ad.BeginInvoke(true, acb, null);
}
else
ad.BeginInvoke(false, acb, null);
}
public void CB(IAsyncResult ar)
{
t.Tim = ar.ToString();
ad.EndInvoke(ar);
lbl_time.Text = t.Tim;
}
and in another class library i get Timez used above. I add a reference of it in the project
public class TimeZ
{
private string tim;
public string Tim
{
get
{
return tim;
}
set
{
tim = value;
}
}
public string GetTime(bool seconds)
{
if (seconds)
{
return DateTime.Now.ToLongTimeString();
}
else
return DateTime.Now.ToShortTimeString();
}
}
However i get this error when i run the program:
Cross-thread operation not valid: Control 'lbl_time' accessed from a thread other than
the thread it was created on.
Can u help me out on how to solve this?
You cannot access forms and controls properties and methods from a thread that is not the form thread.
In windows, each window is bound to the thread that created it.
You can do that only with Control.BeginInvoke or the more useful System.Threading.SynchronizationContext class.
See http://msdn.microsoft.com/it-it/library/system.threading.synchronizationcontext(v=vs.95).aspx
See http://msdn.microsoft.com/it-it/library/0b1bf3y3(v=vs.80).aspx
It means, you have to post through synchronization context for example another async delegate in form thread.
public partial class Form1 : Form
{
AsyncDelegate ad;
TimeZ t = new TimeZ();
// Our synchronization context
SynchronizationContext syncContext;
public Form1()
{
InitializeComponent();
// Initialize the synchronization context field
syncContext = SynchronizationContext.Current;
}
private void btn_async_Click(object sender, EventArgs e)
{
ad = new AsyncDelegate(t.GetTime);
AsyncCallback acb = new AsyncCallback(CB);
if (chk_sec.Checked)
{
ad.BeginInvoke(true, acb, null);
}
else
{
ad.BeginInvoke(false, acb, null);
}
}
public void CB(IAsyncResult ar)
{
// this will be executed in another thread
t.Tim = ar.ToString(); // ar.ToString()???? this will not give you the time for sure! why?
ad.EndInvoke(ar);
syncContext.Post(delegate(object state)
{
// This will be executed again in form thread
lbl_time.Text = t.Tim;
}, null);
}
I don't know why you need an asynchronous callback to print time however :) really don't know why, thinking it is just some test code.
I am using the functions of the following class to invoke a messagebox. I am using thread.Start method to show the messagebox. The problem is it is not reaching to the respective function when thread.Start is called. Am i missing anything?
class MessageManager
{
string _message;
public MessageManager(string message)
{
_message = message;
}
public void ShowBigMessage()
{
Thread thread = new Thread(DisplayBigMessage);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
// thread.Join();
}
public void ShowNormalMessage()
{
Thread thread = new Thread(DisplayNormalMessage);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
//thread.Join();
}
private void DisplayBigMessage()
{
BigAppMessage appMessage = new BigAppMessage(_message);
appMessage.Show();
}
private void DisplayNormalMessage()
{
AppMessage appMessage = new AppMessage(_message);
appMessage.ShowDialog();
}
}
This is called inside a thread/delegate as below. I added this code into my program becuase it was raising
The calling thread must be STA, because many UI components require
this.
exception before
MessageManager message = new MessageManager("This is a test message.");
message.ShowBigMessage();
public partial class BigAppMessage : Window
{
public BigAppMessage(String message)
{
InitializeComponent();
myControl.setMessage(message); // mycontrol is just user control with a
//label on it
}
}
The Show() method requires a message loop. Fix:
private void DisplayBigMessage()
{
Application.Run(new BigAppMessage(_message));
}
There's already a message loop built into the ShowDialog() method. Using a thread to just display a window has no advantages, only problems.
In visual studio go to Debug->Exceptions and check the "thrown" box next to CLR exceptions. this will tell you where your problem is. Probably its a cross thread issue since you would ordinarily only interact with the UI on the UI thread.
I have my main GUI from where I start a long running method in a separate thread.
Now from within this separate thread I need to create and show a new form.
But when I show this new form all the controls are stuck an the window says "not responding".
Which is the best way of solving this ??
regards
Thomas
Put the code that creates the new GUI into the main GUI class and then call the main GUI's Invoke method, or raise an event that the main GUI can subscribe to to know when to trigger the new GUI. If you choose the latter, be sure to use InvokeRequired to determine if you can call the method that creates the new GUI directly or if you need to use an Invoke to get back onto the GUI thread to create the new GUI.
You need to learn about Control.BeginInvoke/Invoke and all that means. Just remember that all UI operations need to occur on the main thread (UI thread) because that is the thread that owns the message pump. You need to call back into that thread in order to have UI actions happen.
Here's an intro to the BeginInvoke/Invoke stuff: http://weblogs.asp.net/justin_rogers/pages/126345.aspx
In order to help further here's a complete working code example that should highlight the basics.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var worker = new Worker(this);
worker.Start();
}
public void updateLabel(int value)
{
if(label1.InvokeRequired) { // check if on UI thread
//If true use begin invoke to call update on UI thread
//this calls the anonymous delegate in the UI thread
//that then calls the updateLabel function again to set the label's text
label1.BeginInvoke(new MethodInvoker(() => this.updateLabel(value)));
return;
}
label1.Text = value.ToString();
}
public void showNewForm()
{
if(this.InvokeRequired) { // check if on UI thread
this.BeginInvoke(new MethodInvoker(this.showNewForm)); // we need to create the new form on the UI thread
return;
}
var anotherForm = new Form1();
anotherForm.Show();
}
}
class Worker
{
private volatile bool stop = false;
private Form1 form;
public Worker(Form1 form)
{
this.form = form;
}
public bool Stop
{
get
{
return stop;
}
set
{
stop = value;
}
}
public void Start()
{
var thread = new Thread(this.work);
thread.IsBackground = true;
thread.Start();
}
private void work()
{
int i = 0;
while(!stop) {
i++;
Thread.Sleep(100);
form.updateLabel(i);
if(i == 50) {
form.showNewForm(); // call into form
// can also do the invokerequired check here and create new form w/ anonymous functions
// however, I'd recommend keeping all the UI code in the same place.
}
}
}
}
Use Form.Show instead of Form.ShowDialog. You can also use a BackgroundWorker to do concurrent tasks.
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 1 year ago.
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);
}
}
}
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 and actions:
if(textBox1.InvokeRequired == true)
textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";});
else
textBox1.Text = "Invoke was NOT needed";
InvokeRequired checks whether the code is running on the UI thread or on a different thread. Only the UI thread is allowed to perform UI operations like changing the content of a control. When its not running on the UI thread, then the Invoke passes the operation temporarily to the UI thread.
This may slow down the performance and reduce the benefits of multy threading. In this case a queue with an event can be used. The backround thread puts the change on the queue and continues. The UI changes are performed in the event asynchrounisly.
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);
}
}
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.
What's even easier is to just use the BackgroundWorker control...
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;
});
}