代码之家  ›  专栏  ›  技术社区  ›  JBeurer

如何在属性网格中显示动态对象?

  •  4
  • JBeurer  · 技术社区  · 15 年前

    我有一个自定义的对象类型,必须在中编辑 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);
            }              
        }
    }
    

    这里的主要问题是 属性表格 DefaultValue 通过设置 CustomProperty Type

    类型 只有在运行时才知道。

    TypeConverter 对于 自定义属性 的财产 类型 Int , String , Color , MyOwnClass ).

    我该怎么做?

    2 回复  |  直到 10 年前
        1
  •  18
  •   Marc Gravell    15 年前

    要沿着这条路线走下去,您需要创建一个自定义 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
  •  3
  •   JBeurer    15 年前

    我试图编辑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 } } });
        }
    }
    

    不过,我觉得这是一个相当尴尬的解决办法。

    有没有一种优雅的方法允许根据对象的运行时信息使用适当的编辑器编辑类型Object的属性(比如DefaultValue)?

        3
  •  0
  •   Shahbaz A.    6 年前
    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;
    }