创建可绑定到DataGrid的自定义集合

我在一家建筑公司工作,我正在创建一个3D建模程序的插件,以协助设计。 我有一个Building类和一个Floor类。 该建筑包含对FloorList楼层集合的引用。 我正在试图弄清楚FloorList集合的基础,这样我就可以最大限度地减少创建界面来编辑集合所需的工作量。

Floor系列代表一系列相互堆叠的建筑楼层。 每个楼层都有一个读写的Floor.Height属性,以及一个只读的Floor.Elevation属性,并通过总计当前楼层以下的楼层高度来设置。 因此,无论何时在集合中添加,删除,移动或更改Floor,都需要更新Floor.Elevation属性。

另外,我想创建一个用于编辑此集合的UI。 我正在考虑使用DataGrid控件,其中列出每个Floor高度和其他属性作为控件的一行。 用户应该能够使用控件添加,删除和重新排序楼层。 我希望将其设置为尽可能简单灵活。 这意味着我只想简单地将Floors集合绑定到DataGrid,并根据Floor类的属性填充DataGrid的列。 如果可能的话,我希望能够利用DataGrid控件的内置添加/删除UI界面,而不必在我的集合和DataGrid之间设置一堆事件关系。

为了使将来进一步复杂化,我需要能够允许用户动态地将自定义属性添加到我希望他们能够在DataGrid中查看和编辑的Floors 。 我想我最终会通过让Floor类实现IExtenderProvider来实现这一点。 所以最终DataGrid看起来像这样:

初始属性未来自定义用户属性

高度提升程序类型UnitType UnitCount  
 15'70'住宅豪宅5
 15'55'住宅豪宅5
 15'40'住宅预算10
 20'20'零售不适用2
 20'0'零售不适用3

我现在的问题是我应该将FloorList集合作为基础来实现这一function? 我正在考虑的选项如下。

1)inheritance自列表(楼层)

  • 诸如添加/删除之类的方法不是玻璃的,因此我不能覆盖它们来更新高程

2)实施IList(楼层)

  • OnChange事件不是内置的,所以如果列表更改,DataGrid将不会更新(我认为?)

  • 我认为这可能是最好的选择,但我需要做什么来确保对FloorList集合或DataGrid的更改彼此同步?

3)inheritance自BindingList(Floor)

  • 添加/删除等方法不是虚拟的,因此我无法修改它们来更新楼层高程。

4)实现IBindingList

  • IBindinglist不是通用的,我只希望我的集合包含Floor对象

您应该为您的集合使用BindingList或实现IBindingList,因为这将通知DataGridView有关列表中的任何更改。

然后为Floor类实现INotifyPropertyChanged接口,这将允许在各个Floor项和DataGridView之间建立TwoWay绑定模式。

埃里克,你也可以这样做

public class MyFloorCollection : BindingList { public MyFloorCollection() : base() { this.ListChanged += new ListChangedEventHandler(MyFloorCollection_ListChanged); } void MyFloorCollection_ListChanged(object sender, ListChangedEventArgs e) { if (e.ListChangedType == ListChangedType.ItemAdded) { Floor newFloor = this[e.NewIndex] as Floor; if (newFloor != null) { newFloor.HeightChanged += new Floor.HeightChangedEventHandler(newFloor_HeightChanged); } } } void newFloor_HeightChanged(int newValue, int oldValue) { //recaluclate } } 

当然,您可以创建自己的HeightChangedEvent并订阅它,这样您就不必在if语句中使用属性名称。

所以你的Floor类看起来像这样

  public class Floor : INotifyPropertyChanged { private int _height; public int Height { get { return _height; } set { if (HeightChanged != null) HeightChanged(value, _height); _height = value; OnPropertyChanged("Height"); } } public int Elevation { get; set; } private void OnPropertyChanged(string property) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(property)); } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion public delegate void HeightChangedEventHandler(int newValue, int oldValue); public event HeightChangedEventHandler HeightChanged; } 

这样你只需要订阅你的HeightChanged变量,而不是订阅PropertyChanged。 PropertyChanged将被DataGridView使用以保持TwoWay绑定。 我相信这种方式更清洁。

您还可以更改委托并将项目作为发件人传递。

 public delegate void HeightChangedEventHandler(Floor sender, int newValue, int oldValue); 

编辑:要取消订阅HeightChanged事件,您需要覆盖RemoveItem

  protected override void RemoveItem(int index) { if (index > -1) this[index].HeightChanged -= newFloor_HeightChanged; base.RemoveItem(index); } 

实现IList(Floor),并让您的新集合也实现INotifyPropertyChanged接口。

您可以尝试在包含类的某些数据上公开ObservableCollection属性。 此外, Floor对象需要实现INotifyPropertyChanged以支持UI的双向绑定。