Как отобразить динамический объект в сетке свойств? - PullRequest
4 голосов
/ 16 августа 2010

У меня есть пользовательский тип объекта, который должен быть редактируемым в PropertyGrid:

public class CustomObjectType
{
    public string Name { get; set; }        
    public List<CustomProperty> Properties {get; set;}
}

, который имеет список пользовательских свойств:

public class CustomProperty
{
    public string Name { get; set; }
    public string Desc { get; set; }
    public Object DefaultValue { get; set; }    
    Type type;

    public Type Type
    {
        get
        {
            return type;
        }
        set
        {
                type = value;
                DefaultValue = Activator.CreateInstance(value);
        }              
    }
}

Основная проблема здесьчто элемент управления PropertyGrid не позволяет редактировать и не использует соответствующие инструменты редактирования для свойства DefaultValue, для которого предварительно создается значение поля CustomProperty Type.

Тип DefaultValue известен только во время выполнения.

Кроме того, мне нужно указать пользовательский TypeConverter для свойства CustomProperty Type, чтобы отобразить раскрывающийся списокподдерживаемых типов (например, Int, String, Color, MyOwnClass).

Как мне это сделать?

Ответы [ 3 ]

17 голосов
/ 16 августа 2010

Чтобы пройти по этому маршруту, вам нужно создать пользовательский PropertyDescriptor для каждого свойства. Затем вы выставите это через пользовательский TypeConverter или (альтернативно) ICustomTypeDescriptor / TypeDescriptionProvider. Пример:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
[TypeConverter(typeof(CustomObjectType.CustomObjectConverter))]
public class CustomObjectType
{
    [Category("Standard")]
    public string Name { get; set; }
    private readonly List<CustomProperty> props = new List<CustomProperty>();
    [Browsable(false)]
    public List<CustomProperty> Properties { get { return props; } }

    private Dictionary<string, object> values = new Dictionary<string, object>();

    public object this[string name]
    {
        get { object val; values.TryGetValue(name, out val); return val; }
        set { values.Remove(name); }
    }

    private class CustomObjectConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var stdProps = base.GetProperties(context, value, attributes);
            CustomObjectType obj = value as CustomObjectType;
            List<CustomProperty> customProps = obj == null ? null : obj.Properties;
            PropertyDescriptor[] props = new PropertyDescriptor[stdProps.Count + (customProps == null ? 0 : customProps.Count)];
            stdProps.CopyTo(props, 0);
            if (customProps != null)
            {
                int index = stdProps.Count;
                foreach (CustomProperty prop in customProps)
                {
                    props[index++] = new CustomPropertyDescriptor(prop);
                }
            }
            return new PropertyDescriptorCollection(props);
        }
    }
    private class CustomPropertyDescriptor : PropertyDescriptor
    {
        private readonly CustomProperty prop;
        public CustomPropertyDescriptor(CustomProperty prop) : base(prop.Name, null)
        {
            this.prop = prop;
        }
        public override string Category { get { return "Dynamic"; } }
        public override string Description { get { return prop.Desc; } }
        public override string Name { get { return prop.Name; } }
        public override bool ShouldSerializeValue(object component) { return ((CustomObjectType)component)[prop.Name] != null; }
        public override void ResetValue(object component) { ((CustomObjectType)component)[prop.Name] = null; }
        public override bool IsReadOnly { get { return false; } }
        public override Type PropertyType { get { return prop.Type; } }
        public override bool CanResetValue(object component) { return true; }
        public override Type ComponentType { get { return typeof(CustomObjectType); } }
        public override void SetValue(object component, object value) { ((CustomObjectType)component)[prop.Name] = value; }
        public override object GetValue(object component) { return ((CustomObjectType)component)[prop.Name] ?? prop.DefaultValue; }
    }
}


public class CustomProperty
{
    public string Name { get; set; }
    public string Desc { get; set; }
    public object DefaultValue { get; set; }
    Type type;

    public Type Type
    {
        get
        {
            return type;
        }
        set
        {
                type = value;
                DefaultValue = Activator.CreateInstance(value);
        }              
    }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        var obj = new CustomObjectType
        {
            Name = "Foo",
            Properties =
            {
                new CustomProperty { Name = "Bar", Type = typeof(int), Desc = "I'm a bar"},
                new CustomProperty { Name = "When", Type = typeof(DateTime), Desc = "When it happened"},
            }
        };
        Application.Run(new Form { Controls = { new PropertyGrid { SelectedObject = obj, Dock = DockStyle.Fill } } });
    }
}
2 голосов
/ 16 августа 2010

