开发者

Databinding in WinForms using IBindingList fails on empty list

I have a particular problem implementing my own collection which should support IBindingList.

I have a collection class (DataCollection) for a particular data class (DataItem). The collection implements interfaces IBindingList, IList, IList<DataItem> and the DataItem implements INotifyPropertyChanged (and has public properties for databinding).

When I try to bind the collection to a DataGridView by setting the DataSource property of the grid, it works correctly if the collection is not empty at the moment of binding. Otherwise, if the collection is empty, the grid notices when rows (i.e. DataItems) are added or removed from the collection, but the cells stay empty. Related to the problem is that the grid fails to recognize the public members of the data class in case of AutoGenerateColumns=true and it cannot generate the columns.

What I also tried, the bind my DataItems using a BindingList<DataItem>. In that case the grid works correctly even if the list is empty at the moment of setting DataSource. On the other hand, if I use a BindingList<object> (but the same DataItems as content) the behavior is just as wrong as with my DataCollection. I guess the problem is, if at the moment of binding the list is empty, the databinding fails to detect the DataItem type properly, and it also cannot recover later, when finally items are added to the collection.

Important is that it works if the collection is not empty at binding time.

Notice, that the same error occurs when I specify the columns:

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;

I also tried to return true on the AllowNew of my IBindingList. That had no observable impact.

What also fails is the following:

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

The question is, how can I tell the binding mechanism to recognize my DataItems?

(Thank you)

UPDATE 1:

I made a small test project which shows the issue:

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;

开发者_StackOverflow            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();
    }
}

If the type of the list is BindingList<DataItem> it works correctly. If the type is BindingList<object> it only works if the list is not empty when initializing the DataSource.


Data binding will start by looking at the list items to try and get their properties, however for an empty list it will get all of its information from the Type of the list items. The reason why data binding fails to discover any properties if you use an empty BindingList<object> is that object has no bindable properties.

To completely ensure that your DataCollection class properly supports binding, even when empty, implement the ITypedList interface. It includes the method GetItemProperties(), which allows you to explicitly state which properties are bindable. Within this method, you can use the following to return the properties on DataItem:

return TypeDescriptor.GetProperties(typeof(DataItem));

This way, even if the collection is empty, data binding will know which properties to show.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