Working in Winforms with C#, I have a main form that creates a second UI form on a separate thread.
viewThread = new Thread( ( ) =>
{
views = new Views.Views( displayRotators, screenLocationLeft, screenLocationTop, screenHeight, screenWidth );
Application.Run( views );
} );
viewThread.SetApartmentState( ApartmentState.STA );
viewThread.Start( );
On the second UI form, I now need to invoke a method to start a display loop that will run until I terminate the thread. I've spent about three hours trying to find a way to do this, but all I can find are examples of calling methods on the main UI thread from the worker thread, not calling methods on the worker thread FROM the main UI thread. Can anyone tell me how I can do this?
Thanks.
There are very few cases where running multiple UI threads really makes sense. Unless you have a very compelling reason to do that, I would go back to a single UI thread.
Assuming you must have multiple UI threads, the golden rule is that Controls may only be updated from the UI thread they were created on.. If you want to call methods in general, you do not have to marshal them at all. However, if those methods in turn interact with a UI control, you would just marshal that call exactly the same way you would if the call were from any other thread that is not the UI thread belonging to the control.
If you might have something like
btnOnForm1OnUIThread1_Click(...)
{
UpdateForm2OnUIThread2();
}
UpdateForm2OnUIThread2()
{
if (control.InvokeRequired)
{
// Syntax of this line may be slightly off as I'm writing from memory ...
// I normally use an extension method
control.Invoke(UpdateForm2OnUIThread2);
}
else
{
control.Text = "Blah";
}
}
a. there is nothing wrong with using multiple UI threads if you know what you do.
b. it is incorrect and confusing calling a UI thread worker - UI thread #2 is same as #1
from the developer standspoint. once you realize it - many problem will clear themselves.
c. if you want to call view2.SomeMethod() [run in UI thread #2] from view1 [run in UI thread #1] - call BeginInvoke on the view2.
The code below is simple to adopt. button1 - launches the second UI thread. button2 - calls method from UI thread 1 on the form which is in UI thread 2
Form2 form;
private void button1_Click(object sender, EventArgs e)
{
Thread viewThread = new Thread(() =>
{
form = new Form2();
Application.Run(form);
});
viewThread.SetApartmentState(ApartmentState.STA);
viewThread.Start();
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
if (form != null)
{
form.BeginInvoke(new Action(() =>
{form.Method("Form1 is calling...");}));
}
}
in the form 2 :
public void Method(string s)
{
label1.Text = string.Format("{0} '{1}'",
Thread.CurrentThread.ManagedThreadId, s);
}
you will see that the thread number is different and yet the string has crossed them OK.
Related
For example I use the following code:
private void startCalculcationButton_Click(object sender, EventArgs e) {
int number;
if (int.TryParse(this.numberTextBox.Text, out number)) {
this.calculationResultLabel.Text = "(computing)";
Task.Factory.StartNew(() => {
int result = LongCalculation(number);
this.calculationResultLabel.BeginInvoke(new ThreadStart(() => {
this.calculationResultLabel.Text = result.ToString();
}));
});
}
}
There I call this.calculationResultLabel.BeginInvoke(...) to switch to the UI-Thread for setting the Result to the Label.
I could also use this.Invoke(...) (this is the Form). Does it make a difference internally?
Invoke on Control vs. Invoke on Form
I could also use this.Invoke(...) (this is the Form). Does it make a difference internally?
It sounds like you are asking two different things. Your title says Invoke but your code is using BeginInvoke but you seem to be also asking about whether to invoke on a form or control.
It makes no difference whether you xxxInvoke to the form or a specific control. Both are UI elements and both can marshal calls for you.
If you are asking about the difference between Invoke and BeginInvoke, two methods that can marshal a call to the UI thread via the message pump, there is a difference between them. Invoke can lead to thread deadlock whilst the latter won't.
Anyway, since you are using Task and that you update the UI immediately after the long running task, you are arguably better doing:
private void startCalculcationButton_Click(object sender, EventArgs e) {
int number;
if (int.TryParse(this.numberTextBox.Text, out number))
{
this.calculationResultLabel.Text = "(computing)"; // UI thread
var result = await Task.Run(() =>
LongCalculation(number)); // threadpool thread
calculationResultLabel.Text = result.ToString(); // UI thread
}
}
No fiddly manual marshalling required via BeginInvoke or dangerous-to-use Invoke.
I am a beginner in high level programming languages. I am trying to make an WForms app for a serial port , im using VS 2010 C#
I get the following error:
Cross-thread operation not valid: Control 'rtxtDataArea' accessed from a thread other than the thread it was created on.
This happens here:
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
I have tried to change the color of a textbox when I receive some data.
It fires that cross thread error.
The question is why it does not fire the same error here, when I try to change the color of a label?
private void btnConnect_Click(object sender, EventArgs e)
{
if (ComPort.IsOpen)
{
disconnect();
}
else
{
connect();
rdText.ForeColor = Color.Blue;//ok, it works
}
}
; this works ; the first does not.
Why? Is not the ComPort_DataReceived_1 the same nature as btnConnect_Click ?
Or what is the reason?
I have read a lot about threads, but I understood nothing I can use, Can someone give an intuitive explanation ?
In winforms there is only one thread that may change anything on the UI like enable buttons, change text boxes, etc. Usually this is the UI thread. Quite often this is the only thread you have.
However, if you start a new thread, this thread might want to change the UI. This happens especially if this new thread fires an event that is received by your form.
Whenever you see the message accessed from a thread other than the thread it was created on, you can be almost certain this is the case.
The most easy solution to solve this is using the functions Control.IsInvokeRequired and Control.Invoke. The pattern to do this is as follows. The following function updates myButton on myForm
private void UpdateMyButton (MyType myParameter)
{
if (myButton.InvokeRequired)
{ // this is not the thread that created the button to update
this.Invoke(new MethodInvoker( () => this.UpdateMyButton(myParameter)));
// this will let the UI thread call this function with the same parameter.
}
else
{ // Now called by the UI thread: this thread may change myButton
myButton.Enabled = myParameter.ShouldButtonEnable;
myButton.Text = myParameter.ButtonText;
}
}
By the way, if you have to update several controls on your form you ought to check InvokeRequired for each of these controls. However, since they are usually created by the same UI thread it is sufficient to check for this.InvokeRequired.
Control.Invoke returns after the invoke is completed, so after all items are updated. Upon return of Invoke you can use the result of UpdateMyButton.
If you don't want your non-ui thread to wait for completion of UpdateMyButton, consider the use of Control.BeginInvoke: "hey UI thread, whenever you've got time, can you UpdateMyButton for me. Of course in that case you can't use the results of UpdateMyButton
Because "DataReceived" runs on another thread and not UI thread. You must use Invoke for that :
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
if (InvokeRequired)
{
// If not on UI thread, then invoke
Invoke(new MethodInvoker(() =>
{
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}));
}
else
{
// On UI thread, invoke not needed
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
}
I have a c# .NET winforms app making this async call:
simpleDelegate.BeginInvoke(null, null);
My function is being called by the delegate and that all works great. The problem is, after the function finishes on the worker thread, I need the main thread to update some controls on my winform. If the worker thread tries to update these controls, .NET freaks out. But I need the main thread to remain responsive to user actions, and then call my function UpdateFormAfterServerCall() ONLY AFTER the worker thread finishes calling the async function.
I would greatly appreciate if you can give me a concise code sample, rather than abstractly explain how to do this. I've read a hundred explanations already, and am just having trouble wiring it together correctly.
Note: Before the BeginInvoke I have:
simpleDelegate = new MethodInvoker(CallServer);
From different thread if you want to update GUI which is owned by another thread use MethodInvoker
if(control.InvokeRequired)
control.Invoke( (MethodInvoker) ( ()=> updating_function() ) );
else
updating_function();
You could use a BackgroundWorker:
BackgroundWorker bw = new BackgroundWorker();
string result = null;
bw.DoWork += (s, e) =>
{
// Executes on background thread.
// UI remains responsive to user activity during this time.
result = CallServer();
};
bw.RunWorkerCompleted += (s, e) =>
{
// Executes on UI thread upon completion.
resultTextBox.Text = result;
};
bw.RunWorkerAsync();
The Control class (Form is a Control as well) has an Invoke method, you can call this from any thread to execute code on the GUI thread.
In addition, Control has a convenient InvokeRequired property that informs you whether you are on the GUI thread already. You could for instance create the following method in your form:
public class MyForm
{
// ...
public void UpdateMe()
{
if (InvokeRequired)
{
Invoke(new Action(UpdateMe));
return;
}
// Code to update the control, guaranteed to be on the GUI thread
}
}
Here is the code sample [what you want exactly] -
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
& you can read about all flavours of async here -
http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.100).aspx
References:
http://msdn.microsoft.com/en-us/library/ms171728.aspx
http://stackoverflow.com/questions/5408155/how-to-make-delegate-thread-sta
I wanted to create a new Thread and make it STA therefore I was not able to use asynchronous delegates or BackgroudWorker (as mentioned in references link 1) Therefore I end up creating a Thread of my own make it STA and attach a callback to know when the Task is complete. The code is something like below and even though I am using invoke required, I still get InvalidOperationException (once in a while)
delegate UpdateEventHander(Object sender, EventArgs e);
class MyTask{
// to generate an event
public event UpdateEventHandler Finished;
public void Start(){
Result = // something that require the thread to be STA.
Finished(this, EventArgs.Empty);
}
public Result GetResult(){
return Result;
}
}
Class Foo : Form{
// It has many UI Controls obviously
public void doSomething(){
MyTask task = new MyTask();
task.Finished += new UpdateEventHander(CompletionHandler);
Thread thread = new Thread(new ThreadStart(task.Start));
thread.setAppartmetnState(AppartmentState.STA);
thread.start();
}
public void CompletionHandler(Object sender, EventArgs e){
MyTask task = (MyTask) sender;
if (oneOfMyControls.InvokeRequired){
delegateToUpdateUIconrols del = new delegateToUpdateUIconrols(updateUIControls);
del.invoke();
}else{
UpdateUIControls();
}
}
public delegate void delegateToUpdateUIconrols();
public void UpdateUIControls(){
// It updates UI controls
// Datagrid view value properties like backgroud color and stuff.
// change text in the label.
}
}
Question 1: Which thread will UpdateUIControls execute ? - if you say "Main UI Thread" - then in that case how will the system know if its supposed to run in Main UI thead and NOT some OTHER thread? I am not passing any reference (about Main UI thread) when I call invoke() .. so invoke() is technically executed on the same thread..
Question 2: Once in a while, I get the InvalidOperationException. Exactly this one
http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/6b450a21-e588-414a-afae-9adabfd03674/
If UpdateUIControls is executing in the main UI thread, there should be not prblem, Right? So, I guess answer to my question really depends upon question 1.
I will appreciate if someone share his/her wisdom on this
Karephul
Controls have thread-affinity; you can only safely talk to them from their creating thread.
You are checking InvokeRequired; however, you are mixing up Delegate.Invoke (runs on the current thread) with Control.Invoke (runs on the UI thread); very different meaning. It should be:
oneOfMyControls.Invoke(del [, args]);
I have a worker thread that needs to add items to a BindingList. However, the BindingList is databound to a DataGridView. So, when I try to add to the list, I get an InvalidOperationException (Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.)
Normally for this exception you would do:
if(winformControl.InvokeRequired) {
winformControl.Invoke(MethodDelegate);
}
However, the databinding confuses things, as there is no Winform control in sight. All I have is the following line, which throws the exception:
ClassInstance.MyBindingList.Add(myObject);
If you have a solution specifically for this scenario, great.
If not, how can I get the worker thread to tell my main thread to perform a particular method (with several parameters supplied by the worker thread)? This may be a preferable option, since my worker thread is actually doing a bunch of stuff at the moment (like writing to the database), and I'm not sure if everything is thread-safe. I'm a student, and new to multithreading, and it really is not my forte yet.
One option here is to tell BindingList<T> to use the sync-context, like this - however, this is arguably not the best approach. I wonder if you could expose your data via an event or similar (rather than adding to the list directly) - then have your UI handle the event by sending to the right thread and adding to the UI model.
In your worker class constructor, try this:
private System.Threading.SynchronizationContext mContext = null;
/// <summary>
/// Constructor for MyBackgroundWorkerClass
/// </summary>
public MyBackgroundWorkerClass(System.Threading.SynchronizationContext context)
{
mContext = context;
}
Then, when you need to invoke something on the UI thread:
private void CallOnTheUiThread(object dataToPassToUiThread)
{
// Make sure the code is run on the provided thread context.
// Make the calling thread wait for completion by calling Send, not Post.
mContext.Send(state =>
{
// Change your UI here using dataToPassToUiThread.
// Since this class is not on a form, you probably would
// raise an event with the data.
}
), null);
}
When creating your worker class from a form on the UI thread, this is what you would pass as the synchronization context.
private void Form1_Load(object sender, EventArgs e)
{
var worker = new MyBackgroundWorkerClass(SynchronizationContext.Current);
}
You can fire an event to the main, UI, thread and there have:
if (this.InvokeRequired)
{
this.Invoke(...);
}
so you are testing on the main Window itself.
BackgroundWorkers are easy to implement if you are able to given the requirements.
Define a DoWork method that runs on a background thread such as saves to the database. The RunWorkerCompleted method is called when DoWork finishes. RunWorkerCompleted runs on the UI thread, and you can update the view's list with no problems.
// on the UI thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += RunWorkerCompleted;
worker.RunWorkerAsync("argument");
Events:
static void DoWork(object sender, DoWorkEventArgs e)
{
e.Result = "4";
}
static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
string a = (string)e.Result;
Console.WriteLine(a);
}
else
{
Console.WriteLine(e.Error.Message);
}
}