Я думаю, что Марк Гравелл, возможно, немного неправильно понял контекст.

Я пытался редактировать свойства CustomObjectTypes, а не сами "CustomObjects".

Вот модифицированный код Марка, который делает это:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

public class CustomObjectType
{
    [Category("Standard")]
    public string Name { get; set; }
    [Category("Standard")]
    public List<CustomProperty> Properties {get;set;}

    public CustomObjectType()
    {
        Properties = new List<CustomProperty>();
    }
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public class Person
{
    public string Name {get;set;}
    public DateTime DateOfBirth { get; set; }
    public int Age { get; set; }
}

[TypeConverter(typeof(CustomProperty.CustomPropertyConverter))]
public class CustomProperty
{
    public CustomProperty()
    {
        Type = typeof(int);
        Name = "SomeProperty";    
    }

    private class CustomPropertyConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var stdProps = base.GetProperties(context, value, attributes);
            CustomProperty obj = value as CustomProperty;            
            PropertyDescriptor[] props = new PropertyDescriptor[stdProps.Count + 1];
            stdProps.CopyTo(props, 0);
            props[stdProps.Count] = new ObjectDescriptor(obj);

            return new PropertyDescriptorCollection(props);
        }
    }
    private class ObjectDescriptor : PropertyDescriptor
    {
        private readonly CustomProperty prop;
        public ObjectDescriptor(CustomProperty prop)
            : base(prop.Name, null)
        {
            this.prop = prop;
        }
        public override string Category { get { return "Standard"; } }
        public override string Description { get { return "DefaultValue"; } }
        public override string Name { get { return "DefaultValue"; } }
        public override string DisplayName { get { return "DefaultValue"; } }
        public override bool ShouldSerializeValue(object component) { return ((CustomProperty)component).DefaultValue != null; }
        public override void ResetValue(object component) { ((CustomProperty)component).DefaultValue = null; }
        public override bool IsReadOnly { get { return false; } }
        public override Type PropertyType { get { return prop.Type; } }
        public override bool CanResetValue(object component) { return true; }
        public override Type ComponentType { get { return typeof(CustomProperty); } }
        public override void SetValue(object component, object value) { ((CustomProperty)component).DefaultValue = value; }
        public override object GetValue(object component) { return ((CustomProperty)component).DefaultValue; }
    }

    private class CustomTypeConverter: TypeConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;

            return base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value.GetType() == typeof(string))
            {
                Type t = Type.GetType((string)value);

                return t;
            }

            return base.ConvertFrom(context, culture, value);

        }

        public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            var types = new Type[] { 
                typeof(bool), 
                typeof(int), 
                typeof(string), 
                typeof(float),
                typeof(Person),
                typeof(DateTime)};

            TypeConverter.StandardValuesCollection svc =
                new TypeConverter.StandardValuesCollection(types);
            return svc;
        }
    }

    [Category("Standard")]
    public string Name { get; set; }
    [Category("Standard")]
    public string Desc { get; set; }

    [Browsable(false)]

    public object DefaultValue { get; set; }

    Type type;

    [Category("Standard")]
    [TypeConverter(typeof(CustomTypeConverter))]       
    public Type Type
    {
        get
        {
            return type;
        }
        set
        {
            type = value;
            if (value == typeof(string))
                DefaultValue = "";
            else
                DefaultValue = Activator.CreateInstance(value);
        }
    }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        var obj = new CustomObjectType
        {
            Name = "Foo",
            Properties =
            {
                new CustomProperty { Name = "Bar", Type = typeof(int), Desc = "I'm a bar"},
                new CustomProperty { Name = "When", Type = typeof(DateTime), Desc = "When it happened"},
            }
        };
        Application.Run(new Form { Controls = { new PropertyGrid { SelectedObject = obj, Dock = DockStyle.Fill } } });
    }
}

Это работает, однако я нахожу это довольно неловким решением. Поскольку я предоставляю PropertyDescriptor для Object и CustomPropertyConverter для CustomProperty, оба из которых на самом деле не делают ничего значимого. И все же я не могу их удалить.

Существует ли элегантный способ разрешить редактирование свойств типа Object (например, DefaultValue) с использованием соответствующих редакторов в соответствии с информацией времени выполнения объекта?

0 голосов
/ 11 июля 2019
public override void SetValue(object component, object value)           
{
    //((CustomObjectType)component)[prop.Name] = value;

    CustomObjectType cot = (CustomObjectType)component;

    CustomProperty cp = cot.Properties.FirstOrDefault(r => r.Name.Equals(prop.Name));
    if (cp == null) return;

    cp.DefaultValue = value;
}
...