20

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?

ephraim
  • 379
  • 1
  • 3
  • 15
Geesu
  • 5,928
  • 11
  • 43
  • 72
  • you can use delegates to do that – jorne May 04 '12 at 14:26
  • 4
    @CodeCaster Just trying and seeing what works doesn't generally get you thread-safe code. –  May 04 '12 at 14:27
  • 1
    @hvd: But it does satisfy all the questions posed _here_. – Austin Salonen May 04 '12 at 14:30
  • 1
    @hvd nevermind, I was too late to edit. I just meant to say I don't see much research in this question, like [searching](http://stackoverflow.com/questions/5516111/how-to-read-combobox-from-a-thread-other-than-the-thread-it-was-created-on). – CodeCaster May 04 '12 at 14:54
  • @CodeCaster That I can agree with. –  May 04 '12 at 14:56
  • 1
    Heh, actually I wish I could upvote this several times because it turned out to be an interesting question. As you can see from my experiments below, it works for WinForms, but not for WPF. Interesting. – Tudor May 04 '12 at 15:02
  • @CodeCaster I've just spent 20 minutes searching for this. There're plenty of posts about _setting_ UI controls, not many (that I can find) about simply reading them. Eg incrementing the Maximum values on a ProgressBar: `pgbMyProgressBar.ThreadSafeMaximum(pgbMyProgressBar.Maximum + 1)` (using custom extension method `ThreadSafeMaximum()`) So this was very useful to me, especially in a WinForms project. – SteveCinq Feb 18 '19 at 05:06

4 Answers4

20

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.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • "the App doesn't complain" -> Running in Release mode perhaps? – H H May 04 '12 at 16:01
  • @Henk Holterman: I've tried both Release and Debug and can't seem to notice any difference. – Tudor May 04 '12 at 16:26
  • @Geesu: Are you using WPF or windows forms? The `Dispatcher` property exists only in WPF. In windows forms you have to use directly `textBox.BeginInvoke(...)`. – Tudor May 25 '12 at 15:23
  • What's strange is now I'm getting "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." when I try to access it, and I know the form is created. Even though I can just print the value out w/o doing the BeginInvoke – Geesu May 25 '12 at 16:21
  • @Geesu: I suggest you post this problem in another question with the code you have so other people can also see it. – Tudor May 25 '12 at 17:07
  • Note: You should use Invoke vs. BeginInvoke: http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke – Geesu Jun 02 '12 at 16:55
8

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.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
2

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.

Peladao
  • 4,036
  • 1
  • 23
  • 43
0

simply read the value as you normally do. Only to update the control, you need to switch to GUI thread.

Romil Kumar Jain
  • 20,239
  • 9
  • 63
  • 92
  • 1
    Probably works with CheckBox.Checked 'cos it's a boolean property. As others have posted, getting textbox.Text is a lot more dodgy. – Martin James May 04 '12 at 14:50