C# BindingList与List-WinForms数据绑定
为了让WinForms中的数据绑定(例如到DataGridView)像您希望的那样工作,并在集合更改时添加/删除行,您必须使用BindingList(或DataTable)而不是通用列表。问题是,几乎没有人会本能地使用BindingList而不是库中的列表进行编码 BindingList实现了列表中没有的两个事件,这两个事件必须是数据绑定操作中的差异(也是抑制第二个事件的属性): 类似地,DataTable有两个可能启用类似功能的事件:C# BindingList与List-WinForms数据绑定,c#,winforms,data-binding,C#,Winforms,Data Binding,为了让WinForms中的数据绑定(例如到DataGridView)像您希望的那样工作,并在集合更改时添加/删除行,您必须使用BindingList(或DataTable)而不是通用列表。问题是,几乎没有人会本能地使用BindingList而不是库中的列表进行编码 BindingList实现了列表中没有的两个事件,这两个事件必须是数据绑定操作中的差异(也是抑制第二个事件的属性): 类似地,DataTable有两个可能启用类似功能的事件: RowDeleted TableNewRow 编辑:正如
RowDeleted
TableNewRow
编辑:正如有帮助的SO社区在此指出的那样,可以通过调用正确的BindingList构造函数来转换列表(可能更准确地封装?):
BindingList<MyType> MyBL = new BindingList<MyType>();
MyList.ForEach(x => MyBL.Add(x));
BindingList MyBL=newbindingList();
MyList.ForEach(x=>MyBL.Add(x));
下面的代码说明了我的情况有点复杂。
编辑添加了必须存在于真实库中的InotifyProperty更改内容
public class RealString : INotifyPropertyChanged
{
private int _KnotCount = 0;
private List<KnotSpace> _KnotSpacings = new List<KnotSpace>();
public RealString()
{
KnotSpacings.Add(new KnotSpace());
}
public int KnotCount
{
get { return _KnotCount; }
set
{
int requiredSpacings = 0;
_KnotCount = value;
// Always one more space than knots
requiredSpacings = _KnotCount + 1;
if (requiredSpacings < KnotSpacings.Count)
{
while (requiredSpacings < KnotSpacings.Count)
{
KnotSpacings.Add(new KnotSpace());
}
}
else if (requiredSpacings > KnotSpacings.Count)
{
while (requiredSpacings > KnotSpacings.Count)
{
KnotSpacings.Remove(KnotSpacings.Last());
}
}
this.OnPropertyChanged(this, "KnotCount");
}
}
public List<KnotSpace> KnotSpacings { get => _KnotSpacings; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(object sender, string PropertyName)
{
if (this.PropertyChanged == null) return;
this.PropertyChanged(sender, new PropertyChangedEventArgs(PropertyName));
}
}
public class KnotSpace
{
private double _Spacing = 10;
public double Spacing { get => _Spacing; set => _Spacing = value; }
}
public类RealString:INotifyPropertyChanged
{
私有整数_KnotCount=0;
私有列表_KnotSpacings=新列表();
公共RealString()
{
添加(新的KnotSpace());
}
公共整数结计数
{
获取{return\u KnotCount;}
设置
{
int所需间距=0;
_结数=值;
//总是比结多出一个空间
所需间距=_节数+1;
如果(所需间距<节间距.计数)
{
while(所需间距<节间距.计数)
{
添加(新的KnotSpace());
}
}
else if(所需间距>节点间距.计数)
{
while(requiredSpacings>KnotSpacings.Count)
{
KnotSpacings.Remove(KnotSpacings.Last());
}
}
此.OnPropertyChanged(此“结数”);
}
}
公共列表KnotSpacings{get=>\u KnotSpacings;}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged(对象发送方,字符串PropertyName)
{
if(this.PropertyChanged==null)返回;
this.PropertyChanged(发送方,新的propertychangedventargs(PropertyName));
}
}
公共类空间
{
专用双_间距=10;
公共双间距{get=>\u spating;set=>\u spating=value;}
}
列表中的内容将显示在UI中,列表中内容的属性将在UI中修改,但UI不会直接从列表中添加/删除内容,除非更改KnotCount属性。当通过更改KnotCount属性更新KnotSpacings时,在BindingList中包装KnotSpacings属性不会导致BindingList更新
编辑好的,进一步澄清
BindingList BL = new BindingList<KnotSpace>(MyRealString.KnotSpacings);
DataGridView1.AutoGenerateColumns = true;
DataGridView1.DataSource = BL;
NumericUpDown1.DataBindings.Add("Value", MyRealString, "KnotCount", false, DataSourceUpdateMode.OnPropertyChanged);
BindingList BL=新的BindingList(MyRealString.KnotSpacings);
DataGridView1.AutoGenerateColumns=true;
DataGridView1.DataSource=BL;
NumericUpDown1.DataBindings.Add(“Value”,MyRealString,“KnotCount”,false,DataSourceUpdateMode.OnPropertyChanged);
与Windows控件相比,BindingList无法成功跟踪对基础列表属性(KnotSpacings)的更改。因此,将控件绑定到BindingList的数据并不能完成很多工作。如果UI从BindingList中添加/删除项,BindingList将非常有用,因为它在基础列表中执行相同的操作。但是,我需要在UI中复制库的添加/删除操作和逻辑,这是等待中的一个突破性变化
编辑对我的原始帖子所做的主要更改,试图:(1)澄清问题。(2) 将其区分为非重复问题(尽管其中一个问题是dup)。(3) 感谢如果我删除了这篇文章,其他人可能会失去的帮助。首先,有一种更好的方法可以将
列表
传递给绑定列表
BindingList
有一个构造函数,该构造函数接受一个List
,它将List
的元素复制到BindingList
中,如下所示:
List<int> myList = new List<int>();
BindingList<int> myBindingList = new BindingList<int>(myList);
List myList=new List();
BindingList myBindingList=新的BindingList(myList);
但这不是你的问题,真的。简单地回答您的问题-正确,List
不是WinForms中双向绑定的好选择。由于列表
没有任何事件通知添加的元素,因此您实际上只能保证单向绑定-数据输入可能会工作,但在尝试刷新时(例如,正在添加到列表的项目)会出现故障
也就是说,您提到这些库正在修改您在修改期间可以访问的列表。我认为一个好的库应该使用接口模式来使用、修改和传递集合。虽然List
和BindingList
是非常不同的类,但它们都实现了IList
、ICollection
和IEnumerable
。因此,任何接受这些接口中任何一个作为参数的函数都将接受列表
或绑定列表
(例如:public void DoSomethingWithCollection(IEnumerable collection)
可以接受列表
、绑定列表
,或实现IEnumerable
的任何其他集合。界面模式在C#生命周期的这一点上是众所周知的标准,尽管没有人的第一本能是在列表
上使用绑定列表
,但他们的第一本能绝对应该是在列表
上使用IEnumerable
(或IList
或ICollection
)
如果可能,绑定最好将列表
传递给绑定列表
的构造函数,然后再也不要使用列表
,而是使用添加
List<int> myList = new List<int>();
BindingList<int> myBindingList = new BindingList<int>(myList);