通过单独的任务更新BindingSource中的元素

我有一个class,比如说人,有一个Id和一个名字。 该类正确实现了INotifyPropertyChanged

另外:有人要求上课人。

我真正的问题是一个更复杂的课程,我把它简化为一个相当简单的POCO,以确定它不是因为我的课程。

本来:

public class Person { public int Id {get; set;} public string Name {get; set;} } 

对于更新,它需要实现INofityChanged。 完整的代码就在这个问题的最后

StackOverflow:如何正确实现INotifyPropertyChanged

  • 我有一个System.Windows.Forms.Form
  • 此表单有一个BindingSource。
  • 绑定源的DataSource属性设置为我的类Person
  • 我有一个绑定到BindingSource的DataGridView
  • 我已经为绑定源添加了几个Person实例
  • 添加的人员被正确显示。
  • 如果我以编程方式更改bindingsource中的Person,则会正确显示更改的值。

到现在为止还挺好。 如果在单独的线程中更改Person,则会出现问题。

我经常收到带有消息的InvalidOperationException

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

我想这与更新是在一个等待的异步任务中完成的事实有关。 我知道在更新用户界面项之前,您应该检查InvokeRequired是否相应地采取行动。

 private void OnGuiItemChanged() { if (this.InvokeRequired) { this.Invoke(new MethodInvoker(() => { OnGuiItemChanged(); })); } else { ... // update Gui Item } } 

但是,使用绑定源时,更改将在bindingsource内处理。 所以我无法检查InvokeRequired

那么如何更新也存储在非UI线程中的绑定源中的项目?

按要求:类Person的实现和我的表单的一些代码

 class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private int id = 0; private string name = null; public int Id { get { return this.id; } set { this.SetField(ref this.id, value); } } public string Name { get { return this.name; } set { this.SetField(ref this.name, value); } } protected void SetField(ref T field, T value, [CallerMemberName] string propertyName = null) { if (!EqualityComparer.Default.Equals(field, value)) { field = value; RaiseEventPropertyChanged(propertyName); } } private void RaiseEventPropertyChanged(string propertyName) { var tmpEvent = this.PropertyChanged; if (tmpEvent != null) { tmpEvent(this, new PropertyChangedEventArgs(propertyName)); } } } 

表格的一些代码:

 private void Form1_Load(object sender, EventArgs e) { for (int i = 0; i  ChangePersonsAsync(this.cancellationTokenSource.Token)); } private async Task ChangePersonsAsync(CancellationToken token) { try { while (!token.IsCancellationRequested) { foreach (var p in this.bindingSource1) { Person person = (Person)p; person.Id = -person.Id; } await Task.Delay(TimeSpan.FromSeconds(0.01), token); } } catch (TaskCanceledException) { } } 

正如您所提到的,更改是在BindingSource类中处理的,因此我看到的最简单方法是将其替换为以下内容

 public class SyncBindingSource : BindingSource { private SynchronizationContext syncContext; public SyncBindingSource() { syncContext = SynchronizationContext.Current; } protected override void OnListChanged(ListChangedEventArgs e) { if (syncContext != null) syncContext.Send(_ => base.OnListChanged(e), null); else base.OnListChanged(e); } } 

只要确保它是在UI线程上创建的。