我有一个绑定到BindingList的ListBox. BindingList是在第三方应用程序引发事件时构建的.我可以看到绑定列表正确绑定…但没有任何内容进入ListBox.我使用了与我自己的一些自定义类型完全相同的逻辑,它通常工作得很好.
表格类
private Facade.ControlFacade _controlFacade; public UavControlForm() { InitializeComponent(); _controlFacade = new UavController.Facade.ControlFacade(); UpdateEntityListBox(); } private void UpdateEntityListBox() { lsbEntities.DataSource = _controlFacade.GetEntityTally(); lsbEntities.DisplayMember = "InstanceName"; }
门面课
private Scenario _scenario; public ControlFacade() { _scenario = new Scenario(); } public BindingList<AgStkObject> GetEntityTally() { BindingList<AgStkObject> entityTally = _scenario.EntityTally; return entityTally; }
场景类
private static BindingList<IAgStkObject> _entityTally = new BindingList<AgStkObject>(); public Scenario() { if (UtilStk.CheckThatStkIsAvailable()) { UtilStk.StkRoot.OnStkObjectAdded += new IAgStkObjectRootEvents_OnStkObjectAddedEventHandler(TallyScenarioObjects); UtilStk.StkRoot.OnStkObjectDeleted += new IAgStkObjectRootEvents_OnStkObjectDeletedEventHandler(TallyScenarioObjects); } } private void TallyScenarioObjects(object sender) { List<AgStkObject> tallyOfStkObjects = UtilStk.GetRunningTallyOfAllStkObjects(); List<string> stkObjectNames = UtilStk.GetInstanceNamesOfStkObjects(tallyOfStkObjects); foreach (string stkObjectName in stkObjectNames) { if (!SearchFlightUavTallyByName(stkObjectName)) { if (!SearchLoiterUavTallyByName(stkObjectName)) { if (!SearchEntityTallyByName(stkObjectName)) { int i = stkObjectNames.IndexOf(stkObjectName); _entityTally.Add(tallyOfStkObjects[i]); } } } } }
我可以看到来自第三方应用程序的事件触发 – 这会根据需要向_entityList添加一个实体,但是没有任何内容添加到lsbEntities – 为什么?
解决方法
(如果你想看到它固定等,请跳到最后一个例子)
线程和“观察者”模式(例如winforms上的数据绑定)很少是好朋友.您可以尝试替换BindingList< T>与ThreadedBindingList< T>的使用我在previous answer上使用的代码 – 但这种线程和UI的组合并不是winforms数据绑定的故意用例.
列表框本身应该支持通过列表通知事件(IBindingList / IBindingListView)进行绑定,只要它们到达形成正确的线程即可. ThreadedBindingList< T>尝试通过代表您的线程切换来解决此问题.请注意,要使其工作,您必须创建ThreadedBindingList< T>来自UI线程,在它具有同步上下文之后,即在它开始显示表单之后.
为了说明列表框确实尊重列表更改通知(处理单个线程时):
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); } }