这是我的观点:
我有一个绑定到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));
}
}
答案 0 :(得分:10)
如果您从UI线程外部(例如来自工作线程)操作UI,则需要重新加入UI线程。您可以通过在UI控件上调用Invoke
来执行此操作。您可以使用InvokeRequired
来测试是否需要这样做。
通常使用的模式是:
public void ChangeText(string text)
{
if(this.InvokeRequired)
{
this.Invoke(new Action(() => ChangeText(text)));
}
else
{
label.Text = text;
}
}
在您的情况下,由于INotifyPropertyChanged
,UI正在被操纵,因此您需要确保始终在UI线程上修改您的实体(使用上述技术),或使用{{ 3}}。这是绑定项目的包装器。它使用上述技术来确保在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);
}
}
}
答案 1 :(得分:2)
我对TheGateKeeper的最终解决方案采取了类似的方法。但是我绑定了许多不同的对象。所以我需要一些更通用的东西。解决方案是创建一个也实现了ICustomTypeDescriptor的包装器。通过这种方式,我不需要为可以在UI中显示的所有内容创建包装器属性。
public class SynchronizedNotifyPropertyChanged<T> : 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<MyViewModel>(_model, this);
directiveLabel.DataBindings.Add("Text", threadSafeModel, "DirectiveText", false, DataSourceUpdateMode.OnPropertyChanged);
}
MyViewModel具有“DirectiveText”属性并实现了INotifyPropertyChanged,没有特别考虑线程或视图类。
答案 2 :(得分:1)
以防万一有人遇到同样的问题......我设法在几个小时后修复它。这是我做的:
基本上问题是实现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;
希望这有助于某人!!
答案 3 :(得分:0)
我已将return 0
子类化,因此我可以检查所需的BindingList
。这样我的业务对象就没有对UI的引用。
Invoke