Should I Invoke or change my Get/Set Method? "InvalidOperationException" C# - c#

I get an InvalidOperationException with the message,
Control control name accessed from a thread other than the thread it was created on.
The "Control" was created by a Thread that is no longer doing any work, and I don't know how if I can invoke the method or if I should create a local variable that contains the value of the tags_richtextbox.
The class that is Giving the error:
public class details_Panel
{
public string tags
{
get { return tags_richtextbox.Text; }
set { tags_richtextbox.Text = value; }
}
private RichTextBox tags_richtextbox = new RichTextBox() { DetectUrls = false, Dock = System.Windows.Forms.DockStyle.Fill, ReadOnly = true };
}
The Function Giving the error:
detailsPanelSaveData.tags.AddRange(ent.detailspanel.tags.Split(','));
So should I Invoke or add a private variable that has the same value as tags_richtextbox.Text and if I should invoke, whats the syntax to do that and wait for it to finish? (I'm still learning about threading and figuring out the correct invoke syntax)
Edit: DetailPanelSaveData.tags is a List<string> Just realized I wasn't very clear
So to Clarify I'm trying to save a split string in a list of strings.

The "Control" was created by a Thread that is no longer doing any work
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to completely understand the problem. However, the idea of there being a thread in which some instance of a control was created, and yet that thread "is no longer doing any work" is a bit weird, to say the least.
Control objects must only be created in a thread that is running the UI. In most programs, there is only one such thread and there should only ever be one such thread. If you have code running in a different thread, and due to some operation in that thread there is a point at which a control needs to be created, you need to transfer execution to that one UI thread for the purpose of creating the control, so that the control is created in the correct thread.
Without a better code example, it's not clear what else you need to fix. But for sure, you need to change the code where that control is created so that you're creating the control in the UI thread.
Elsewhere, e.g. when you are converting the text into an array of tags to add to your list, you may also need to transfer execution to the UI thread when you access the tags property where the tags_richtextbox object is accessed. Or maybe you won't. That all depends on where that code is executing; if it's in the UI thread, then you're all set. Otherwise, yes…you need to wrap the operation in some kind of cross-thread invocation.
NOTE: nowhere in your question are you specific about which GUI API in .NET you are using. Judging from the little bit of code and your comments in the question, I'm guessing this is Winforms. But WPF and Winrt (Store apps) have the same issue, and so the above advice applies equally in all scenarios. Of course, the exact technique for transferring execution to the UI thread varies according to API. Winforms uses Control.Invoke(), WPF uses Dispatcher.Invoke(), and Winrt uses Dispatcher.RunAsync(). It is these methods (or their asynchronous equivalents, in the case of Winforms or WPF) to which I am referring when I describe a "cross-thread invocation" or the need to "transfer execution to the UI thread".
NOTE: there is one exception to some of the above, which I hesitate to even mention because I doubt it applies in your scenario, and even if it does, the first thing you should do is fix your code so it doesn't. But for completeness, I am compelled to mention that a program can have more than UI thread running at a time. This is rare and should be avoided, but it can happen. If it does, then it is possible that one control object could be created in a thread that is legitimately different from that which owns some other control object. Now, in that case it would be your responsibility to ensure that any given UI thread continues to run for as long as any of the objects it owns still exist, and you will still need to use the GUI API's mechanism for transferring execution to a different thread when that object is accessed by a thread other than the one which owns it. But that is a way that, even though still not correct, you could wind up with a control object that was created by a UI thread that "is no longer doing any work".

Related

Changing Label properties in a thread [duplicate]

This question already has answers here:
Why can I access controls from a form in a thread without cross thread exception?
(3 answers)
Closed 6 years ago.
I'm writing the functionality for a particular part of a program which requires me to change the text of a number of labels on a window continuously until the user presses a button. I've created a thread to accomplish this, and quickly noticed that the runtime complains about cross-thread accessing of a label, giving the error message:
Cross-thread operation not valid: Control 'label2' accessed from a thread other than the thread it was created on.
I've since learned that accessing a System.Windows.Forms.Control member in a thread other than the UI thread is not allowed. However, my question has to do with the following simplified code:
private void myThread(){
//'labels' is an array of all labels on my form
Label currLabel = labels[rand.Next(0, labels.Length)];
currLabel.BackColor = Color.Yellow; //This works with no complaint from the runtime
currLabel.Text = "Hello"; //This causes the previously-mentioned error
}
So, why am I able to change some properties of labels in a thread other than the UI thread, but others are just off-limits? Am I missing some larger concept here? Any help is appreciated.
UI Components can only be accessed on the Main UI Thread. Therefore, as Multi-threading behaves, is creating a new thread on your system. By doing so, the Main UI's thread is not blocked. That being said, if you want to access the Controls, you need to call Invoke or BeginInvoke Methods, depending on the condition
sample:
Label1.BeginInvoke((Action) (() => { \\Multiple Line of codes here. }));
Also, check on these links:
Accessing UI Control from BackgroundWorker Thread
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
Solve a cross-threading Exception in WinForms
ADDED NOTE: while it is possible not to use Invoke Methods at some point, it is still better safe than sorry. some may eventually throw such exceptions on runtime. depending on the situation the program is running at
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
So, why am I able to change SOME attributes of labels in a thread other than the UI thread, but others are just off-limits?
Possibilities include:
Because someone forgot to add the cross-thread check
Changing that attribute doesn't happen to trigger some piece of code that required to UI thread.
Someone forgot to add the cross-thread check and changing that attribute triggers some piece of code that required to UI thread, but you got lucky and the program didn't crash this time.
Since 2 and 3 are indistinguishable 99% of the time, you may think that you are in situation 2 when really you are in situation 3. Which means random crashes that you can't reproduce.

Run a function in new thread c# windows application

I am working on application which calls a function populate() on window_load
This function execution takes about 1 minute to complete. I wanna make this function to be called in separate thread.
I am using following code
Thread thread = new Thread(PopulateAndDrawGraph);
thread.Start();
in this function at last line is
nodeXlControl1.DrawGraph(true);
Here exception occurs
The calling thread cannot access this object because a different thread owns it.
what wrong is actually occurs
You are only allowed to access UI controls from within the same thread that created the control, which is usally the UI thread itself.
You will need to change your code in a way that it becomes thread-aware.
Here is a fantastic article that was published in the MSDN magazine: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads, which will explain in great detail how you can do what you want to do.
The article is a little old, but the same principles still apply.
I guess that the newer C# language features - like the new async / await keywords - should make your task a little easier.
But keep in mind that the same old limitations for accessing UI controls still exist. There is no way around to understand the basics as descripbed in the article.
There are 2 ways to handle this, a correct way and another way:
1: If you want a good working solution then this should do the trick, ..
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;
}
}
msdn.microsoft.com/en-us/library/ms171728.aspx
2: but if you want a quick solution without guarantee that it will work properly, just set this variable:
Control.CheckForIllegalCrossThreadCalls = False
The problem is, that the any UI interaction has to occur in the main thread of the application. My advice is that you draw make the computation in the separate thread, but collect your results in an object. After the thread is finished you can extract the result from that object, and place it the nodeXlControl1.
Sadly i don't know the details about your objective, the sum of the solution is this.
If u can tell more details about this i may be able to further help you.
check this article (How to: Make Thread-Safe Calls) here out. It explains all the related items regarding cross thread calls. I also recommend that you look into the concept of "Task" in c#. Theres a very well written lib that helps you handle parallelism and similar concepts!
Since you are trying to access a UI element from a thread you need to call Invoke. So in PopulateAndDrawGraph you should have:
Invoke((Action)(() =>
{
nodeXlControl1.DrawGraph(true);
}));
You can't access the UI controls from any other thread except the thread which has created this control(you can access/change the UI control properties from the main UI thread only). Go through this link, i hope this makes you clear Cross-thread operation not valid
I have used BackgroundWorker class in c#.

