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

时间:2009-07-16 19:57:05

标签: c# list collections binding bindinglist

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

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

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

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

Initial Properties      Future Custom User Properties

Height    Elevation     ProgramType    UnitType  UnitCount  
15'       70'           Residential    Luxury    5
15'       55'           Residential    Luxury    5
15'       40'           Residential    Budget    10
20'       20'           Retail         N/A       2
20'       0'            Retail         N/A       3

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

1)从列表(楼层)继承

  • 添加/删除等方法不是玻璃的,因此我无法覆盖它们以更新高程

2)实施IList(发言人)

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

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

3)继承自BindingList(Floor)

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

4)实施IBindingList

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

3 个答案:

答案 0 :(得分:1)

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

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

Eric,你也可以这样做

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

答案 1 :(得分:0)

实施IList(Floor),让您的新集合也实现INotifyPropertyChanged界面。

答案 2 :(得分:0)

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