INotifyPropertyChanged导致跨线程错误

这是我的观点:

我有一个绑定到BindingList的GridControl。 起初我正在做的是创建一个工作线程并直接访问BindingList,但这是一个“检测到跨线程操作”,所以我按照这里的指南:

http://www.devexpress.com/Support/Center/p/AK2981.aspx

通过将原始BindingList克隆到工作线程并更改那个,我得到了预期的效果。 但是,我最近将INotifyPropertyChanged实现到了绑定到BindingList的对象中,然后我又开始收到错误。

我的猜测是GridView仍在从对象中侦听INotifyPropertyChanged。

我怎样才能解决这个问题?

我的课:

public class Proxy : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } 

如果您从UI线程外部(例如从工作线程)操作UI,则需要重新加入UI线程。 您可以通过在UI控件上调用Invoke来完成此操作。 您可以使用InvokeRequired测试是否需InvokeRequired

通常使用的模式是:

 public void ChangeText(string text) { if(this.InvokeRequired) { this.Invoke(new Action(() => ChangeText(text))); } else { label.Text = text; } } 

在您的情况下,由于INotifyPropertyChanged正在操纵UI,因此您需要确保始终在UI线程上修改实体(使用上述技术),或使用通用异步INotifyPropertyChanged帮助程序 。 这是绑定项目的包装器。 它使用上述技术确保在UI线程上触发ChangeProperty事件。

这是一个非常粗略的Entity类代理示例。 这可确保属性更改事件重新加入UI线程,并使实体本身保持不变。 显然,您可能希望使用DynamicObject更具体地实现它。

 public class NotificationHelper : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private readonly ISynchronizeInvoke invokeDelegate; private readonly Entity entity; public NotificationHelper(ISynchronizeInvoke invokeDelegate, Entity entity) { this.invokeDelegate = invokeDelegate; this.entity = entity; entity.PropertyChanged += OnPropertyChanged; } public string Name { get { return entity.Name; } } private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { if (PropertyChanged != null) { if (invokeDelegate.InvokeRequired) { invokeDelegate.Invoke(new PropertyChangedEventHandler(OnPropertyChanged), new[] { sender, e }); return; } PropertyChanged(this, e); } } } 

我对TheGateKeeper的最终解决方案采取了类似的方法。 但是,我绑定了许多不同的对象。 所以我需要一些更通用的东西。 解决方案是创建一个也实现ICustomTypeDescriptor的包装器。 通过这种方式,我不需要为UI中可以显示的所有内容创建包装器属性。

 public class SynchronizedNotifyPropertyChanged : INotifyPropertyChanged, ICustomTypeDescriptor where T : INotifyPropertyChanged { private readonly T _source; private readonly ISynchronizeInvoke _syncObject; public SynchronizedNotifyPropertyChanged(T source, ISynchronizeInvoke syncObject) { _source = source; _syncObject = syncObject; _source.PropertyChanged += (sender, args) => OnPropertyChanged(args.PropertyName); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged == null) return; var handler = PropertyChanged; _syncObject.BeginInvoke(handler, new object[] { this, new PropertyChangedEventArgs(propertyName) }); } public T Source { get { return _source; }} #region ICustomTypeDescriptor public AttributeCollection GetAttributes() { return new AttributeCollection(null); } public string GetClassName() { return TypeDescriptor.GetClassName(typeof(T)); } public string GetComponentName() { return TypeDescriptor.GetComponentName(typeof (T)); } public TypeConverter GetConverter() { return TypeDescriptor.GetConverter(typeof (T)); } public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(typeof (T)); } public PropertyDescriptor GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(typeof(T)); } public object GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(typeof (T), editorBaseType); } public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(typeof(T)); } public EventDescriptorCollection GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(typeof (T), attributes); } public PropertyDescriptorCollection GetProperties() { return TypeDescriptor.GetProperties(typeof (T)); } public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { return TypeDescriptor.GetProperties(typeof(T), attributes); } public object GetPropertyOwner(PropertyDescriptor pd) { return _source; } #endregion ICustomTypeDescriptor } 

然后在Ui中,我使用类似的东西绑定到这个包装器:

  private void CreateBindings() { if (_model == null) return; var threadSafeModel = new SynchronizedNotifyPropertyChanged(_model, this); directiveLabel.DataBindings.Add("Text", threadSafeModel, "DirectiveText", false, DataSourceUpdateMode.OnPropertyChanged); } 

MyViewModel具有“DirectiveText”属性并实现了INotifyPropertyChanged,没有特别考虑线程或视图类。

万一有人遇到同样的问题……我设法在几个小时后修复它。 这是我做的:

基本上问题是实现INotifyPropertyChanged的对象生活在一个工作线程中,这会在访问UI线程时引起问题。

所以我所做的是将对需要更新的对象的引用传递给INotifyPropertyChanged对象,然后对其使用invoke。

这是它的样子:

  public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { //If the Proxy object is living in a non-UI thread, use invoke if (c != null) { c.BeginInvoke(new Action(() => handler(this, new PropertyChangedEventArgs(name)))); } //Otherwise update directly else { handler(this, new PropertyChangedEventArgs(name)); } } } //Use this to reference the object on the UI thread when there is need to public Control C { set { c = value; } } 

从线程来看,我所做的只是:

  prox.c = this; //Logic here prox.c = null; 

希望这有助于某人!

我将BindingList子类化,因此我可以检查所需的Invoke 。 这样我的业务对象就没有对UI的引用。

 public class InvokingBindingList : BindingList { public InvokingBindingList(IList list, Control control = null) : base(list) { this.Control = control; } public InvokingBindingList(Control control = null) { this.Control = control; } public Control Control { get; set; } protected override void OnListChanged(ListChangedEventArgs e) { if (Control?.InvokeRequired == true) Control.Invoke(new Action(() => base.OnListChanged(e))); else base.OnListChanged(e); } }