2

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

Floor系列代表了一系列相互堆叠的建筑楼层。每个楼层都有一个可读写的 Floor.Height 属性和一个只读的Floor.Elevation属性,该属性通过合计低于当前楼层的楼层高度来设置。因此,每当在集合中添加、删除、移动或更改楼层时,都需要更新Floor.Elevation属性。

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

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

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

海拔高度 P​​rogramType UnitType UnitCount  
15' 70' 豪华住宅 5
15' 55' 豪华住宅 5
15' 40' 住宅预算 10
20' 20' 零售 N/A 2
20' 0' 零售 N/A 3

我现在的问题是我应该以什么为基础来实现FloorList集合以实现此功能?我正在考虑的选项如下。

1)从列表继承(楼层)

  • 诸如添加/删除之类的方法不是虚拟的,因此我不能不覆盖它们来更新海拔

2) 实现 IList(Floor)

  • OnChange 事件未内置,因此如果列表更改 DataGrid 将不会更新(我认为?)

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

3) 继承自 BindingList(Floor)

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

4) 实现 IBindingList

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

3 回答 3

1

您应该为您的集合使用 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);
        }
于 2009-07-16T20:22:21.800 回答
0

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

于 2009-07-16T20:20:50.993 回答
0

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

于 2009-07-16T20:24:38.473 回答