PropertyGrid, как вы добавляете редактируемый список <Class>? - PullRequest
1 голос
/ 14 сентября 2011

У меня есть класс MyClassA, у которого есть свойство IList.Я использую элемент управления PropertyGrid для отображения всех свойств MyClassA, и мне бы хотелось, чтобы список MyClassB отображался и редактировался с помощью PropertyGrid для MyClassA.

В настоящее время все другие свойства отображаются в свойствесетка за исключением свойства, которое является списком MyClassB.Как я могу добавить Список MyClassB в сетку свойств, где пользователь может добавлять / редактировать / удалять элементы из Списка?

Мне не удалось найти ни одного примера, который подробно описывал быэто пока, хотя я все еще копаю.

Ответы [ 2 ]

1 голос
/ 15 сентября 2011

Вот решение, которое я разработал до сих пор, хотя оно все еще не соответствует 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;
    }
}

Пересечение теперь содержит коллекцию ZoneCollection вместо IList, и теперь я могу редактировать / добавлять / удалять зоны, содержащиеся в коллекции.

Теперь, если бы я мог сделать это более общим, я был бы относительно счастлив. Еще одним препятствием для моей модели является то, что мне пришлось наследовать от базы Collection, используя это, а не IList. Это полностью сломало мое отображение моего класса для NHibernate, и теперь мне нужно попытаться выяснить, как переназначить этот список, используя метод, упомянутый выше.

Если кто-то захочет уточнить это дальше, я был бы очень признателен за еще немного понимания.

0 голосов
/ 12 марта 2014

Я знаю, что этой теме более 2 лет, но, возможно, это может быть интересно для вас.

У меня была похожая проблема. Начиная с: мне нужна точка в 3D-пространстве, которая должна быть настраиваемой в Property-Grid Для этого я создал Класс Koord. Чтобы сделать его изменяемым в PropertyGrid, я создал новый класс "KoordConverter: TypeConverter" Это используется в Vexel (проверьте Википедию, чтобы узнать, для чего она нужна :-))

Для создания TestBock (некоторый 3D-объект) я использую Список Векселей. К сожалению, мне нужен список TestBlocks в моей программе, видимый через 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 решило эту проблему (вы можете найти TypeConverter под кодом Koord)

[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 или Vexels внутри каждого TestBlock) Надеюсь, это поможет кому-то, если он перешагнет эту тему.

С наилучшими пожеланиями

Робин Блад

PS: Редактирование не является проблемой в PropertyGrid, может быть, вы просто не правильно поняли свои конструкторы !? http://i.stack.imgur.com/LD3zf.png

...