BindingList ListChanged事件

我有一个类的BindingList 设置为BindingSource的DataSource属性,该属性又设置为DataGridView的DataSource属性。

1.我的理解是,对列表的任何添加都将触发ListChanged事件,该事件将通过BindingSource传播,然后传播到DataGridView,DataGridView将更新自身以显示更改。 这将发生,因为事件已自动连接。 (是?)

当所有工作都在UI线程上完成时,这一切都很好,但是当从非UI线程创建和更改列表时,最终在更新网格时会发生跨线程exception。 我能理解为什么会这样,但是没有办法解决这个问题……

2.我很难理解,我应该在哪里最好拦截ListChanged事件来尝试将内容整理到UI线程中? 我猜我需要一个UI线程的引用以某种方式帮助做到这一点?

我已经阅读了很多这方面的post/文章,但我很难挣扎,因为我不完全理解这里的工作机制。

一旦他们在列表中,我将永远不会更改任何项目,只添加它们,并最初清除列表。

(我使用的是.NET 2.0)

您可以扩展BindingList以使用ISynchronizeInvoke(由System.Windows.Forms.Control实现)将事件调用封送到UI线程。

然后,您需要做的就是使用新的列表类型,并对所有列表进行排序。

public partial class Form1 : System.Windows.Forms.Form { SyncList _List; public Form1() { InitializeComponent(); _List = new SyncList(this); } } public class SyncList : System.ComponentModel.BindingList { private System.ComponentModel.ISynchronizeInvoke _SyncObject; private System.Action _FireEventAction; public SyncList() : this(null) { } public SyncList(System.ComponentModel.ISynchronizeInvoke syncObject) { _SyncObject = syncObject; _FireEventAction = FireEvent; } protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args) { if(_SyncObject == null) { FireEvent(args); } else { _SyncObject.Invoke(_FireEventAction, new object[] {args}); } } private void FireEvent(System.ComponentModel.ListChangedEventArgs args) { base.OnListChanged(args); } } 
  1. 这种观点足够公平。 在封面下,其他对象(如CurrencyManager和Binding)确保在基础数据源更改时更新控件。

  2. 将项添加到数据绑定BindingList会触发一系列事件,最终尝试更新DataGridView。 由于UI只能从UI线程更新,因此您应该通过Control.Invoke从UI线程向BindingList添加项。

我组装了一个快速示例,创建一个带有DataGridView,BindingSource和Button的Form。

该按钮旋转另一个线程,该线程模拟获取包含在BindingList中的新项目。

包含本身通过Control.Invoke在UI线程中完成。

 public partial class BindingListChangedForm : Form { BindingList people = new BindingList(); Action personAdder; public BindingListChangedForm() { InitializeComponent(); this.dataGridView1.AutoGenerateColumns = true; this.bindingSource1.DataSource = this.people; this.personAdder = this.PersonAdder; } private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(this.GotANewPersononBackgroundThread); t.Start(); } // runs on the background thread. private void GotANewPersononBackgroundThread() { Person person = new Person { Id = 1, Name = "Foo" }; //Invokes the delegate on the UI thread. this.Invoke(this.personAdder, person); } //Called on the UI thread. void PersonAdder(Person person) { this.people.Add(person); } } public class Person { public int Id { get; set; } public string Name { get; set; } }