【问题标题】:PropertyGrid - Property derived from IList<T>, How do I add to the PropertyGrid so user can Add/Edit/Remove itemsPropertyGrid - 从 IList<T> 派生的属性,如何添加到 PropertyGrid 以便用户可以添加/编辑/删除项目
【发布时间】:2011-09-15 18:27:37
【问题描述】:

让我提供一些关于我是如何达到这一点的历史。

我的类中最初有一个从 CollectionsBase 派生的属性,并将此集合映射到 PropertyGrid,用户可以随意从列表中添加/编辑/删除项目。

但是,我无法将 CollectionsBase 与 NHibernate 映射,因此我不得不放弃我的初始实现,而不是从 CollectionsBase 派生,我让类派生自 IList。

现在我可以映射到 NHibernate,但我无法通过 PropertyGrid 编辑集合。

我需要一些帮助才能让两人相处融洽。

在我的主类中,我有一个属性定义为:

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

继承IList的我的Zone Collection定义如下:

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

}

现在,当此类从 CollectionsBase 派生时,我的 ICustomTypeDescriptor 和 PropertyDescriptor 工作正常,但现在它只在属性名称中显示类名 ZoneCollection,而没有“...”按钮来添加/编辑/删除列表中的项目.

现在我做错了什么,因为它是从 IList 继承的,这不起作用?

如果我添加:

[TypeConverter(typeof(ExpandableObjectConverter))]

在 ZoneCollection 的开头,我将列表中的项目列在一个可展开的树中,但这不是我想要的。当我从 IList 而不是 CollectionBase 继承时,“...”按钮在哪里打开了一个弹出窗口,使我能够添加/编辑/删除集合中的项目?

【问题讨论】:

    标签: c# collections propertygrid collectioneditor icustomtypedescriptor


    【解决方案1】:

    PropertyGrid 是一只脾气暴躁的老怪物。它需要非泛型 IList 显式实现,而不是泛型实现。

    作为网站说明,您可以直接从 List&lt;Zone&gt; 派生 ZoneCollection,对于此 PropertyGrid 问题,您不需要任何 ICustomTypeDescriptor / PropertyDescriptor。

    这是一个似乎可行的实现:

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

    【讨论】:

    • 是的,但是 NHibernate 需要 IList 而不是 List。我没有使用 IList 的非通用 IList 实现吗?还是我必须将其定义为从 IList 派生,然后将其中的列表设置为特定类型?
    • @Nathan - 对于可以进行强制转换的代码(原样),它会工作,它 已实现,但 PropertyGrid 代码使用反射并仅检查 IList由类直接实现。您可以保留通用实现,但您只需要添加 IList(并实现它)
    • 好吧,我没有从 IList 派生,而是从 IList 派生。在 ZonesCollection 的构造函数中,我将 IList _list 设置为等于 new List()。现在使用“...”按钮在 PropertyGrid 中列出属性 Zone;但是,不会显示集合编辑器中区域的属性。有什么想法吗?
    • @Nathan - 不知道为什么它在没有完整源代码的情况下不起作用。我已经更新了我的答案。
    • 在这种情况下,您将如何“只添加 IList(并实现它)”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多