如何从后台线程正确更新数据绑定数据网格视图

我有一个实现INotifyPropertyChanged的自定义对象。 我有这些对象的集合,其中集合基于BindingList我已经为集合创建了一个绑定源,并设置了bindingsource和datagridview的数据源。

一切都很好,除了我需要从后台线程更新自定义对象的属性。 当我这样做时,我收到以下错误:

BindingSource不能是自己的数据源。 不要将DataSource和DataMember属性设置为引用BindingSource的值

我发现以下post似乎有我的确切问题(和解决方案?)但我无法弄明白。

http://social.msdn.microsoft.com/forums/en-US/winformsdatacontrols/thread/3566f7c7-eb47-422e-ab09-9549a18da360/

我在业务对象中创建并初始化了每个post的oper变量,然后将两个事件函数放入我的集合类中。 这个编译正确,但运行时毫无例外地挂起。

我看过许多post说使用Invoke / Begin Invoke,但是我没有调用UI上的任何函数,只是更新业务对象,所以我不确定在哪里调用invoke。

一个限制:我希望业务对象不知道谁在显示它(因为有多个消费者),因此将GUI引用发送到业务对象,以便我以后能够使用这些引用调用invoke不是一个选项。

我在一个有效的论坛中找到了这个课程。 只需使用它而不是BindingList

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Threading; namespace Utility { public class ThreadedBindingList : BindingList { SynchronizationContext ctx = SynchronizationContext.Current; protected override void OnAddingNew(AddingNewEventArgs e) { if (ctx == null) { BaseAddingNew(e); } else { ctx.Send(delegate { BaseAddingNew(e); }, null); } } void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); } protected override void OnListChanged(ListChangedEventArgs e) { // SynchronizationContext ctx = SynchronizationContext.Current; if (ctx == null) { BaseListChanged(e); } else { ctx.Send(delegate { BaseListChanged(e); }, null); } } void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } } } 

由于我花时间根据自己的需要格式化样本,因此我可以将其作为可读参考发布在此处。 除格式化外没有任何改变。

 using System.ComponentModel; using System.Threading; namespace Utility { public class ThreadedBindingList : BindingList { SynchronizationContext ctx = SynchronizationContext.Current; protected override void OnAddingNew(AddingNewEventArgs e) { if (ctx == null) { BaseAddingNew(e); } else { ctx.Send(delegate { BaseAddingNew(e); }, null); } } void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); } protected override void OnListChanged(ListChangedEventArgs e) { // SynchronizationContext ctx = SynchronizationContext.Current; if (ctx == null) { BaseListChanged(e); } else { ctx.Send(delegate { BaseListChanged(e); }, null); } } void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } } } 

不完全是线程安全的,但是如果你的后台线程修改对象属性的速度比它们显示得快,那么对上述答案的这个小改动可能会产生很大的影响。

 protected override void OnListChanged(ListChangedEventArgs e) { // SynchronizationContext ctx = SynchronizationContext.Current; if (ctx == null) { BaseListChanged(e); } else if(e.ListChangedType == ListChangedType.ItemChanged) { ctx.Post(delegate { BaseListChanged(e); }, null); } else { ctx.Send(delegate { BaseListChanged(e); }, null); } } 

如果同一对象被多次修改,欢迎任何减少已发布呼叫数量的建议,并确保任何后续发送呼叫都将阻止,直到所有已发布呼叫都已处理完毕。