Skip to main content
 首页 » 编程设计

c#之如何仅从 WPF MVVM 中的调度程序线程以外的线程更新可观察集合中的属性

2024年02月25日36powertoolsteam

我有一个名为Employee 的类。我只需要从当前调度程序线程以外的线程更新属性 Status:

class Employee 
{ 
    public string Name { get; set; } 
    public string Status { get; set; } 
} 

Viewmodel(ViewModelBase 实现了INotifyPropertyChanged 接口(interface)):

    public abstract class ViewModelBase : INotifyPropertyChanged 
    { 
        public event PropertyChangedEventHandler PropertyChanged; 
 
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
        { 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
        } 
    } 
 
 
public class DisplayViewModel : ViewModelBase 
{ 
 
    public static event EventHandler OnStatusChanged; 
 
    ObservableCollection<Employee> _dailyEmployees; 
 
    public ObservableCollection<Employee> DailyEmployees 
    { 
        get { return _dailyEmployees; } 
        set 
        { 
            _dailyEmployees = value; 
            OnPropertyChanged(); 
        } 
    } 
 
    public DisplayViewModel() 
    { 
        OnStatusChanged += DisplayViewModel_OnStatusChanged; 
    } 
 
    //invoked in other thread 
    private void DisplayViewModel_OnStatusChanged(object sender, EventArgs e) 
    { 
        var d = sender as Employee; 
        if (d == null) 
            return; 
        var em = DailyEmployees.FirstOrDefault(a => a.Name == d.Name); 
 
        if(em == null) 
        { 
            DailyEmployees.Add(em); 
        } 
        else 
        { 
            em.Status = d.Status; 
        } 
    } 
} 

当我尝试向可观察集合添加值时,它抛出异常

“这种类型的 CollectionView 不支持从与 Dispatcher 线程不同的线程更改其 SourceCollection”

我该如何进行?谢谢。

请您参考如下方法:

要么使用调度器在 UI 线程上调用 Add 方法:

Application.Current.Dispatcher.BeginInvoke(() => DailyEmployees.Add(em));  

或调用BindingOperations.EnableCollectionSynchronization UI 线程上的方法,使集合能够在多个线程上使用:

public class DisplayViewModel 
{ 
    private readonly ObservableCollection<Employee> _dailyEmployees = new ObservableCollection<Employee>(); 
    private readonly object _lock = new object(); 
 
    public ObservableCollection<Employee> DailyEmployees 
    { 
        get { return _dailyEmployees; } 
    } 
 
    public DisplayViewModel() 
    { 
        System.Windows.Data.BindingOperations.EnableCollectionSynchronization(_dailyEmployees, _lock); 
        OnStatusChanged += DisplayViewModel_OnStatusChanged; 
    } 
 
    //invoked in other thread 
    private void DisplayViewModel_OnStatusChanged(object sender, EventArgs e) 
    { 
        var d = sender as Employee; 
        if (d == null) 
            return; 
        var em = DailyEmployees.FirstOrDefault(a => a.Name == d.Name); 
 
        if (em == null) 
        { 
            DailyEmployees.Add(em); 
        } 
        else 
        { 
            em.Status = d.Status; 
        } 
    } 
} 

另请注意,Employee 类应实现 INotifyPropertyChanged 接口(interface)并为 Status 引发 PropertyChanged 事件属性,如果你希望这个的值在你设置它时反射(reflect)在 View 中。