How to display a dynamic object in property grid?

后端 未结 3 980
时光取名叫无心
时光取名叫无心 2021-01-01 04:57

I have a custom object type which has to be editable in PropertyGrid:

public class CustomObjectType
{
    public string Name { get; set; }               


        
相关标签:
3条回答
  • 2021-01-01 05:16
    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;
    }
    
    0 讨论(0)
  • 2021-01-01 05:30

    I think Marc Gravell might have misunderstood the context a little.

    I was trying to edit properties of CustomObjectTypes not the "CustomObjects" themselves.

    Here's modified Marc's code that does that:

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

    It works, however, I find it a rather awkward solution. Because i'm supplying a PropertyDescriptor for Object and CustomPropertyConverter for CustomProperty, both of which doesn't actually do anything meaningful. Yet I can't remove them either.

    Is there an elegant way to allow editing properties of type Object(like the DefaultValue) using appropriate editors according to runtime information of the object?

    0 讨论(0)
  • 2021-01-01 05:37

    To go down this route, you would need to create a custom PropertyDescriptor per property. You would then expose that via a custom TypeConverter, or (alternatively) ICustomTypeDescriptor/TypeDescriptionProvider. Example:

    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 } } });
        }
    }
    
    0 讨论(0)
提交回复
热议问题