У меня есть особая проблема с реализацией моей собственной коллекции, которая должна поддерживать 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
.