PropertyGrid - Свойство, производное от IList ‹T›, Как добавить в PropertyGrid, чтобы пользователь мог добавлять / редактировать / удалять элементы

Позвольте мне рассказать немного о том, как я дошел до этого момента.

Изначально у меня было свойство в моем классе, производное от CollectionsBase, и эта коллекция была сопоставлена ​​с PropertyGrid, и пользователь мог добавлять / редактировать / удалять элементы из списка по своему усмотрению.

Однако я не мог сопоставить CollectionsBase с NHibernate, поэтому мне пришлось отказаться от своей первоначальной реализации, и вместо того, чтобы быть производным от CollectionsBase, у меня был класс, производный от IList.

Теперь я могу подключиться к NHibernate, но не могу редактировать коллекцию через PropertyGrid.

Мне нужна помощь, чтобы эти двое хорошо играли друг с другом.

В моем основном классе у меня есть свойство, определенное как:

    public virtual ZoneCollection Zones
    {
        get { return zones; }
        set { zones = value; }
    }

Моя коллекция зон, наследующая IList, определяется следующим образом:

public class ZoneCollection : IList<Zone>, ICustomTypeDescriptor
{
    private IList<Zone> _list;

    public IList<Zone> _List
    {
        get { return _list; }
    }

    public ZoneCollection()
    {
        _list = new List<Zone>();
    }

    #region Implementation of IEnumerable

    public IEnumerator<Zone> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion

    #region Implementation of ICollection<Zone>

    public void Add(Zone item)
    {
        _list.Add(item);
    }

    public void Clear()
    {
        _list.Clear();
    }

    public bool Contains(Zone item)
    {
        return _list.Contains(item);
    }

    public void CopyTo(Zone[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }

    public bool Remove(Zone item)
    {
        return _list.Remove(item);
    }

    public int Count
    {
        get { return _list.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    #endregion

    #region Implementation of IList<Zone>

    public int IndexOf(Zone item)
    {
        return _list.IndexOf(item);
    }

    public void Insert(int index, Zone item)
    {
        _list.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _list.RemoveAt(index);
    }

    public Zone this[int index]
    {
        get { return (Zone)_list[index]; }
        set { _list[index] = value; }
    }

    #endregion

    // Implementation of interface ICustomTypeDescriptor 
    #region ICustomTypeDescriptor impl

    public String GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public String GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return TypeDescriptor.GetDefaultProperty(this, true);
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public EventDescriptorCollection GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }


    /// <summary>
    /// Called to get the properties of this type. Returns properties with certain
    /// attributes. this restriction is not implemented here.
    /// </summary>
    /// <param name="attributes"></param>
    /// <returns></returns>
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    /// <summary>
    /// Called to get the properties of this type.
    /// </summary>
    /// <returns></returns>
    public PropertyDescriptorCollection GetProperties()
    {
        // Create a collection object to hold property descriptors
        PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

        // Iterate the list of zones
        for (int i = 0; i < this._list.Count; i++)
        {
            // Create a property descriptor for the zone item and add to the property descriptor collection
            ZoneCollectionPropertyDescriptor pd = new ZoneCollectionPropertyDescriptor(this, i);
            pds.Add(pd);
        }
        // return the property descriptor collection
        return pds;
    }

    #endregion
}

/// <summary>
/// Summary description for CollectionPropertyDescriptor.
/// </summary>
public class ZoneCollectionPropertyDescriptor : PropertyDescriptor
{
    private ZoneCollection collection = null;
    private int index = -1;

    public ZoneCollectionPropertyDescriptor(ZoneCollection coll, int idx) :
        base("#" + idx.ToString(), null)
    {
        this.collection = coll;
        this.index = idx;
    }

    public override AttributeCollection Attributes
    {
        get
        {
            return new AttributeCollection(null);
        }
    }

    public override bool CanResetValue(object component)
    {
        return true;
    }

    public override Type ComponentType
    {
        get
        {
            return this.collection.GetType();
        }
    }

    public override string DisplayName
    {
        get
        {
            Zone zone = this.collection[index];
            return zone.ID.ToString();
        }
    }

    public override string Description
    {
        get
        {
            Zone zone = this.collection[index];
            StringBuilder sb = new StringBuilder();
            sb.Append(zone.ID.ToString());

            if (zone.Streets.Route != String.Empty || zone.Streets.Crossing != String.Empty)
                sb.Append("::");
            if (zone.Streets.Route != String.Empty)
                sb.Append(zone.Streets.Route);
            if (zone.Streets.Crossing != String.Empty)
            {
                sb.Append(" and ");
                sb.Append(zone.Streets.Crossing);
            }

            return sb.ToString();
        }
    }

    public override object GetValue(object component)
    {
        return this.collection[index];
    }

    public override bool IsReadOnly
    {
        get { return false; }
    }

    public override string Name
    {
        get { return "#" + index.ToString(); }
    }

    public override Type PropertyType
    {
        get { return this.collection[index].GetType(); }
    }

    public override void ResetValue(object component)
    {
    }

    public override bool ShouldSerializeValue(object component)
    {
        return true;
    }

    public override void SetValue(object component, object value)
    {
        // this.collection[index] = value;
    }
}

}

Теперь мои ICustomTypeDescriptor и PropertyDescriptor работали нормально, когда этот класс был производным от CollectionsBase, но теперь он просто показывает имя класса ZoneCollection в имени свойства без кнопки «...» для добавления / редактирования / удаления элементов из списка.

Что я делаю неправильно теперь, когда от IList унаследовано, что это не работает?

Если я добавлю:

[TypeConverter(typeof(ExpandableObjectConverter))]

В начале ZoneCollection я получаю элементы в списке, перечисленные в расширяемом дереве, но это не то, что я ищу. Куда делась кнопка «...», которая открывала всплывающее окно, которое позволяло мне добавлять / редактировать / удалять элементы в коллекции, когда я унаследовал от IList вместо CollectionBase?


person Nathan Raley    schedule 15.09.2011    source источник


Ответы (1)


PropertyGrid - старый сварливый зверь. Ему нужна явная реализация необщего IList, а не общий.

В качестве примечания к сайту, вы можете получить ZoneCollection непосредственно из List<Zone>, и вам не нужен ICustomTypeDescriptor / PropertyDescriptor в связи с этой проблемой PropertyGrid.

Вот реализация, которая, кажется, работает:

public class ZoneCollection : IList<Zone>, IList
{
    private List<Zone> _list = new List<Zone>();

