2011-12-30 31 views
5

Điều tôi đang làm là tạo một đối tượng (A) giữ một tham chiếu đến đối tượng khác (B). Phần giao diện người dùng của mã của tôi chứa các đối tượng đó (A) trong một BindingList được sử dụng làm nguồn dữ liệu cho chế độ xem lưới DevExpress. Bộ điều khiển gửi các đối tượng mới được tạo (A) tới giao diện người dùng thông qua một sự kiện. Bộ điều khiển cũng có một luồng cập nhật đối tượng được tham chiếu (B). Các ngoại lệ ném đến từ DevExpress GridView và đọc "Cross thread hoạt động phát hiện. Để ngăn chặn ngoại lệ này, thiết lập DevExpress.Data.CurrencyDataController.DisableThreadingProblemsDetection = true".Sử dụng đối tượng được tham chiếu qua các chủ đề

Bây giờ tôi không muốn chặn ngoại lệ này vì mã cuối cùng sẽ kết thúc trong một ứng dụng quan trọng.

Vậy làm cách nào để cập nhật đối tượng tham chiếu trên các chuỗi mà không gây ra sự cố? Đây là mã từ ứng dụng Kiểm tra của tôi. Về cơ bản nó sẽ giống nhau trong chương trình thực tế.

CẬP NHẬT Các lỗi trong giao diện người dùng đã được cố định bởi câu trả lời từ Nicholas Butler nhưng bây giờ ngoại trừ đã chuyển sang class Employee. Tôi đã cập nhật mã để phản ánh các thay đổi.

Đây là mã của tôi

* 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(); 
    } 
} 

Bộ điều khiển:

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; 
} 

nhân viên Class: (Exception ném vào lớp này)

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; 
} 

nhân viên Interface:

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

Pay Class:

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; 
} 

Tôi đánh giá rất cao sự giúp đỡ nào.

Trả lời

2

Vấn đề là EmployeeController.onNewEmployee đang được kích hoạt trên một chuỗi không phải là giao diện người dùng. Sử dụng mô hình async sự kiện có trụ sở để nâng cao sự kiện trên một cụ thể (trong trường hợp này UI) chủ đề: http://msdn.microsoft.com/en-us/library/hkasytyf.aspx.

Hoặc bạn có thể kiểm tra IsInvokeRequired trong mỗi trình xử lý sự kiện và nếu có, hãy sử dụng .Invoke để quay lại luồng UI .. Điều này càng cồng kềnh hơn nhưng có thể trong trường hợp của bạn dễ thực hiện hơn.

1

Bạn đang gọi empList.Add(empl); ngay cả khi InvokeRequired == true. Hãy thử:

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

Bạn cũng cần phải nâng cao INotifyPropertyChanged sự kiện của bạn trên thread UI, nhưng bạn không có một điều khiển giao diện người dùng để gọi Invoke trên. Cách đơn giản để làm điều này là để lưu trữ một tham chiếu đến hình thức chính của bạn và làm cho nó public static:

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

    public Form1() 
    { 
     UI = this; 
    } 
} 

Sau đó bạn có thể sử dụng Form1.UI.InvokeRequiredForm1.UI.Invoke từ bất cứ nơi nào trong ứng dụng của bạn.


Tôi đã cố gắng để có một bước tại một thời điểm, nhưng nếu bạn muốn có một giải pháp chính xác hơn, bạn có thể vượt qua các giao diện người dùng SynchronizationContext với bộ điều khiển và sử dụng nó Post hoặc Send phương pháp:

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

class EmployeeController 
{ 
    private SynchronizationContext _SynchronizationContext = null; 

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

Sau đó, bạn phải lấy nó cho các vật thể của mình. Để tăng sự kiện, bạn sẽ làm điều này:

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

Ahh mà làm việc nhưng bây giờ trong class Employee của tôi ở phương pháp "_myPay_PropertyChanged()" this.PropertyChanged (this, PropertyChangedEventArgs mới ("Mức lương")); đang ném cùng một ngoại lệ. Tôi cũng cần phải gọi một cuộc gọi về điều đó? – Stephen

+0

Có - Tôi đã cập nhật câu trả lời của mình. –

+0

Hoặc bạn có thể thực hiện các sự kiện dựa trên mô hình async trong EmployeeController của bạn và bạn sẽ không phải làm bất kỳ điều này như EmployeeController sẽ chịu trách nhiệm cho việc thực hiện các cuộc gọi trên thread righe. Mất thêm một chút thời gian để hiểu, nhưng một giải pháp gọn gàng hơn nhiều. –

Các vấn đề liên quan