Can You Access UI Elements from Another Thread? (Get Not Set)

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

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.

Why cant a thread that is not the UI thread access the view?

Yes your right: You can't modify views on another thread for the sake of security (that's why it's called UI thread). It prevents you from leaving the UI data in an inconsistent state which might crash your application and would be really hard to debug. So the android API simply forbids that (and that's a good idea). That's a common UI pattern which you'll find in the most APIs.

You can update any view with post() or runOnUiThread():

anyView.post(new Runnable() {
public void run() {
// do update here
}
});

Why this pattern?
Synchronization is not for free. It impacts the performance. So it's easier to keep modifications to UI on the same thread.

If I could modify data from different threads what could happen?
For example: Thread A is changing the color of a view and thread B is reading the color at the same tim. Since multi-threading doesn't guarantee which instruction is executed first you might get unexpected results. The color is black (0|0|0) before, thread A wants to set white (255|255|255) and start with setting the red component to 255, thread B starts to read and gets the whole color before thread A had a chance to finish and got the color red (255|0|0) instead black.

This is a simple example which would impact a visual effect but if that happens for some really important data your application will crash horribly and such an error is so nasty and hard to debug. There's a lot to learn about multi-threading and maybe this java tutorial is a good starting point...

Is it possible to access a UI element from another thread if the thread doesn't modify that element?

It's definitely possible, however, it could lead to unexpected behaviour. Also, other thread-related bugs also need to be taken into consideration e.g. race conditions/deadlocks see Managed Threading Best Practises.

I would always stick to accessing the UI on the UI thread to be on the safe side.

How to access UI elements in multiple threads?

To do this, you need data binding. You may follow such a way,

First create your view model and update your properties:

public class MainViewModel : BindableBase
{
private string m_TextProgress;
public string TextProgress
{
get { return m_TextProgress; }
set { SetProperty(ref m_TextProgress, value); }
}

public void Run()
{
List<Variable> calculatedTestVariables = new List<Variable>();

for (int i = 0; i < 5000; i++)
{
Variable item = new Variable();

item.id = i;
calculatedTestVariables.Add(item);
}

Parallel.ForEach(calculatedTestVariables, variable =>
{
string idName = "id_" + variable.id;

//some calculations
int x = 1 + 2 + 3 + 4;

TextProgress = "" + variable.id + x;
});
}
}

Set your DataContext

<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>

<Grid>

<StackPanel>
<TextBlock
Width="120"
Height="30"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="{Binding TextProgress, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />

<Button
Width="120"
Height="30"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="Button_Click"
Content="Calculate" />
</StackPanel>
</Grid>

and call your Run method from GUI side

enter image description here

Accessing JavaFX UI elements from a different thread?

Whenever needing to update a UI element from a thread, you must get the JavaFX UI thread to do so. Attempting to update an element from a different thread may lead to an exception, but could also lead to some unexpected behaviour.

Fortunately, JavaFX includes a useful way to do this. Simply add this in the code that runs on your separate thread:

Platform.runLater(() -> {
msgArea.setText("Your text");
});

It is better to use a task or service, because those provide in-built means of updating UI elements. For example, a task allows one to call updateMessage("...") or updateProgress("..."), which updates a bound element without you even needing to call the UI thread.

Accessing ui element in other thread wpf

If you create a copy of the values you need, then you should be able to access them:

string username = login.Text, password = password.Text, ip = Ip.Text, port = Port.Text;
var task = new Task(() => TryConnect(username, password, ip, port));

And:

void TryConnect(string username, string password, string ip, string port) 
{
// ...
}

Copying the values locally like this means you don't need to access UI elements from your background thread.

Access UI element from non UI thread

Now I change both properties from a non UI thread like this (which is
not allowed, as far as I know it)

In general, changing property values is perfectly fine no matter what thread you are on. Problems and restrictions may come up when changing a property has an "interesting" side effect.

In this case both of the properties being changed produce interesting side effects, and the difference in observed behavior is due to these side effects being handled (from framework code, which you do not get to see directly) in different ways.

