[英]BindingList vs List - WinForms Data Binding
为了让 WinForms 中的数据绑定(例如到 DataGridView)像您希望的那样工作并在集合更改时添加/删除行,您必须使用 BindingList(或 DataTable)而不是通用列表。 问题是,几乎没有人的第一直觉是在他们的库中使用 BindingList 而不是 List 进行编码。
BindingList 实现了两个 List 没有的事件,它们必须是数据绑定操作的不同之处(也是抑制第二个事件的属性):
AddingNew
ListChanged
RaiseListChangedEvents
同样,DataTable 有两个事件可能会启用类似的功能:
RowDeleted
TableNewRow
编辑:正如有用的 SO 社区在此处和另一篇文章中指出的那样,可以通过调用正确的 BindingList 构造函数来转换 List(也许更准确地封装?):
BindingList<MyType> MyBL = new BindingList<MyType>();
MyList.ForEach(x => MyBL.Add(x));
我的情况有点复杂,如下面的代码所示。 编辑添加了必须存在于真实库中的 INotifyPropertyChanged 内容。
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; }
}
列表中的事物显示在 UI 中,列表中事物的属性在 UI 中进行修改,但 UI 不会直接从列表中添加/删除事物,除非更改 KnotCount 属性。 当通过更改 KnotCount 属性更新 KnotSpacings 时,将 KnotSpacings 属性包装在 BindingList 中不会导致 BindingList 更新。
编辑好的,更多说明......
BindingList BL = new BindingList<KnotSpace>(MyRealString.KnotSpacings);
DataGridView1.AutoGenerateColumns = true;
DataGridView1.DataSource = BL;
NumericUpDown1.DataBindings.Add("Value", MyRealString, "KnotCount", false, DataSourceUpdateMode.OnPropertyChanged);
与 Windows 控件相比,BindingList 在跟踪基础 List 属性 (KnotSpacings) 的更改方面没有更多成功。 因此,将控件绑定到 BindingList 的数据并没有太大的作用。 如果 UI 从 BindingList 添加/删除项目,则 BindingList 效果很好,因为它在底层 List 中执行相同的操作。 但随后我需要在我的 UI 中复制库的添加/删除操作和逻辑,这是等待中的重大变化。
编辑对我的原始帖子所做的主要更改,试图:(1)澄清问题。 (2) 将其区分为不是重复问题(尽管几个问题之一是重复问题)。 (3) 承认如果我删除帖子可能会失去其他人的有益努力。
首先,有一种更好的方法可以将List<T>
传递给BindingList<T>
。 BindingList<T>
有一个接受List<T>
的构造函数,它将List
的元素复制到BindingList
,如下所示:
List<int> myList = new List<int>();
BindingList<int> myBindingList = new BindingList<int>(myList);
但这不是你的问题,真的。 简单地回答您的问题 -正确, List<T>
不是 WinForms 中双向绑定的好选择。 由于List<T>
没有任何事件通知添加的元素,因此您实际上只能保证单向绑定 - 数据输入可能有效,但在尝试刷新时,例如添加到List
项目时,事情会崩溃。
也就是说,您提到这些库正在修改您在修改期间有权访问的List<T>
。 我认为一个好的库会使用接口模式来使用、修改和传递集合。 尽管List<T>
和BindingList<T>
是非常不同的类,但它们都实现了IList<T>
、 ICollection<T>
和IEnumerable<T>
。 因此,任何接受这些接口作为参数的函数都可以接受List<T>
或BindingList<T>
(例如: public void DoSomethingWithCollection(IEnumerable<int> collection)
可以接受List<int>
, BindingList<int>
,或任何其他实现IEnumerable<int>
集合)。 Interface 模式是 C# 生命周期中的一个众所周知的标准,尽管没有人的第一直觉是在List<T>
上使用BindingList<T>
List<T>
,但他们的第一直觉绝对应该是使用IEnumerable<T>
(或IList<T>
或ICollection<T>
)在List<T>
。
在可能的情况下,绑定将您的List
传递给BindingList
的构造函数会更好,然后永远不要再次使用List
- 相反,使用BindingList
的Add
和Remove
方法来管理它的内部集合。
如果您使用接受IList<T>
实例的BindingList<T>
构造函数,则该实例用于支持BindingList<T>
,并且IList<T>
中的更改反映在BindingList
。
然而,这并不是故事的结局。 WinForms 数据绑定的结构方式是,您离简单的、单一属性的 2 向绑定越远,您需要涵盖的内容就越多。
例如, INotifyPropertyChanged
接口由用作数据源的类实现,以通知子属性(如KnotCount
属性)的更改。
对于更复杂的场景,我们不会使用BindingList<T>
,而是从它派生一个类并覆盖一个或多个数据绑定机制。 BindingSource
类也是如此。
数据绑定机制背后有很多样板,但几乎每个部分都可以派生以自定义行为。 绘制数据绑定中使用的类和接口的对象图有时很有用(大量阅读相关文档),以便对整个过程有一个很好的心理概述。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.