PropertyGrid如何添加可编辑的List <class>?</class>

时间:2011-09-13 20:43:50

标签: c# .net propertygrid

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

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

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

2 个答案:

答案 0 :(得分: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 base继承。这完全破坏了我的NHibernate类的映射,我现在不得不尝试使用上面提到的方法重新映射这个列表。

如果有人想进一步阐述这一点,我会非常感谢更多的见解。

答案 1 :(得分:0)

我知道这个主题已经超过2年了,但也许这对你来说很有意思。

我遇到了类似的问题。 从:我需要一个3D空间中的Point,它应该可以在Property-Grid中配置 为此,我创建了一个Class Koord。为了使它在PropertyGrid中可以更改,我创建了一个新的类“KoordConverter:TypeConverter” 这用于Vexel(检查维基百科以找出它的用途:-))

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

开始最顶层:

public partial class FormMain : Form
{
    private BlockProperties _bp = new BlockProperties();

    public FormMain()
    {
        InitializeComponent();
        pgProperties.SelectedObject = _bp;
    }
[...]
}

Class 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。 Class非常简单,但在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