使用IBindingList的WinForms中的数据绑定在空列表中失败 [英] Databinding in WinForms using IBindingList fails on empty list

查看:135
本文介绍了使用IBindingList的WinForms中的数据绑定在空列表中失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特殊的问题,实现我自己的集合,应该支持 IBindingList



我有一个集合类( DataItem )的特定数据类( DataCollection )。该集合实现接口 IBindingList IList IList< DataItem> DataItem 实现 INotifyPropertyChanged (并具有数据绑定的公共属性)。



当我尝试通过设置网格的 DataSource 属性将集合绑定到 DataGridView ,如果在绑定时集合不为空,则它正常工作。否则,如果集合为空,则在集合中添加或删除行(即 DataItems )时,网格会注意到,但单元格保持为空。与该问题相关联的是,如果 AutoGenerateColumns = true ,则无法识别数据类的公共成员,并且无法生成列。



我还试过,绑定我的 DataItems 使用 BindingList< DataItem> 。在这种情况下,即使在设置 DataSource 时,列表为空,网格也能正常工作。另一方面,如果我使用 BindingList< object> (但是相同的 DataItems 作为内容),行为是就像我的 DataCollection 一样错误。我猜问题是,如果在绑定的时候列表为空,数据绑定无法正确检测到 DataItem 类型,而且在最后的项目中也无法恢复



重要的是,如果集合在绑定时间不为空,那么它是有效的。



请注意,当我指定列时,同样的错误发生:

  this.dataGridView.ReadOnly = true; 

this.dataGridView.AutoGenerateColumns = false;
DataGridViewTextBoxColumn列;

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;

我也尝试在真的 AllowNew 我的 IBindingList 。这没有可观察的影响。



还有什么失败的是:

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

问题是,我如何告诉绑定机制来识别我的 DataItems



(谢谢)



更新1:



我做了一个小测试项目,显示了这个问题:

  public partial class Form1:表单{
public Form1(){
InitializeComponent();
}

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

私有字符串_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;
}

///创建DataItem或对象的列表...
// 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
}
);

//对数据进行一些修改...
(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))作为DataItem;
user.UserName =Administrator;
}));
}))。
}
}

如果列表的类型是 BindingList< DataItem> 它正常工作。如果类型为 BindingList< object> ,则只有在初始化 DataSource 时列表不为空时才起作用。 p>

解决方案

数据绑定将从查找列表项开始,尝试获取其属性,但是对于一个空列表,它将获得所有其信息来自类型的列表项。如果使用空的 BindingList< object> ,数据绑定无法发现任何属性的原因是对象没有可绑定的属性。



要完全确保您的 DataCollection 类正确支持绑定,即使为空,也可以实现 ITypedList 界面。它包括方法 GetItemProperties(),它允许您显式指定哪些属性是可绑定的。在此方法中,您可以使用以下命令返回 DataItem 上的属性:

  return TypeDescriptor.GetProperties(typeof(DataItem)); 

这样,即使集合为空,数据绑定也将知道要显示的属性。 p>

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;

            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.

这篇关于使用IBindingList的WinForms中的数据绑定在空列表中失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