我有一个实现INotifyPropertyChanged的自定义对象。我有这些对象的集合,其中集合基于BindingList 我已经为集合创建了一个绑定源,并设置了bindingsource和datagridview的数据源。
一切都很好,除了我需要从后台线程更新自定义对象的属性。当我这样做时,我收到以下错误:
BindingSource不能是自己的数据源。不要将DataSource和DataMember属性设置为引用BindingSource
的值
我发现以下帖子似乎有我的确切问题(和解决方案?)但我无法弄明白。
我在业务对象中为每个帖子创建并初始化了oper变量,然后将两个事件函数放入我的集合类中。这个编译正确,但在运行时毫无例外地挂起。
我看过许多帖子说要使用Invoke / Begin Invoke,但我没有调用UI上的任何函数,只是更新业务对象,所以我不确定在哪里调用invoke。
一个限制:我希望业务对象不知道谁在显示它(因为有多个消费者)所以将GUI引用发送到业务对象,以便我以后能够使用这些引用调用invoke不是选项。
答案 0 :(得分:14)
我在一个有效的论坛中找到了这个课程。只需使用它而不是BindingList
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
namespace Utility
{
public class ThreadedBindingList<T> : BindingList<T>
{
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);
}
}
}
答案 1 :(得分:1)
由于我花时间根据自己的需要设置样本格式,因此我可以将其作为可读参考发布在此处。除格式化外没有任何改变。
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);
}
}
}
答案 2 :(得分:0)
不完全是线程安全的,但如果你的后台线程修改对象属性的速度超过了它们的显示速度,那么对上述答案的这一小改动可能会产生很大的影响;
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);
}
}
欢迎任何建议,如果多次修改同一个对象,减少已发布的呼叫数量,并确保任何后续发送呼叫都将阻止,直到所有已发布的呼叫都已处理完毕。