    public ZoneCollection()
    {
    }

    public int IndexOf(Zone item)
    {
        return _list.IndexOf(item);
    }

    public void Insert(int index, Zone item)
    {
        _list.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _list.RemoveAt(index);
    }

    public Zone this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            _list[index] = value;
        }
    }

    public void Add(Zone item)
    {
        _list.Add(item);
    }

    public void Clear()
    {
        _list.Clear();
    }

    public bool Contains(Zone item)
    {
        return _list.Contains(item);
    }

    public void CopyTo(Zone[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _list.Count; }
    }

    public bool IsReadOnly
    {
        get { return ((IList)_list).IsReadOnly; }
    }

    public bool Remove(Zone item)
    {
        return _list.Remove(item);
    }

    public IEnumerator<Zone> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    int IList.Add(object value)
    {
        int index = Count;
        Add((Zone)value);
        return index;
    }

    bool IList.Contains(object value)
    {
        return Contains((Zone)value);
    }

    int IList.IndexOf(object value)
    {
        return IndexOf((Zone)value);
    }

    void IList.Insert(int index, object value)
    {
        Insert(index, (Zone)value);
    }

    bool IList.IsFixedSize
    {
        get { return ((IList)_list).IsFixedSize; }
    }

    bool IList.IsReadOnly
    {
        get { return ((IList)_list).IsReadOnly; }
    }

    void IList.Remove(object value)
    {
        Remove((Zone)value);
    }

    object IList.this[int index]
    {
        get
        {
            return this[index];
        }
        set
        {
            this[index] = (Zone)value;
        }
    }

    void ICollection.CopyTo(Array array, int index)
    {
        CopyTo((Zone[])array, index);
    }

    bool ICollection.IsSynchronized
    {
        get { return ((ICollection)_list).IsSynchronized; }
    }

    object ICollection.SyncRoot
    {
        get { return ((ICollection)_list).SyncRoot; }
    }
}
person Simon Mourier    schedule 15.09.2011
comment
Да, но NHibernate требует IList, а не List. Разве я не использовал неконструктивную реализацию IList с помощью IList ‹Zone›? Или мне нужно определить его как производный от IList, а затем установить в нем мой список определенного типа? - person Nathan Raley; 15.09.2011
comment
@Nathan - для кода, который будет выполнять приведение (как есть), он будет работать, он реализован, но код PropertyGrid использует отражение и просто проверяет, что IList реализован непосредственно классом. Вы можете сохранить общую реализацию, но вам нужно просто добавить IList (и реализовать его) - person Simon Mourier; 15.09.2011
comment
Хорошо, вместо того, чтобы наследовать от IList ‹Zone›, я получил от IList. В конструкторе ZonesCollection я установил для IList _list значение new List ‹Zone› (). Теперь это перечисляет Зону свойств в PropertyGrid с помощью кнопки ...; однако свойства Зоны в редакторе коллекции не отображаются. Любые идеи? - person Nathan Raley; 16.09.2011
comment
@Nathan - не уверен, почему он не работает без полного исходного кода. Однако я обновил свой ответ. - person Simon Mourier; 16.09.2011
comment
Как именно добавить IList (и реализовать его) в этом случае? - person Nathan Raley; 16.09.2011
comment
Спасибо, в этом есть смысл. Я не знал, что смогу сделать это, имея общую и не общую реализацию. Мне пришлось изменить Список ‹Zone› _list = new List ‹Zone› () в IList ‹Zone› _list = новый список ‹Zone› (), чтобы NHibernate сохранил элемент, но он отлично работает. Спасибо! - person Nathan Raley; 16.09.2011