INotifyPropertyChanged and Threading

I have a base class that implements INotifyPropertyChanged :

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

I have a derived class with the Latitude property as follows:

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

My derived class also has a Fly method that controls Latitude .

I also have a TextBox form related to the Latitude my derived class:

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

The thread is used to launch Fly as follows:

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

When Latitude changes, an exception is thrown:

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

+6
multithreading c # winforms binding
source share
1 answer

This seems like a weird thread binding problem. Ultimately, the code tries to update from a thread other than the UI β€” I don’t understand why it doesn’t just display the cross-thread exception, however - I wonder if this is really an exception handler for everyone. If I remove the BindingSource (and bind directly to the object that is valid), you do get a cross-thread exception (which I expected).

Personally, I would be inclined to handle this manually, that is, to subscribe to the event using a method that Invoke UI thread and update the Text manually. However, I am just checking if any previous cross-threading code can help ...


Here is an example using 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); } } } } 

Here is an example of using a (modified) version of some old cleaning code:

 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<FlightUav>(); 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<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); } } 
+8
source share

All Articles