Andrew Stephens
Andrew Stephens

Reputation: 10203

Updating the UI from events raised by a background thread

My WPF application launches long running functions on background threads, typically via a button click/command, e.g.

StartCommand = new RelayCommand(async o => await StartAsync(), o => true);

...

private async Task StartAsync()
{
    await Task.Run(() => LongRunningFunction());
}

These long running functions raise various events to report progress to the user, update UI values, etc. The view-model handles such events and updates bound properties, e.g.:

private void LongRunningProcessProgressChanged(object sender, ProgressEventArgs e)
{
    StatusMessageText = string.Format("Progress {0}%", e.Progress);
}

Most of the time this works fine but occasionally I get the usual exception about updating the UI from a background thread ("The calling thread cannot access this object because a different thread owns it")), so I have to wrap the code in a Dispatcher.Invoke(...). I haven't really spotted a pattern as to when I do or don't have to do this, so can anyone shed some light on it?

To be honest I'm surprised that the above code works at all. A breakpoint confirms that these event handlers run on a worker thread, not the UI thread, so why don't I see the exception all the time? Is it something to do with the type of property being updated?

Edit People are proposing answers to a problem which I am already aware of. Reading my question again I might have confused readers with the part "Most of the time this works fine but occasionally I get the usual exception". I didn't mean that it's an intermittent problem - what I really meant was that some of my VMs don't have a problem updating bound properties within their respective progress event handler, while other VMs do. I suspect it has something to do with the property type, e.g. updating a string property works but not an ObservableCollection.

My question was more of a curiosity thing, i.e. why some bound properties are okay with being updated from a b/g thread while others aren't.

Upvotes: 4

Views: 1814

Answers (2)

3615
3615

Reputation: 3885

Your code should always throw exceptions when you are updating UI from background threads. You don't always see the exceptions, because exceptions happens in background threads and remains unobserved. For such exceptions

.. TPL needs to backstop these exceptions and hold on to them until such time that they can be thrown again when the consuming code accesses the task.

So you simply don't see the exceptions immediatly after they are thrown from background task.


Update

This answer sheds some light on common scenarios when accessing a control from another thread. About the issue you described, it'a actually depending on type of property you are binding to as stated at MSDN forum:

Data binding to primitive types is basically type safe, since the binding mechanism, internally, uses the dispatcher to marshal back to the UI thread for you.

However, you need to take care with collections. For example, ObservableCollection will not handle this, so you need to make changes to the collection on the UI thread.

In this blog post you can find some more details.

Upvotes: 2

Paul Ra Je
Paul Ra Je

Reputation: 1

The problem that you are facing is , you are trying to update an UI element which are at in the different thread and you could try this

 App.Current.Dispatcher.Invoke(new Action(() =>
            {
               // your code
            }));

Upvotes: -1

Related Questions