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

我有一个自定义对象类型,必须在PropertyGrid可编辑:

 public class CustomObjectType { public string Name { get; set; } public List 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类型仅在运行时已知。

此外,我需要为CustomProperty的属性Type提供自定义TypeConverter ,以显示受支持类型的下拉列表(例如, IntStringColorMyOwnClass )。

我该怎么做?

要沿着这条路线前进,您需要为每个属性创建一个自定义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 props = new List(); [Browsable(false)] public List Properties { get { return props; } } private Dictionary values = new Dictionary(); 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 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 } } }); } } 

我认为Marc Gravell可能会误解一下这个背景。

我试图编辑CustomObjectTypes的属性而不是“CustomObjects”本身。

这是改进的Marc代码,它可以做到这一点:

 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 Properties {get;set;} public CustomObjectType() { Properties = new List(); } } [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 } } }); } } 

然而,它有效,我觉得这是一个相当尴尬的解决方案。 因为我正在为CustomProperty提供Object和CustomPropertyConverter的PropertyDescriptor,所以这两者实际上都没有做任何有意义的事情。 但我也无法删除它们。

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