Привязка данных в WinForms с использованием IBindingList завершается с ошибкой в ​​пустом списке - PullRequest
3 голосов
/ 20 ноября 2010

У меня есть особая проблема с реализацией моей собственной коллекции, которая должна поддерживать IBindingList.

У меня есть класс коллекции (DataCollection) для определенного класса данных (DataItem).Коллекция реализует интерфейсы IBindingList, IList, IList<DataItem>, а DataItem реализует INotifyPropertyChanged (и имеет открытые свойства для привязки данных).

Когда я пытаюсь привязать коллекцию к DataGridView установив свойство DataSource сетки, он будет корректно работать , если коллекция не пуста в момент привязки.В противном случае, если коллекция пуста, сетка замечает, когда строки (т.е. DataItems) добавляются или удаляются из коллекции, но ячейки остаются пустыми.С этой проблемой связано то, что сетка не распознает открытые члены класса данных в случае AutoGenerateColumns=true и не может генерировать столбцы.

Что я и пробовал, связать мой DataItems с помощьюBindingList<DataItem>.В этом случае сетка работает правильно, даже если список пуст в момент установки DataSource.С другой стороны, если я использую BindingList<object> (но тот же DataItems в качестве содержимого), поведение будет таким же неправильным, как и в моем DataCollection.Я предполагаю, что проблема в том, что если в момент привязки список пуст, привязка данных не сможет правильно определить тип DataItem, а также не сможет восстановиться позже, когда наконец элементы будут добавлены в коллекцию.

Важно то, что он работает, если коллекция не пуста во время привязки.

Обратите внимание, что при указании столбцов возникает та же ошибка:

this.dataGridView.ReadOnly = true;

this.dataGridView.AutoGenerateColumns = false;
DataGridViewTextBoxColumn column;

column = new DataGridViewTextBoxColumn();
column.DataPropertyName = "Id";
column.HeaderText = "Id";
this.dataGridView.Columns.Add(column);

column = new DataGridViewTextBoxColumn();
column.DataPropertyName = "UserName";
column.HeaderText = "UserName";
this.dataGridView.Columns.Add(column);

this.dataGridView.DataSource = myList;

Я также пытался вернуть true на AllowNew моего IBindingList.Это не оказало заметного влияния.

Что также дает сбой, так это:

var bindingSource = new BindingSource();
bindingSource.DataSource = myList;
this.dataGridView.DataSource = bindingSource;

Вопрос в том, как я могу заставить механизм привязки распознавать мои DataItems?

(Спасибо)

ОБНОВЛЕНИЕ 1:

Я создал небольшой тестовый проект, который показывает проблему:

public partial class Form1: Form {
    public Form1() {
        InitializeComponent();
    }

    class DataItem: INotifyPropertyChanged {
        private int _id;
        public int Id {
            get {
                return _id;
            }
            set {
                if (value != _id) {
                    _id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        private string _userName;
        public string UserName {
            get {
                return _userName;
            }
            set {
                if (value != _userName) {
                    _userName = value;
                    OnPropertyChanged("UserName");
                }
            }
        }

        private void OnPropertyChanged(string propertyName) {
            var handler = PropertyChanged;
            if (handler != null) {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    /// Make a list of type DataItem or object...
    //BindingList<object> list = new BindingList<object>() {
    BindingList<DataItem> list = new BindingList<DataItem>() {
        //new DataItem() {
        //    Id = 1,
        //    UserName = "testuser"
        //}
    };
    private void Form1_Load(object sender, EventArgs e) {
        DataGridView dataGridView = new System.Windows.Forms.DataGridView();
        dataGridView.Size = new Size(this.Width-20, this.Height-30);

        dataGridView.AutoGenerateColumns = true;
        DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
        column.DataPropertyName = "Id";
        column.HeaderText = "Id";
        dataGridView.Columns.Add(column);

        this.Controls.Add(dataGridView);



        dataGridView.DataSource = list;

        list.Add( 
            new DataItem() {
                Id = 3,
                UserName = "admin"
            }
        );

        // Make some modifications on the data...
        (new System.Threading.Thread( state => {
            System.Threading.Thread.CurrentThread.IsBackground = true;

            System.Threading.Thread.Sleep(2000);
            this.Invoke( (Action)( () => {
                list.Add(new DataItem() {
                    Id = 2,
                    UserName = "guest"
                });
            } ) );

            System.Threading.Thread.Sleep(2000);
            this.Invoke( (Action)( () => {
                DataItem user = (list.First( obj => ((DataItem)obj).Id == 3 )) as DataItem;
                user.UserName = "Administrator";
            } ) );
        })).Start();
    }
}

Если тип списка BindingList<DataItem> это работает правильно.Если тип BindingList<object>, он работает только в том случае, если список не пуст при инициализации DataSource.

1 Ответ

7 голосов
/ 22 ноября 2010

Привязка данных начнется с просмотра элементов списка, чтобы попытаться получить их свойства, однако для пустого списка он получит всю свою информацию из Type элементов списка.Причина, по которой привязка данных не может обнаружить какие-либо свойства, если вы используете пустой BindingList<object>, заключается в том, что object не имеет привязываемых свойств.

Чтобы полностью гарантировать, что ваш класс DataCollection правильно поддерживает привязку, даже если он пуст, реализовать интерфейс ITypedList.Он включает метод GetItemProperties(), который позволяет явно указать, какие свойства являются связываемыми.В этом методе вы можете использовать следующее для возврата свойств DataItem:

return TypeDescriptor.GetProperties(typeof(DataItem));

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...