【问题标题】:PropertyGrid how do you add an editable List<Class>?PropertyGrid 如何添加可编辑的 List<Class>?
【发布时间】:2011-09-13 20:43:50
【问题描述】:

我有一个具有 IList 属性的类 MyClassA。我正在使用 PropertyGrid 控件来显示 MyClassA 的所有属性,我希望通过 MyClassA 的 PropertyGrid 显示和编辑 MyClassB 的列表。

我目前在“属性”网格中显示了所有其他属性,但 MyClassB 列表中的属性除外。如何将 MyClassB 的列表添加到用户可以从列表中添加/编辑/删除项目的属性网格中?

尽管我仍在挖掘,但我还没有真正找到任何详细说明此问题的示例。

【问题讨论】:

  • 所以您在列表中有一个列表,并且您希望使用某种自定义编辑器为您的 pGrid 显示该列表?
  • 或多或少。我的 Main 中有一个 MyClassA 列表。 MyClassA 的列表绑定到 pGrid。每个 MyClassA 都有一个与之关联的 MyClassB 列表。我想在当前显示的 MyClassA 的列表中显示所有 MyClassB,并允许用户编辑与其关联的每个 MyClassB 或将新的 MyClassB 添加到 MyClassA 中包含的 MyClassB 列表中。这有意义吗?
  • 这种自定义需要您为网格创建自定义编辑器,当给定特定类型的属性时,它会使用自定义用户控件显示它们。如果我理解正确的话。
  • 更贴近上下文。我有一个交叉点列表。每个路口都有一个与之关联的区域列表。 Intersection 绑定到 PropertyGrid。我希望显示 Zones 属性,它是一个 IList,具有以下功能:编辑或删除当前包含在区域列表中的任何区域和/或将新区域添加到区域列表中。
  • 如何去做这件事?您知道任何参考资料显示如何实现这一目标吗?

标签: c# .net propertygrid


【解决方案1】:

这是我迄今为止制定的一个解决方案,尽管它仍然不能 100% 符合我的要求。

我发现此参考可根据自己的喜好进行修改:http://www.codeproject.com/KB/tabs/customizingcollectiondata.aspx

我所做的是创建一个继承自 CollectionBase 并使用 ICustomTypeDescriptor 的新类。

完成此操作并实现基本功能后,我必须为该类创建一个 PropertyDescriptor。

代码如下:

public class ZoneCollection : CollectionBase, ICustomTypeDescriptor
{
    #region Collection Implementation

    /// <summary>
    /// Adds an zone object to the collection
    /// </summary>
    /// <param name="emp"></param>
    public void Add(Zone zone)
    {
        this.List.Add(zone);
    }

    /// <summary>
    /// Removes an zone object from the collection
    /// </summary>
    /// <param name="emp"></param>
    public void Remove(Zone zone)
    {
        this.List.Remove(zone);
    }

    /// <summary>
    /// Returns an zone object at index position.
    /// </summary>
    public Zone this[int index]
    {
        get
        {
            return (Zone)this.List[index];
        }
    }

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

Intersection 现在包含 ZoneCollection 而不是 IList,我现在可以编辑/添加/删除集合中包含的区域。

现在,如果我能让这个更通用,我会比较高兴。我的模型的另一个障碍是我必须使用它而不是 IList 从 Collection 库继承。这完全破坏了我对 NHibernate 类的映射,我现在不得不尝试找出如何使用上述方法重新映射此列表。

如果有人想进一步详细说明这一点,我将不胜感激。

【讨论】:

    【解决方案2】:

    我知道这个主题已有 2 年多的历史了,但也许这对你来说可能很有趣。