Why does solution 1 throw a NotSupportedExpection (not allowed to
change collection from non dispatcher thread) while solution 2 works
as desired?

When a binding source's properties are changed the WPF binding system responds by making the corresponding updates to the UI; however, when the changes are made from a background thread then the binding system's event handler will also run in the background thread and will not be able to update the UI directly. This is true for both the cases in question here.

The difference is that for "simple" property value changes the binding system automatically detects that it's not responding to the change on the UI thread and dispatches the UI changes to the correct thread using Dispatcher.Invoke, but when the observable collection is modified this dispatch does not happen automatically. The result is that the code that updates the UI runs on the background thread and an exception is thrown.

The solution

There are two things either one of which can solve this problem:

  1. Make the property change in the UI thread directly

    If the change is made on the UI thread then any PropertyChanged handlers will also run on the UI thread, so they will be free to make any UI changes they want. This solution can be enforced in your own code and will never result in a problem, but if no UI changes are required the extra work of dispatching to the UI thread will have been done for no benefit.

  2. Make sure the PropertyChanged handler dispatches any UI-related changes to the UI thread

    This solution has the benefit that it only dispatches work on demand, but also the drawback that the event handler (which might not be your own code) must be explicitly programmed to make the dispatch. .NET already does this for plain properties, but not for ObservableCollection. See How do I update an ObservableCollection via a worker thread? for more information.

Accessing UI controls from another thread

If your library's methods are called from the UI thread, you can capture the current context like this:

var UiContext = TaskScheduler.FromCurrentSynchronizationContext();

Then you can launch your thread, and when you want to run code back on the UI thread, create a task and schedule it to run on the captured context:

Task t = new Task(() => 
//update UI
);

t.Start(uiContext);

Or better yet, create a TaskFactory with the above scheduled and use it to create new tasks whenever you need to update the UI:

TaskFactory tf = new TaskFactory(uiContext);

tf.StartNew(() => { ... });

How to access separate thread generated WPF UI elements from the Dispatcher thread?

Found another guy with exactly the same problem - Printing the content of a DocumentViewer in a different UI thread. Just followed the same path. The code here was a real savior.

Now I'm NOT trying to access the secondary thread generated UI element from the Dispatcher thread, instead now the rest of the printing procedure is executed on the secondary thread. No cross-thread "VerifyAccess" of UI elements, and It's working smoothly. :)

How do I update the GUI from another thread?

For .NET 2.0, here's a nice bit of code I wrote that does exactly what you want, and works for any property on a Control:

private delegate void SetControlPropertyThreadSafeDelegate(
Control control,
string propertyName,
object propertyValue);

public static void SetControlPropertyThreadSafe(
Control control,
string propertyName,
object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate
(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(
propertyName,
BindingFlags.SetProperty,
null,
control,
new object[] { propertyValue });
}
}

Call it like this:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

If you're using .NET 3.0 or above, you could rewrite the above method as an extension method of the Control class, which would then simplify the call to:

myLabel.SetPropertyThreadSafe("Text", status);

UPDATE 05/10/2010:

For .NET 3.0 you should use this code:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
Control @this,
Expression<Func<TResult>> property,
TResult value);

public static void SetPropertyThreadSafe<TResult>(
this Control @this,
Expression<Func<TResult>> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;

if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(
propertyInfo.Name,
propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}

if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>
(SetPropertyThreadSafe),
new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(
propertyInfo.Name,
BindingFlags.SetProperty,
null,
@this,
new object[] { value });
}
}

which uses LINQ and lambda expressions to allow much cleaner, simpler and safer syntax:

// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);

Not only is the property name now checked at compile time, the property's type is as well, so it's impossible to (for example) assign a string value to a boolean property, and hence cause a runtime exception.

Unfortunately this doesn't stop anyone from doing stupid things such as passing in another Control's property and value, so the following will happily compile:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

Hence I added the runtime checks to ensure that the passed-in property does actually belong to the Control that the method's being called on. Not perfect, but still a lot better than the .NET 2.0 version.

If anyone has any further suggestions on how to improve this code for compile-time safety, please comment!



Related Topics



Leave a reply



Submit