2009-08-29 31 views
19

Tôi có một BindingList <> của một lớp được đặt thành thuộc tính DataSource của một BindingSource, mà lần lượt được đặt thành thuộc tính DataSource của một DataGridView.BindingList <> ListChanged event

1. Đó là sự hiểu biết của tôi rằng mọi bổ sung vào danh sách sẽ kích hoạt sự kiện ListChanged sẽ truyền bá thông qua BindingSource và sau đó lên DataGridView, bản cập nhật sẽ hiển thị thay đổi. Điều này sẽ xảy ra vì các sự kiện đã được tự động kết nối. (Có?)

Điều này là tốt và tốt khi tất cả công việc được thực hiện trên chuỗi giao diện người dùng, nhưng khi danh sách được tạo và thay đổi từ chuỗi không phải giao diện người dùng, cuối cùng là trường hợp ngoại lệ chéo xảy ra khi lưới đã cập nhật. Tôi có thể hiểu lý do tại sao điều này xảy ra, nhưng không có cách khắc phục ...

2. Điều tôi đang gặp khó khăn là tôi nên chặn sự kiện ListChanged để thử và sắp xếp thứ gì vào chuỗi giao diện người dùng ? Tôi đoán rằng tôi cần một tham chiếu đến thread UI bằng cách nào đó để giúp làm điều này?

Tôi đã đọc nhiều bài viết/bài viết về vấn đề này, nhưng tôi đang gặp khó khăn bởi vì tôi không hiểu đầy đủ về cơ chế tại nơi làm việc ở đây.

Tôi sẽ không bao giờ được thay đổi bất kỳ mục khi họ đang có trong danh sách, chỉ thêm chúng, và ban đầu thanh toán bù trừ danh sách.

(Tôi đang sử dụng NET 2,0)

Trả lời

27

Bạn có thể mở rộng BindingList sử dụng một ISynchronizeInvoke (thực hiện bởi System.Windows.Forms.Control) để marshal invokations sự kiện vào thread UI.

Sau đó, tất cả những gì bạn cần làm là sử dụng loại danh sách mới và tất cả được sắp xếp.

public partial class Form1 : System.Windows.Forms.Form { 

    SyncList<object> _List; 
    public Form1() { 
     InitializeComponent(); 
     _List = new SyncList<object>(this); 
    } 
} 

public class SyncList<T> : System.ComponentModel.BindingList<T> { 

    private System.ComponentModel.ISynchronizeInvoke _SyncObject; 
    private System.Action<System.ComponentModel.ListChangedEventArgs> _FireEventAction; 

    public SyncList() : this(null) { 
    } 

    public SyncList(System.ComponentModel.ISynchronizeInvoke syncObject) { 

     _SyncObject = syncObject; 
     _FireEventAction = FireEvent; 
    } 

    protected override void OnListChanged(System.ComponentModel.ListChangedEventArgs args) { 
     if(_SyncObject == null) { 
      FireEvent(args); 
     } 
     else { 
      _SyncObject.Invoke(_FireEventAction, new object[] {args}); 
     } 
    } 

    private void FireEvent(System.ComponentModel.ListChangedEventArgs args) { 
     base.OnListChanged(args); 
    } 
} 
+3

TUYỆT VỜI. Nếu tôi có thể upvote điều này một triệu lần, tôi sẽ. – bulltorious

+0

Tôi hiểu nó, nhưng nó vẫn là phép thuật và làm những gì mọi người muốn làm. Cập nhật điều khiển thông qua cấu trúc dữ liệu và không phải lo lắng về việc gọi. – Beached

+0

@bulltorious bạn có thể cung cấp tiền thưởng :) –

2
  1. Quan điểm này là đủ công bằng. Bên dưới trang bìa, các đối tượng khác như CurrencyManager và Binding đảm bảo các điều khiển được cập nhật khi nguồn dữ liệu cơ bản thay đổi.

  2. Thêm mục vào BindingList bị ràng buộc dữ liệu sẽ kích hoạt một loạt các sự kiện kết thúc cố gắng cập nhật DataGridView. Vì giao diện người dùng chỉ có thể được cập nhật từ chuỗi giao diện người dùng, bạn nên thêm các mục vào BindingList từ chuỗi giao diện người dùng thông qua Control.Invoke.

Tôi đã lắp ráp mẫu nhanh tạo Biểu mẫu có DataGridView, BindingSource và nút.

Nút quay lên một chủ đề mà mô phỏng nhận được một mục mới để đưa vào BindingList.

Bản thân việc đưa vào được hoàn thành trong chuỗi giao diện người dùng thông qua Control.Invoke.


    public partial class BindingListChangedForm : Form { 
     BindingList<Person> people = new BindingList<Person>(); 
     Action<Person> personAdder; 

     public BindingListChangedForm() { 
      InitializeComponent(); 
      this.dataGridView1.AutoGenerateColumns = true; 
      this.bindingSource1.DataSource = this.people; 
      this.personAdder = this.PersonAdder; 
     } 

     private void button1_Click(object sender, EventArgs e) { 
      Thread t = new Thread(this.GotANewPersononBackgroundThread); 
      t.Start(); 
     } 

     // runs on the background thread. 
     private void GotANewPersononBackgroundThread() { 
      Person person = new Person { Id = 1, Name = "Foo" }; 

      //Invokes the delegate on the UI thread. 
      this.Invoke(this.personAdder, person); 
     } 

     //Called on the UI thread. 
     void PersonAdder(Person person) { 
      this.people.Add(person); 
     } 
    } 

    public class Person { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 
+0

Alfred, cảm ơn vì ví dụ rõ ràng của bạn. Nhưng, giả sử tôi có một lớp được tạo trên một luồng khác tạo và thêm vào danh sách trên chuỗi này. Để dữ liệu liên kết này với một lưới Tôi cần một tham chiếu đến thread UI có? Tôi không chắc phải làm gì ở đây một cách tốt nhất. Vượt qua một tham chiếu đến các hình thức thông qua các nhà xây dựng lớp có lẽ, và làm một cái gì đó theo cách này? – Andy

+1

1. Có. 2. Chuyển một tham chiếu của đối tượng của bạn tới biểu mẫu thông qua hàm tạo ** Form ** hoặc một thành viên khác. –

+0

Andy, đối tượng thường không có liên quan đến một Chủ đề, Điều khiển là một ngoại lệ.Đó là mã thực hiện trên một chuỗi. –

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