Can you access UI elements from another thread? (get not set)

I see a lot of threads on google/here on UPDATING a UI element from another thread.
What if I want to just get the value of a checkbox?
Am I able to do this without having to do anything special?
Edit: It seems I have to take back what I wrote before. Tried the following:
Added a textbox called myTextBox and tried to retrieve the value of the Text property:
Thread t = new Thread(
o =>
{
Thread.Sleep(2000);
string value = myTextBox.Text;
Thread.Sleep(2000);
});
t.Start();
And it seems that the app (WPF) crashes after 2 seconds. Using the dispatcher works:
Thread t = new Thread(
o =>
{
Thread.Sleep(2000);
myTextBox.Dispatcher.BeginInvoke(
(Action)(() => { string value = myTextBox.Text; }));
Thread.Sleep(2000);
});
t.Start();
Thus, you still need to go through the dispatcher thread when reading values from GUI components, at least in WPF.
Second edit: This gets better. Apparently repeating the experiment for classic WinForms reveals that it works to read the Text property without using Invoke/BeginInvoke. Interestingly enough, it seems that also setting the property works fine (without invoke), although I'll wager it's not thread safe and the app doesn't complain for some reason.
Bottom line: It's a good idea in any case to use the dispatcher when interacting with GUI components from other threads, as it ensures the reads/writes are serialized to a single thread and so you have no thread-safety issues.
Can you access UI elements from another thread? (get not set)?
No.
Here is the deal. UI elements have very strict thread affinity requirements. This means you can only access the element from the thread hosting it. This includes all kinds of accesses including simple reads.1
It may work fine for simple property getters, but its perceived safeness would be an accidental result of the way that particular control was implemented. Since Control instances have thread affinity they could potentially use thread local storage techniques to save some of their state which, of course, would not be compatible with different thread. Or what if the value you are trying to read is in a half-baked state? There would be no way to synchronize access to that read since the write may be occurring inside code you have no control over. And still this is ignoring subtle memory barrier problems that may arise.
Again, if it appears to work then chalk it up as an accident. Accessing UI elements from a thread than the one hosting them is a recipe for disaster. Things may fail unpredictably and spectacularly.
1There are very few exceptions to this rule. Using the ISynchronizeInvoke methods is one such exception.
You could but strictly it wouldn't be thread safe.
For instance if the property Get code would consist of multiple operations, the UI thread could act in the mean time, halfway during the Get operation, causing unexpected results.
simply read the value as you normally do. Only to update the control, you need to switch to GUI thread.

