3

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.

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152
  • If *some* properties have problems, *post the code and exception*. Both the ViewModel code *and* the data binding code/XAML. Post the *full* exception, including its call stack. You can get it with `Exception.ToString()`. *None* of them should have any problems, since its' the *UI* thread that reads the bound properties data in response to `INotifyPropertyChanged`. How did that call end up on a different thread? Impossible to answer without the code and *call stack* – Panagiotis Kanavos Mar 24 '17 at 12:17
  • Why does the VM use events *at all*? Who calls `LongRunningProcessProgressChanged` and why? Why don't you use a `LongRunningProcessProgres(string)` method? Events introduce tight coupling, which `IProgress< T>` removes. In this case though there are no events to handle, you just want to update a property from inside the same VM – Panagiotis Kanavos Mar 24 '17 at 12:21

2 Answers2

2

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.

Community
  • 1
  • 1
3615
  • 3,787
  • 3
  • 20
  • 35
  • But the UI does update. If it was failing silently I would expect this not to happen. – Andrew Stephens Mar 24 '17 at 11:01
  • There never was. Not even in 1995, when the first multithreaded version of Windows came out. You *already* use `await`, why are you trying to modify the UI from *inside* the task? If you want to send messages to the UI thread, use `IProgress< T>` – Panagiotis Kanavos Mar 24 '17 at 11:05
  • @AndrewStephens please check if updated answer is more useful now. – 3615 Mar 27 '17 at 07:25
-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
            }));
  • There's absolutely no reason to use `BeginInvoke` when you have `await`. Simply decouple UI updating from background processing. In WPF, you shouldn't modify the UI directly anyway, only the ViewModel's properties. You can use `IProgress< T>` to send messages to the views from any thread anyway – Panagiotis Kanavos Mar 24 '17 at 11:04