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

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.

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.

Xamarin.iOS main and background threads not playing nice

I will try and keep this short and sweet.
I have this code which is a result of a button being pressed (so its on the main UI thread)
MessageCenter.Init();
the above method does this (as well as other things)
NS = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.Parse("00:00:30"), delegate
{
NSObject.InvokeInBackground(() =>
{
HandleElapsed();
});
});
HandleElapsed(); obtains an exclusive lock on an object using the Monitor.Enter(obj) method. mean while the main ui thread also may need to obtain an exclusive lock. (the lock is in place to ensure sqlite data integrity)
when the main UI encounters a lock on the object (i.e its already locked) the entire app just halts (including the background thread)
I should mention the UI may need to get a lock when its told to change its content. HandleElapsed(); will ask the main UI thread to change its content.
NSNotificationCenter.DefaultCenter.PostNotificationName("ChangeDetail", new NSString("News"));
please note the change in contents is completed on the main thread
UIApplication.SharedApplication.InvokeOnMainThread();
its seems when the main ui is stuck on a lock... its also doesn't allow the background thread to continue thus the background thread is not able to move on a call to Monitor.Exit();
am i missing something?
Solved it.
I kept the database open throughout. and simply applied the locking mechanism
lock(SQLight.Connection)
{
...
}
this means all my threads uses the same connection but each thread can only interact with the data in turn.
This seems to have achieved what I wanted

Should I Invoke or change my Get/Set Method? "InvalidOperationException" 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".

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#.

Keep U.I Updated using Threads

I have a lot of long-running activities and think that spawning this activity off to another thread will be a good way to have my U.I be able to update to show its current status.
However, when I use the following:
Thread t = new Thread(() =>
{
/* do magic here */
});
Nothing inside the foreach loop that's inside the thread gets done. But, when I don't use a thread, the work does get done, so I know it's not a problem with the loop.
Any suggestions?
You may also want to take a look at BackgroundWorker as it nicely encapsulates everything.
Are you even starting the thread?
newThread.Start();
In the sample you provide you merely declare it.
Also bear in mind that if you're using WinForms, you won't be able to update the UI directly from any thread other than the one that created it; for example, modifying a progress bar or label control from within your foreach loop.
You need to start the tread, t.Start();
Creating the instance just creates an managed wrapper for a thread. Calling Start will set things in motion and eventually make your code run on a separate thread.
Probably you haven't started the thread, so its not running yet.
However, in your case its usually better to use BackgroundWorker class, this will create the thread for you and provide thread-safe way to update the UI with the progress of the threads work.

Categories