2011-12-30 23 views
5

Yaptığım şey, başka bir nesneye (B) referans içeren bir nesne (A) oluşturmaktır. Kodumun UI kısmı, bir DevExpress ızgara görünümü için veri kaynağı olarak kullanılan bir Bağlama Listesindeki (A) nesnelerini tutar. Denetleyici yeni oluşturulan nesneleri (A) bir olay aracılığıyla UI'ye gönderir. Denetleyici, başvurulan nesneyi (B) güncelleyen bir iş parçacığına da sahiptir. Atılan istisna, DevExpress GridView'den geliyor ve "Çapraz iş parçacığı işlemi algılandı. Bu istisnayı bastırmak için DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection = true" değerini ayarlayın.Başvurulan bir nesneyi iş parçacığı genelinde kullanın

Şimdi bu özel durumu bastırmak istemiyorum çünkü kod sonunda kritik bir uygulamada sona erecek.

Peki, bir referans nesnesini sorun oluşturmadan iş parçacıklarında nasıl güncelleyebilirim? İşte Test uygulamamın kodu. Esas programda esas olarak aynı olacaktır.

GÜNCELLEME arayüzünde hata Nicholas Butler dan cevap tarafından düzeltildi ama şimdi durum Çalışan sınıfa taşındı. Değişiklikleri yansıtacak kodu güncelledim. İşte

benim kodudur

* UI *

public partial class Form1 : Form 
{ 
    private BindingList<IEmployee> empList; 
    EmployeeController controller; 
    private delegate void AddEmployeInvoke(IEmployee employee); 
    public Form1() 
    { 
     controller = new EmployeeController(); 
     controller.onNewEmployee += new EmployeeController.NewEmployee(controller_onNewEmployee); 
     empList = new BindingList<IEmployee>(); 
     InitializeComponent(); 
    } 

    void controller_onNewEmployee(IEmployee emp) 
    { 
     AddEmployee(emp); 
    } 

    private void AddEmployee(IEmployee empl) 
    { 
     if (InvokeRequired) 
     { 
      this.Invoke(new AddEmployeInvoke(AddEmployee), new Object[] {empl}); 
     } 
     else 
     { 
      empList.Add(empl); 
     } 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     this.gridControl1.DataSource = empList; 
     this.gridControl1.RefreshDataSource(); 
     controller.Start(); 
    } 
} 

Denetleyici:

class EmployeeController 
{ 
    List<IEmployee> emps; 
    Task empUpdater; 
    CancellationToken cancelToken; 
    CancellationTokenSource tokenSource; 
    Pay payScale1; 
    Pay payScale2; 

    public EmployeeController() 
    { 
     payScale1 = new Pay(12.00, 10.00); 
     payScale2 = new Pay(14.00, 11.00); 
     emps = new List<IEmployee>(); 
    } 

    public void Start() 
    { 
     empUpdater = new Task(AddEmployee, cancelToken); 
     tokenSource = new CancellationTokenSource(); 
     cancelToken = tokenSource.Token; 
     empUpdater.Start(); 
    } 

    public bool Stop() 
    { 
     tokenSource.Cancel(); 
     while (!empUpdater.IsCompleted) 
     { } 
     return true; 
    } 

    private void AddEmployee() 
    { 
     IEmployee emp = new Employee("steve", ref payScale1); 
     ThrowEmployeeEvent(emp); 
     emps.Add(emp); 
     emp = new Employee("bob", ref payScale2); 
     ThrowEmployeeEvent(emp); 
     emps.Add(emp); 
     int x = 0; 

     while (!cancelToken.IsCancellationRequested) 
     { 
      emp = new Employee("Emp" + x, ref payScale1); 
      ThrowEmployeeEvent(emp); 
      x++; 
      emp = new Employee("Emp" + x, ref payScale2); 
      ThrowEmployeeEvent(emp); 

      Thread.Sleep(1000); 

      payScale2.UpdatePay(10.0); 
      payScale1.UpdatePay(11.0); 

      Thread.Sleep(5000); 
     } 
    } 

    private void ThrowEmployeeEvent(IEmployee emp) 
    { 
     if (onNewEmployee != null) 
      onNewEmployee(emp); 
    } 

    public delegate void NewEmployee(IEmployee emp); 
    public event NewEmployee onNewEmployee; 
} 

Çalışan Sınıf: (bu sınıfta atılmış İstisna)

class Employee : IEmployee 
{ 
    private string _name; 
    private double _salary; 
    private Pay _myPay; 
    public string Name 
    { 
     get { return _name; } 
     set { _name = value; 
      //if (PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Name")); 
      } 
    }   
    public double Salary 
    { 
     get { return _salary; } 
    } 
    int x = 1; 

