INotifyPropertyChanged和线程

我有一个实现INotifyPropertyChanged的基类:

 protected void OnNotifyChanged(string pName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(pName)); } } public event PropertyChangedEventHandler PropertyChanged; 

我有一个带有Latitude属性的派生类,如下所示:

 private double latitude; public double Latitude { get { return latitude; } set { latitude = value; OnNotifyChanged("Latitude"); } } 

我的派生类也有一个方法Fly来操纵Latitude

我还有一个Form,其TextBox绑定到我的派生类的Latitude

 txtLat.DataBindings.Clear(); txtLat.DataBindings.Add("Text", bindSrc, "Latitude"); 

一个线程用于启动Fly如下所示:

 Thread tFly = new Thread(f.Fly); tFly.IsBackground = true; tFly.Start(); 

Latitude更改时,会抛出exception:

DataBinding cannot find a row in the list that is suitable for all bindings.

这似乎是线程亲和力的一个奇怪问题。 最终,代码试图从非UI线程进行更新 – 我不清楚为什么它不仅仅是显示跨线程exception – 我不知道这是否实际上是一个包罗万象的exception处理程序。 如果我删除BindingSource (并直接绑定到有效的对象),你得到一个跨线程exception(我预期)。

就个人而言 ,我倾向于手动处理这个问题,即使用一种方法订阅事件,该方法对UI线程进行Invoke并手动更新Text 。 但是,我只是检查一些以前的跨线程绑定代码是否有帮助……


这是使用Invoke的示例:

 using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; class FlightUav : INotifyPropertyChanged { protected void OnNotifyChanged(string pName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(pName)); } public event PropertyChangedEventHandler PropertyChanged; private double _latitude; public double Latitude { get { return _latitude; } set { _latitude = value; OnNotifyChanged("Latitude"); } } public void Fly() { for (int i = 0; i < 100; i++) { Latitude++; Thread.Sleep(10); } } [STAThread] static void Main() { using (Form form = new Form()) { FlightUav currentlyControlledFlightUav = new FlightUav(); currentlyControlledFlightUav.PropertyChanged += delegate { // this should be in a *regular* method so that you can -= it when changing bindings... form.Invoke((MethodInvoker)delegate { form.Text = currentlyControlledFlightUav.Latitude.ToString(); }); }; using (Button btn = new Button()) { btn.Text = "Fly"; btn.Click += delegate { Thread tFly = new Thread(currentlyControlledFlightUav.Fly); tFly.IsBackground = true; tFly.Start(); }; form.Controls.Add(btn); Application.Run(form); } } } } 

以下是使用我的一些旧线程代码的(修改)版本的示例:

 using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; class FlightUav : INotifyPropertyChanged { protected void OnNotifyChanged(string pName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(pName)); } public event PropertyChangedEventHandler PropertyChanged; private double _latitude; public double Latitude { get { return _latitude; } set { _latitude = value; OnNotifyChanged("Latitude"); } } public void Fly() { for (int i = 0; i < 100; i++) { Latitude++; Thread.Sleep(10); } } [STAThread] static void Main() { using (Form form = new Form()) { FlightUav currentlyControlledFlightUav = new FlightUav(); BindingSource bindSrc = new BindingSource(); var list = new ThreadedBindingList(); list.Add(currentlyControlledFlightUav); bindSrc.DataSource = list; form.DataBindings.Clear(); form.DataBindings.Add("Text", list, "Latitude"); using (Button btn = new Button()) { btn.Text = "Fly"; btn.Click += delegate { Thread tFly = new Thread(currentlyControlledFlightUav.Fly); tFly.IsBackground = true; tFly.Start(); }; form.Controls.Add(btn); Application.Run(form); } } } } public class ThreadedBindingList : BindingList { 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); } }