我在一家建筑公司工作,正在为3D建模程序创建一个插件来协助设计。我有一个建筑班和一个地板班。该建筑物包含对楼层的FloorList集合的引用。我正在尝试弄清FloorList集合的基础,以便可以最大程度地减少创建界面以编辑集合的工作量。

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

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

为了使将来的事情变得更加复杂,我将需要允许用户将自定义属性动态添加到Floors中,希望它们也可以在DataGrid中查看和编辑。我想我最终要通过让Floor类实现IExtenderProvider来做到这一点。因此,最终,DataGrid看起来像这样:

初始属性将来的自定义用户属性

高度标高ProgramType UnitType UnitCount
15'70'住宅豪华5
15'55'住宅豪华5
15'40'住宅预算10
20'20'零售N / A 2
20'0'零售N / A 3


现在我的问题是我应该将FloorList集合作为基础以允许此功能吗?我正在考虑的选项如下。

1)继承自List(Floor)


诸如“添加/删除”之类的方法并不重要,因此我无法覆盖它们来更新高程


2)实现IList(Floor)


OnChange事件不是内置的,因此如果列表发生更改,DataGrid将不会更新(我认为?)
我认为这可能是最好的选择,但是我需要做些什么来确保对FloorList集合或DataGrid所做的更改彼此同步?


3)从BindingList(Floor)继承


诸如“添加/删除”之类的方法不是虚拟的,因此我无法对其进行修改以更新地面高程。


4)实现IBindingList


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

最佳答案

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

然后为Floor类实现INotifyPropertyChanged接口,这将允许您的单个Floor项目与DataGridView之间建立双向绑定模式。

埃里克,你也可以做这样的事情

   public class MyFloorCollection : BindingList<Floor>
            {
                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。 DataGridView将使用PropertyChanged来保持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);
        }

09-11 03:34
查看更多