(переходите прямо к последнему примеру, если вы хотите, чтобы он был исправлен и т. Д.)
Потоки и шаблоны "наблюдателя" (такие как привязка данных на winforms) редко бывают хорошими друзьями.Вы можете попробовать заменить использование BindingList<T>
кодом ThreadedBindingList<T>
, который я использовал в предыдущем ответе , но эта комбинация потоков и пользовательского интерфейса не является преднамеренным вариантом использования привязки данных winforms.
Сам список должен поддерживать привязку через события уведомлений списка (IBindingList
/ IBindingListView
), если они поступают из правильного потока.ThreadedBindingList<T>
пытается исправить это путем переключения потоков от вашего имени.Обратите внимание, что для этого вам необходимо создать ThreadedBindingList<T>
из потока пользовательского интерфейса, после он имеет контекст синхронизации, т.е. после того, как он начал отображать формы.
Чтобы проиллюстрировать тот факт, что listbox учитывает уведомления об изменении списка (при работе с одним потоком):
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);
}
}
}
и теперь с добавлением потоковThreadedBindingList<T>
(не работает с обычным 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);
}
}