    我遇到了类似的问题。 开始:我需要一个 3D 空间中的点,它应该可以在 Property-Grid 中配置 为此,我创建了一个 Class Koord。为了使其在 PropertyGrid 中可更改,我创建了一个新类“KoordConverter:TypeConverter” 这在 Vexel 中使用(查看 Wikipedia 以了解它的用途:-))

    为了创建一个 TestBock(一些 3D 对象),我使用了一个 Vexels 列表。 不幸的是,我的程序中需要一个 TestBlock 列表,可以通过 Property-Grid 看到。

    从 Topmost 开始:

    public partial class FormMain : Form
    {
        private BlockProperties _bp = new BlockProperties();
    
        public FormMain()
        {
            InitializeComponent();
            pgProperties.SelectedObject = _bp;
        }
    [...]
    }
    

    类 BlockProperties 包括 TestBocks 列表,我填写了一些内容以向您展示里面的内容。

    class BlockProperties
    {
        public List<TestBocks> Testing { get; set; }
    
        public BlockProperties()
        {
            Testing = new List<TestBocks>(3);
    
            List<Vexel> t1 = new List<Vexel>(1);
            t1.Add(new Vexel(new Koord(1,0,1), 1));
    
            List<Vexel> t2 = new List<Vexel>(2);
            t2.Add(new Vexel(new Koord(2, 0, 1), 2));
            t2.Add(new Vexel(new Koord(2, 0, 2), 2));
    
            List<Vexel> t3 = new List<Vexel>(3);
            t3.Add(new Vexel(new Koord(3, 0, 1), 3));
            t3.Add(new Vexel(new Koord(3, 0, 2), 3));
            t3.Add(new Vexel(new Koord(3, 0, 3), 3));
    
            TestBocks tb1 = new TestBocks();
            tb1.Koords = t1;
    
            TestBocks tb2 = new TestBocks();
            tb2.Koords = t2;
    
            TestBocks tb3 = new TestBocks();
            tb3.Koords = t3;
    
            Testing.Add(tb1);
            Testing.Add(tb2);
            Testing.Add(tb3);
        [...]
        }
    [...]
    }
    

    接下来是我的 TestBlock 类,很简单

    [Serializable]
    public class TestBocks
    {
        public List<Vexel> Vexels{ get; set; }
        public TestBocks()
        {
            Vexels = new List<Vexel>();
        }
    }
    

    在 Vexels 中是我的程序所需的大部分魔法: 我什至在这里放了一个 ToString() 以方便调试。

    public class Vexel
    {
        private Koord _origin;
        private double _extent;
    
        public Koord Origin { get { return _origin; }  set { _origin = value; } }
    
        public double Extent { get { return _extent; } set { _extent = value; } }
    
        public string ToString()
        {
            NumberFormatInfo nFormatInfo = new NumberFormatInfo
            {
                NumberDecimalSeparator = ".",
                NumberGroupSeparator = ""
            }; 
            return String.Format(nFormatInfo, "Origin;{0};{1};{2};Extent;{3}", _origin.X, _origin.Y, _origin.Z, _extent);
        }
    
        public Vexel()
        {
            _origin = new Koord(0,0,0);
            Extent = 0;
        }
    
        public Vexel(Koord origin, double extent)
        {
            //TODO do some checking
            _origin = origin;
            _extent = extent;
        }
    

    到目前为止,PropertyGrid 一切正常,但我无法编辑 Koords。 该类非常简单,但在 PropertyGrid 中不可编辑。 添加一个TypeConverterClass解决了这个问题(你可以在Koord的代码下面找到TypeConverter)

    [TypeConverter(typeof(KoordConverter))]
    [Serializable]
    public class Koord
    {
        private double p_1;
        private double p_2;
        private double p_3;
    
        public Koord(double x, double y, double z)
        {
            this.p_1 = x;
            this.p_2 = y;
            this.p_3 = z;
        }
    
        public string ToString()
        {
            return String.Format("X;{0};Y;{1};Z;{2}", p_1, p_2, p_3);
        }
    
        public double X { get { return p_1; } }
        public double Y { get { return p_2; } }
        public double Z { get { return p_3; } }
    }
    

    Typeconverter 是最复杂的代码。您可以在下面找到它:

    public class KoordConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }
    
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
        }
    
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            string text = value as string;
            if (text == null)
            {
                return base.ConvertFrom(context, culture, value);
            }
            string text2 = text.Trim();
            if (text2.Length == 0)
            {
                return null;
            }
            if (culture == null)
            {
                culture = CultureInfo.CurrentCulture;
            }
            char c = culture.TextInfo.ListSeparator[0];
            string[] array = text2.Split(new char[]
            {
                c
            });
            int[] array2 = new int[array.Length];
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
            for (int i = 0; i < array2.Length; i++)
            {
                array2[i] = (int)converter.ConvertFromString(context, culture, array[i]);
            }
            if (array2.Length == 3)
            {
                return new Koord(array2[0], array2[1], array2[2]);
            }
            throw new ArgumentException("TextParseFailedFormat");
        }
    
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (value is Koord)
            {
                if (destinationType == typeof(string))
                {
                    Koord Koord = (Koord)value;
                    if (culture == null)
                    {
                        culture = CultureInfo.CurrentCulture;
                    }
                    string separator = culture.TextInfo.ListSeparator + " ";
                    TypeConverter converter = TypeDescriptor.GetConverter(typeof(int));
                    string[] array = new string[3];
                    int num = 0;
                    array[num++] = converter.ConvertToString(context, culture, Koord.X);
                    array[num++] = converter.ConvertToString(context, culture, Koord.Y);
                    array[num++] = converter.ConvertToString(context, culture, Koord.Z);
                    return string.Join(separator, array);
                }
                if (destinationType == typeof(InstanceDescriptor))
                {
                    Koord Koord2 = (Koord)value;
                    ConstructorInfo constructor = typeof(Koord).GetConstructor(new Type[]
                    {
                        typeof(double),
                        typeof(double),
                        typeof(double)
                    });
                    if (constructor != null)
                    {
                        return new InstanceDescriptor(constructor, new object[]
                        {
                            Koord2.X,
                            Koord2.Y,
                            Koord2.Z
                        });
                    }
                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    
        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            if (propertyValues == null)
            {
                throw new ArgumentNullException("propertyValues");
            }
            object obj = propertyValues["X"];
            object obj2 = propertyValues["Y"];
            object obj3 = propertyValues["Z"];
            if (obj == null || obj2 == null || obj3 == null || !(obj is double) || !(obj2 is double) || !(obj3 is double))
            {
                throw new ArgumentException("PropertyValueInvalidEntry");
            }
            return new Koord((double)obj, (double)obj2, (double)obj3);
        }
    
        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
    
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(Koord), attributes);
            return properties.Sort(new string[]
            {
                "X",
                "Y",
                "Z"
            });
        }
    
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
    }
    

    基本上在所有这些设置之后,修改任何对象列表(TestBlocks或每个TestBlock中的Vexels)都不是问题 如果他们跨过这个线程,希望它对某人有所帮助。

    最好的问候

    知更鸟

    PS:在 PropertyGrid 中编辑没有问题,也许你只是没有让你的构造函数正确!? http://i.stack.imgur.com/LD3zf.png

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多