    public Employee(string name, ref Pay pay) 
    { 
     _myPay = pay; 
     _myPay.PropertyChanged += new PropertyChangedEventHandler(_myPay_PropertyChanged); 
     _salary = _myPay.Salary; 
     Name = name; 
    } 

    void _myPay_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName == "Salary") 
     { 
      _salary = _myPay.Salary; 
      if (this.PropertyChanged != null) 
       // exception thrown on the line below 
       this.PropertyChanged(this, new PropertyChangedEventArgs("Salary")); 
     } 
    } 

    public void ChangeName() 
    { 
     Name = "Me " + x; 
     x++; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Çalışan Arayüzü:

interface IEmployee : INotifyPropertyChanged 
{ 
    string Name { get; set; } 
    double Salary { get;} 
} 

Öde Sınıfı:

class Pay : INotifyPropertyChanged 
{ 
    private double _salary; 
    private double _bonus; 
    public double Salary { get { return _salary; } set { _salary = value; if(PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Salary"));} } 
    public double Bonus { get { return _bonus; } set { _bonus = value; if (PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("Bonus")); } } 

    public Pay(double salary, double bonus) 
    { 
     Salary = salary; 
     Bonus = bonus; 
    } 

    public void UpdatePay(double salary) 
    { 
     Salary += salary; 
     if (onChange != null) 
      this.onChange(); 
    } 

    public void UpdatePay(double salary, double bonus) 
    { 
     Salary += salary; 
     Bonus += bonus; 

     if (onChange != null) 
      this.onChange(); 
    } 

    public delegate void Change(); 
    public event Change onChange; 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Ben büyük ölçüde herhangi bir yardım için teşekkür ederiz.

cevap

2

sorun EmployeeController.onNewEmployee olmayan bir UI iş parçacığı üzerinde ateş ediliyor olmasıdır. Olayları belirli bir (bu durumda UI) iş parçacığı üzerinde yükseltmek için olay tabanlı async şablonunu kullanın: http://msdn.microsoft.com/en-us/library/hkasytyf.aspx.

Alternatif olarak, her olay işleyicide IsInvokeRequired seçeneğini işaretleyebilir ve eğer varsa, UI iş parçacığına geri dönmek için .Invoke işlevini kullanın. Bu daha hantaldır, ancak sizin durumunuzda daha kolay uygulanabilir.

1

InvokeRequired == true olduğunda bile empList.Add(empl); numaralı telefonu arıyorsunuz. Deneyin:

private void AddEmployee(IEmployee empl) 
{ 
    if (InvokeRequired) 
    { 
     this.Invoke(new AddEmployeInvoke(AddEmployee), new Object[] {empl}); 
    } 
    else 
    { 
     empList.Add(empl); //exception thrown here 
    } 
} 

Ayrıca UI iş parçacığı üzerinde INotifyPropertyChanged olayları yükseltmek gerekir, ancak üzerinde Invoke çağırmak için UI denetiminde yok.

public partial class Form1 : Form 
{ 
    public static Control UI { get; private set; } 

    public Form1() 
    { 
     UI = this; 
    } 
} 

Daha sonra uygulamada herhangi bir yerde Form1.UI.InvokeRequired ve Form1.UI.Invoke kullanabilirsiniz: Bunu yapmanın kolay yolu, ana forma bir referans olarak saklamak ve ona public static yapmaktır.


ben her seferinde bir adım atmak için çalışıyordu, ama daha doğru bir çözüm istiyorsanız, kontrolöre UI SynchronizationContext geçmek ve onun Post veya Send yöntemleri kullanabilirsiniz:

public Form1() 
{ 
    controller = new EmployeeController(SynchronizationContext.Current); 
    ... 

class EmployeeController 
{ 
    private SynchronizationContext _SynchronizationContext = null; 

    public EmployeeController(SynchronizationContext sc) 
    { 
     _SynchronizationContext = sc; 
     ... 

Daha sonra nesnelerinize almak zorundasınız. Bir etkinliği artırmak için, o zaman bu yapardın:

var evt = this.PropertyChanged; 
if (evt != null) sc.Send(
    new SendOrPostCallback(state => evt(this, ...EventArgs...)), 
    null); 
+0

Ahh bu çalıştı ancak şimdi "_myPay_PropertyChanged()" yönteminde Çalışan sınıfımda çalıştı.PropertyChanged (bu yeni PropertyChangedEventArgs ("Maaş")); aynı istisna atıyor. Bunun için bir de çağırmak zorunda mıyım? – Stephen

+0

Evet - Cevabımı güncelledim. –

+0

Ya da EmployeeController'da olaya dayalı async modelini uygulayabilirsiniz ve bu konuda herhangi bir işlem yapmanız gerekmeyecektir çünkü EmployeeController araçtaki çağrıları yapmaktan sorumlu olacaktır. Anlamak için biraz daha zaman alır, ama çok daha iyi bir çözüm. –

İlgili konular