Событие BindingList <> ListChanged - PullRequest
       4

Событие BindingList <> ListChanged

19 голосов
/ 29 августа 2009

У меня есть BindingList <> класса, для которого установлено свойство DataSource объекта BindingSource, которое, в свою очередь, установлено для свойства DataSource объекта DataGridView.

1. Насколько я понимаю, любые добавления в список будут вызывать событие ListChanged, которое будет распространяться через BindingSource, а затем в DataGridView, который будет обновляться для отображения изменений. Это произойдет, потому что события были автоматически подключены. (Да?)

Это все хорошо, когда вся работа выполняется в потоке пользовательского интерфейса, но когда список создается и изменяется из потока, не являющегося пользовательским интерфейсом, в конечном итоге возникает исключение между потоками при обновлении сетки. Я могу понять, почему это происходит, но нет, как это исправить ...

2. Что мне трудно понять, это где лучше всего перехватывать событие ListChanged, чтобы попытаться собрать вещи в потоке пользовательского интерфейса? Я предполагаю, что мне нужна ссылка на поток пользовательского интерфейса, чтобы помочь сделать это?

Я прочитал много постов / статей по этому вопросу, но я изо всех сил, потому что я не до конца понимаю механизмы работы здесь.

Я никогда не буду изменять какие-либо элементы, если они есть в списке, только добавляя их и первоначально очищая список.

(я использую .NET 2.0)

Ответы [ 2 ]

28 голосов
/ 29 августа 2009

Вы можете расширить BindingList, чтобы использовать ISynchronizeInvoke (реализованный System.Windows.Forms.Control) для перенаправления вызовов событий в поток пользовательского интерфейса.

Тогда все, что вам нужно сделать, это использовать новый тип списка, и все будет отсортировано.

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);
    }
}
2 голосов
/ 29 августа 2009
  1. Это мнение достаточно справедливо. Под покровами другие объекты, такие как CurrencyManager и Binding, обеспечивают обновление элементов управления при изменении базового источника данных.

  2. Добавление элемента в привязанный к данным BindingList запускает серию событий, которые заканчиваются попыткой обновить DataGridView. Поскольку пользовательский интерфейс может быть обновлен только из потока пользовательского интерфейса, необходимо добавить элементы в BindingList из потока пользовательского интерфейса через Control.Invoke .

Я собрал быстрый пример создания формы с DataGridView, BindingSource и Button.

Кнопка раскручивает другой поток, имитирующий получение нового элемента для включения в BindingList.

Само включение выполняется обратно в поток пользовательского интерфейса через 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; }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...