(go to the last example if you want it to be fixed, etc.)
Observer themes and templates (such as data binding to winforms) are rarely good friends. You can try replacing the use of BindingList<T> code ThreadedBindingList<T> that I used in the previous answer , but this combination of threads and user interface is not an intentional use case for data binding winforms.
The list itself must support binding through list notification events ( IBindingList / IBindingListView ) if they come from the correct thread. ThreadedBindingList<T> tries to fix this by switching threads on your behalf. Please note: for this you need create a ThreadedBindingList<T> from the user interface stream after it synchronizes the context, that is, after it starts displaying forms.
To illustrate what listbox does respect list change notifications (when working with a single thread):
using System; using System.ComponentModel; using System.Windows.Forms; class Foo { public int Value { get; set; } public Foo(int value) { Value = value; } public override string ToString() { return Value.ToString(); } } static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); using(var form = new Form()) using (var lst = new ListBox()) using (var timer = new Timer()) { var data = new BindingList<Foo>(); form.Controls.Add(lst); lst.DataSource = data; timer.Interval = 1000; int i = 0; timer.Tick += delegate { data.Add(new Foo(i++)); }; lst.Dock = DockStyle.Fill; form.Shown += delegate { timer.Start(); }; Application.Run(form); } } }
and now with the addition of threading / ThreadedBindingList<T> (it does not work with the usual BindingList<T> ):
using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; class Foo { public int Value { get; set; } public Foo(int value) { Value = value; } public override string ToString() { return Value.ToString(); } } static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); using(var form = new Form()) using (var lst = new ListBox()) { form.Controls.Add(lst); lst.Dock = DockStyle.Fill; form.Shown += delegate { BindingList<Foo> data = new ThreadedBindingList<Foo>(); lst.DataSource = data; ThreadPool.QueueUserWorkItem(delegate { int i = 0; while (true) { data.Add(new Foo(i++)); Thread.Sleep(1000); } }); }; Application.Run(form); } } } public class ThreadedBindingList<T> : BindingList<T> { private readonly SynchronizationContext ctx; public ThreadedBindingList() { ctx = SynchronizationContext.Current; } protected override void OnAddingNew(AddingNewEventArgs e) { SynchronizationContext ctx = SynchronizationContext.Current; 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) { if (ctx == null) { BaseListChanged(e); } else { ctx.Send(delegate { BaseListChanged(e); }, null); } } void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } }
Marc gravell
source share