是的。它是线程安全的。 INotifyPropertyChanged 总是从其他线程编组到 UI 线程。
无需将 PropertyChanged 从另一个线程分派到 UI 线程,因为 PropertyChanged 事件会自动编组到 UI 调度程序。
正如a MSDN article 所说:
请注意,在 WPF 中,情况有所不同,代码如图 5 所示
即使 Status 属性数据绑定到 TextBlock 也可以工作。这个
是因为 WPF 自动将 PropertyChanged 事件调度到
主线程,与所有其他 XAML 框架不同。在所有其他
框架,需要一个调度解决方案。
但是,它仅适用于标量属性的更改通知(即PropertyChanged 事件)。集合更改通知(INotifyCollectionChanged.CollectionChanged 事件)不能那样工作,它们必须在 UI 线程上手动引发。也就是说,当使用INotifyCollectionChanged(例如使用ObservableCollection)时,这些更改不会封送至UI 线程。这意味着如果您从非 UI 线程修改集合,您将接受异常。例如,有一些 ViewModel 我们在 ViewModel 类中,我们不使用 Dispatcher 来更新 UI。所以我建议你使用 David Rickard 的方法:
public static class DispatchService
{
public static void Invoke(Action action)
{
Dispatcher dispatchObject = Application.Current.Dispatcher;
if (dispatchObject == null || dispatchObject.CheckAccess())
{
action();
}
else
{
dispatchObject.Invoke(action);
}
}
}
和:
DispatchService.Invoke(() =>
{
this.MyCollection.Add("new value");
});
David Rickard article at msdn blog.
更新:
是的,article 使用 MVVMLight 框架。但是,MVVM Light 使用Dispatcher 将标量属性编组到 UI 线程是不正确的。从MVVM Light的ViewModelBase类的源代码可以看出there is no marshal between threads to update scalar property。请参阅RaisePropertuChanged() 方法。
为了消除对调度标量属性的任何疑问,我做了一个测试:
XAML:
<TextBlock Text="{Binding Number}" FontSize="188" Foreground="Red" />
视图模型:
public int Number { get; set; }
private void UpdateNumber()
{
Task.Run(() =>
{
System.Timers.Timer timer = new System.Timers.Timer(250);
timer.Elapsed += (sender, eventArgs) =>
{
Number++;
OnPropertyChanged("Number");//No exceptions, no errors
};
timer.Enabled = true;
});
}
更新 1:
毫无疑问,INotifyProperyChanged 事件由 WPF 自动分派到 UI 线程。我相信a MSDN article 和您在问题中显示的link :)。
注意这一点:这是因为 WPF 会自动将 PropertyChanged 事件分派到主线程,这与所有其他 XAML 框架不同。