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

时间:2018-07-10 06:07:34

标签: c# wpf mvvm

我有一个名为class的{​​{1}}。我只需要更新当前调度程序线程以外的线程的属性Employee

Status

Viewmodel(class Employee { public string Name { get; set; } public string Status { get; set; } } 继承自ViewModelBase界面):

inotifypropertychanged

公共类DisplayViewModel:ViewModelBase     {

public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

当我尝试向可观察的集合中添加值时,它会抛出

异常

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

我应该如何进行?谢谢。

3 个答案:

答案 0 :(得分:2)

使用分派器在UI线程上调用Add方法:

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

或在UI线程上调用BindingOperations.EnableCollectionSynchronization方法以使集合可以在多个线程上使用:

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接口并为PropertyChanged属性引发Status事件在视图中进行设置。

答案 1 :(得分:1)

您的代码中有一个大问题。首先,ObservableCollection已经是一个通知更改的集合,因此您无需重新初始化它,只需调用Clear和Add / Insert。

第二,Employee类应为ViewModelBase:

class Employee: ViewModelBase
{
   private string _status;

   public string Name { get; set; }
   public string Status
   {
     get
     {
        return _status;
     }
     private set
     {
        _status=value;
       RaisePropertyChanged("Status");
     }
   }
}

这应该允许您更改DailyEmployees[0].Status="NewStatus";

LE:如果从其他线程更改数据时遇到问题,请检查以下链接:Using BindingOperations.EnableCollectionSynchronization

LLE:我将ViewModelBase类用于Employee,因为它已在代码示例中使用。原始答案意味着实现class Employee: INotifyPropertyChanged和实现所需的方法。

答案 2 :(得分:0)

尝试Application.Current.Dispatcher.BeginInvoke(() => /* update value */);

https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.begininvoke(v=vs.110).aspx

编辑:see this answer