Создание пользовательской коллекции, которая может быть связана с DataGrid

2

Я работаю в фирме по архитектуре, и я создаю плагин для программы 3D-моделирования, чтобы помочь дизайну. У меня есть класс Building и класс Этаж. В здании содержится ссылка на коллекцию этажей FloorList. Я пытаюсь выяснить, на чем основать коллекцию FloorList, чтобы я мог свести к минимуму объем работы, которую мне нужно сделать, чтобы создать интерфейс для редактирования коллекции.

Коллекция Этаж представляет собой серию строительных этажей, которые сложены друг над другом. Каждый Этаж имеет свойство Floor.Height, которое считывает запись, и свойство Floor.Elevation, которое доступно только для чтения и задается путем суммирования пола высота ниже текущего Этаж. Поэтому при добавлении, удалении, перемещении или изменении Этаж в коллекции необходимо обновить свойства Floor.Elevation.

Кроме того, я хочу создать пользовательский интерфейс для редактирования этой коллекции. Я думал об использовании элемента DataGrid, где каждый Этаж указан с его Высота и другими свойствами в виде строки элемента управления. Пользователь должен иметь возможность добавлять, удалять и повторно заказывать полы с помощью элемента управления. Я бы хотел, чтобы это было максимально простым и гибким. Значение Я хотел бы просто привязать коллекцию Floors к DataGrid и иметь столбцы DataGrid, на основе свойств Этаж. Если возможно, я бы хотел использовать встроенный интерфейс Add/Remove UI элемента управления DataGrid, не связавшись с настройкой связки событийных связей между моей коллекцией и DataGrid.

Чтобы усложнить ситуацию в будущем, мне нужно будет позволить пользователю динамически добавлять пользовательские свойства в Floors, которые я хочу, чтобы они могли видеть и редактировать в DataGrid. Я думаю, что в итоге это сделаю, если класс Этаж реализует 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) Наследовать из списка (этаж)

  • Методы, такие как Add/Remove, не являются витруальными, и поэтому я не могу их переопределить, чтобы обновить отметки.

2) Внедрить IList (этаж)

  • Событие OnChange не встроено, поэтому, если в списках изменения DataGrid не будут обновляться (я думаю?)

  • Я думаю, что это, вероятно, лучший вариант, но что мне нужно сделать, чтобы изменения в коллекции FloorList или DataGrid синхронизировались друг с другом

3) Наследовать от BindingList (этаж)

  • Метод добавления/удаления не является виртуальным, поэтому я не могу их модифицировать, чтобы обновить высоты пола.

4) Внедрение IBindingList

  • IBindinglist не является общим, и я хочу, чтобы в моей коллекции содержались Пол.
Теги:
list
collections
binding
bindinglist

3 ответа

1
Лучший ответ

Вы должны использовать BindingList для своей коллекции или реализовать IBindingList, поскольку это уведомит DataGridView о любых изменениях в списке.

Затем реализуем интерфейс INotifyPropertyChanged для класса Floor, это позволит использовать режим привязки TwoWay между отдельными элементами 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. PropertyChanged будет использоваться DataGridView для сохранения привязки TwoWay. Я верю, что этот путь чище.

Вы также можете изменить делегат и передать элемент в качестве отправителя.

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

EDIT: для отмены подписки на событие HeightChanged вам необходимо переопределить RemoveItem

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

            base.RemoveItem(index);
        }
  • 0
    Но IBindingList не является универсальным, и я бы предпочел не делать кучу приведения типов объектов к моему типу пола.
  • 0
    Эрик, я обновил свой ответ.
Показать ещё 4 комментария
0

Вы можете попробовать разоблачить свойство ObservableCollection<Floor> для некоторых данных, содержащих класс. Кроме того, для объекта Floor необходимо реализовать INotifyPropertyChanged для поддержки двусторонней привязки от пользовательского интерфейса.

0

Внедрите IList (Floor), и ваша новая коллекция также реализует INotifyPropertyChanged интерфейс.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню