C# BindingList与List-WinForms数据绑定

C# BindingList与List-WinForms数据绑定,c#,winforms,data-binding,C#,Winforms,Data Binding,为了让WinForms中的数据绑定(例如到DataGridView)像您希望的那样工作,并在集合更改时添加/删除行,您必须使用BindingList(或DataTable)而不是通用列表。问题是,几乎没有人会本能地使用BindingList而不是库中的列表进行编码 BindingList实现了列表中没有的两个事件,这两个事件必须是数据绑定操作中的差异(也是抑制第二个事件的属性): 类似地,DataTable有两个可能启用类似功能的事件: RowDeleted TableNewRow 编辑:正如

为了让WinForms中的数据绑定(例如到DataGridView)像您希望的那样工作,并在集合更改时添加/删除行,您必须使用BindingList(或DataTable)而不是通用列表。问题是,几乎没有人会本能地使用BindingList而不是库中的列表进行编码

BindingList实现了列表中没有的两个事件,这两个事件必须是数据绑定操作中的差异(也是抑制第二个事件的属性):

类似地,DataTable有两个可能启用类似功能的事件:

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);