MistyK
MistyK

Reputation: 6232

WPF and NotifyPropertyChanged from a different thread

I'm quite expierenced with WPF but there is one thing that bothers me. I'm using ReactiveUI for raising INotifyPropertyChanged events. I've got two places similiar to this:

 public UiModel UiModel
            {
                get { return _uiModel; }
                set { this.RaiseAndSetIfChanged(ref _uiModel, value); }
            }

    public void Run()
    {
      UiModel = GetUIModel();
    }

where Run() is called like this await Task.Run(()=>Run()); and UiModel is bound in XAML. So what basically should happen -> it should throw a cross-threading exception if Run() function is invoked on a different thread, shouldn't it? I've two places in the code and in one place it always throws an exception and in the second place exception is never thrown. I've compared thread IDs and in both places Run() is run not on UI thread. Why is this the case? In place where it doesn't throw exceptions I bind it to Dependency Property in UserControl and in the second place I bind it using normal binding

Exception being thrown: {"The calling thread cannot access this object because a different thread owns it."}

It looks like moreless I've found the answer for this specific situation. The exception is not thrown by UiModel and it's binding, but UiModel is also used in ReactiveUI validation. If I don't update it from UI it will try to update CanExecuteChanged from a different thread causing that exception. Nevertheless my question remains the same - why can I Update UI from a different thread without cross threading exception? I can't see dispatcher calls in ReactiveUI PropertyChanged invocation.

Upvotes: 4

Views: 2584

Answers (1)

Gusdor
Gusdor

Reputation: 14322

There are some annoying inconsistencies in this area. Here is a list of actions that you can and can't perform from a worker thread.

Raises cross thread exception:

  • Modifying a DependencyProperty on a Control
  • Raising the ICommand.CanExecuteChanged event
  • Raising the INotifyCollectionChanged.CollectionChanged event, commonly from an instance of ObservableCollection<T>

Does not throw an exception:

  • Raising the INotifyPropertyChanged.PropertyChanged event, commonly from view model binding

I'm sure there is a good reason why WPF doesn't invoke the first list automatically but I can't think what it might be.

My recommendations for handling service events are:

  1. Maintain a reference to a SynchronizationContext captures from the UI thread (probably the most use to you here).
  2. Use a mediator service to call application event handlers on the UI thread

Upvotes: 4

Related Questions