C# GUI Application, Another class from another thread updating the UI

I've been researching on how to do this for about a week and I'm still not sure about the correct approach, in some examples I see the Thread class is used in others I see Invoke is used which has confused me a bid.
I have a GUI program in c# which contains a textBox which will be used to give information to the user.
The problem I'm facing is that I'm not sure how I can append text to textBox from another class which is running on another thread. If someone can show me a working example, it would help me greatly.
Best Regards!
Easy:
MainWindow.myInstance.Dispatcher.BeginInvoke(new Action(delegate() {MainWindow.myInstance.myTextBox.Text = "some text";});
WHERE MainWindow.myInstance is a public static variable set to the an instance of MainWindow (should be set in the constructor and will be null until an instance is constructed).
Ok thats a lot in one line let me go over it:
When you want to update a UI control you, as you say, have to do it from the UI thread. There is built in way to pass a delegate (a method) to the UI thread: the Dispatcher. I used MainWindow.myInstance which (as all UI components) contains reference to the Dispatcher - you could alternatively save a reference to the Dispatcher in your own variable:
Dispatcher uiDispatcher = MainWindow.myInstance.Dispatcher;
Once you have the Dispatcher you can either Invoke() of BeginInvoke() passing a delegate to be run on the UI thread. The only difference is Invoke() will only return once the delegate has been run (i.e. in your case the TextBox's Text has been set) whereas BeginInvoke() will return immediately so your other thread you are calling from can continue (the Dispatcher will run your delegate soon as it can which will probably be straight away anyway).
I passed an anonymous delegate above:
delegate() {myTextBox.Text = "some text";}
The bit between the {} is the method block. This is called anonymous because only one is created and it doesnt have a name - but I could instantiated a delegate:
Action myDelegate = new Action(UpdateTextMethod);
void UpdateTextMethod()
{
myTextBox.Text = "new text";
}
Then passed that:
uiDispatcher.Invoke(myDelegate);
I also used the Action class which is a built in delegate but you could have created your own - you can read up more about delegates on MSDN as this is going a bit off topic..
Sounds like you're using a background thread for processing, but want to keep the UI responsive? The BackgroundWorker sounds like the ticket:
The BackgroundWorker class allows you
to run an operation on a separate,
dedicated thread. Time-consuming
operations like downloads and database
transactions can cause your user
interface (UI) to seem as though it
has stopped responding while they are
running. When you want a responsive UI
and you are faced with long delays
associated with such operations, the
BackgroundWorker class provides a
convenient solution.
Just use BackgroundWorker for the same. It is simple and takes away the pain of managing threads of your own. for more, you can see: http://dotnetperls.com/backgroundworker

Strange cross-threading UI errors

I'm writing a WinForms app which has two modes: console or GUI. Three projects within the same solution, one for the console app, one for the UI forms and the third to hold the logic that the two interfaces will both connect too. The Console app runs absolutely smoothly.
A model which holds the user-selections, it has an IList<T> where T is a local object, Step, which implements INotifyPropertyChanged, so in the UI this is mounted on to a DataGridView. All is fine at runtime, the initial state of the objects is reflected on the screen.
Each of the Step objects is a task which is performed in turn; some of the properties will change, being reflected back to the IList and passed on to the DataGridView.
This action in the UI versions is done by creating a BackgroundWorker raising events back to the UI. The Step does it thing and generates a StepResult object which is an enumerated type indicating a result (e.g. Running, NotRun, OK, NotOK, Caveat) and a string to indicate a message (because the step ran but not quite as expected, i.e. with a Caveat). Normally the actions will involve a database interaction, but in debug mode I randomly generate a result.
If the message is null, there's never a problem, but if I generate a response like this:
StepResult returnvalue = new StepResult(stat, "completed with caveat")
I get an error saying that the DataGridView was being accessed from a thread other than the thread it was created on. (I'm passing this through a custom handler which should handle the invoking when required - maybe it doesn't?)
Then if I generate a unique response, e.g. using a random number r:
StepResult returnvalue = new StepResult(stat, r.ToString());
the actions succeed with no problem, the numbers are written cleanly to the DataGridView.
I'm baffled. I'm assuming it's somehow a string literal problem, but can anyone come up with a clearer explanation?
You've answered your own quesion:-
I get an error saying that the DataGridView was being accessed from a thread other than the thread it was created on.
WinForms insists that all actions performed on forms and controls are done in the context of the thread the form was created in. The reason for this is complex, but has a lot to do with the underlying Win32 API. For details, see the various entries on The Old New Thing blog.
What you need to do is use the InvokeRequired and Invoke methods to ensure that the controls are always accessed from the same thread (pseudocodeish):
object Form.SomeFunction (args)
{
if (InvokeRequired)
{
return Invoke (new delegate (Form.Somefunction), args);
}
else
{
return result_of_some_action;
}
}
Since you are doing UI binding via event subscription, you might find this helpful; it is an example I wrote a while ago that shows how to subclass BindingList<T> so that the notifications are marshalled to the UI thread automatically.
If there is no sync-context (i.e. console mode), then it reverts back to the simple direct invoke, so there is no overhead. When running in UI thread, note that this essentially uses Control.Invoke, which itself just runs the delegate directly if it is on the UI thread. So there is only any switch if the data is being edited from a non-UI thread - juts what we want ;-p
I found this article - "Updating IBindingList from different thread" - which pointed the finger of blame to the BindingList -
Because the BindingList is not setup for async operations, you must update the BindingList from the same thread that it was controlled on.
Explicitly passing the parent form as an ISynchronizeInvoke object and creating a wrapper for the BindingList<T> did the trick.

Categories