2011-01-28 15 views
6

ho una classe di base attuazione INotifyPropertyChanged:INotifyPropertyChanged e Threading

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

public event PropertyChangedEventHandler PropertyChanged; 

ho una classe derivata con una proprietà Latitude in questo modo:

private double latitude; 

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

mia classe derivata ha anche un metodo Fly che manipola Latitude.

Ho anche un modulo con un TextBox legato alla Latitude della mia classe derivata:

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

Un filo viene utilizzato per dare il via Fly in questo modo:

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

When Latitude cambiamenti, un viene generata un'eccezione:

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

risposta

8

Questo sembra essere un problema strano con affinità di thread. In definitiva, il codice sta cercando di eseguire l'aggiornamento da un thread non dell'interfaccia utente - Non sono chiaro il motivo per cui non è solo la visualizzazione dell'eccezione cross-thread, tuttavia - Mi chiedo se si tratti effettivamente di un gestore di eccezioni catch-all. Se rimuovo lo BindingSource (e leghiamo direttamente all'oggetto, che è valido) si do ottenere un'eccezione cross-thread (che mi aspettavo).

personalmente, sarei propenso a gestire manualmente, cioè sottoscrivere l'evento con un metodo che fa un Invoke al thread UI e aggiorna il Text manualmente. Comunque, sto solo controllando se qualche precedente codice vincolante cross-threaded potrebbe aiutare ...


Ecco un esempio utilizzando 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); 
      } 
     } 
    } 


} 

Ecco un esempio utilizzando un (modificato) versione di un vecchio codice di threading:

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); 
    } 
} 
+0

@WulfgarPro aggiunto due * diversi * esempi –

+0

@WulfgarPro - sì, ero semplicemente in sintonia con l'esempio - limitandolo al minimo che avrebbe mostrato * qualcosa * che stava accadendo. –

+0

@Marc Gravell - Ho ottenuto con successo l'esempio Invoke. Devo ammettere che sono ancora abbastanza alle prime armi quando si tratta di delegare e gestire gli eventi. Ho provato un paio di cose, ma continuo a venire con un 'Impossibile accedere a un oggetto disposto denominato" FormName "' eccezione quando si tenta di uscire dalla mia applicazione. Qualche idea ? – wulfgarpro