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

发布于 2024-09-14 19:52:12 字数 1080 浏览 9 评论 0原文

我有一个自定义对象类型,必须在 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 的类型仅在运行时已知。

此外,我需要为 CustomProperty 的属性 Type 提供自定义 TypeConverter 以显示支持类型的下拉列表(例如,<代码>Int、字符串颜色MyOwnClass)。

我该怎么做?

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

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

Which has a list of custom properties:

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

The main problem here is that the PropertyGrid control doesn't allow to edit, nor uses appropriate editing tools for property DefaultValue which is beforehand instantiated by setting value of CustomProperty's field Type.

Type of DefaultValue is only known at runtime.

Moreover I need to supply a custom TypeConverter for CustomProperty's property Type to show a drop-down list of supported types (for example, Int, String, Color, MyOwnClass).

How would i do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

娇妻 2024-09-21 19:52:12

要沿着这条路线走下去,您需要为每个属性创建一个自定义 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 } } });
    }
}

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 } } });
    }
}
等风来 2024-09-21 19:52:12

我认为马克·格拉维尔可能有点误解了上下文。

我试图编辑 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<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 提供 PropertyDescriptor,为 CustomProperty 提供 CustomPropertyConverter,这两者实际上并没有做任何有意义的事情。
但我也无法删除它们。

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

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?

故人如初 2024-09-21 19:52:12
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;
}
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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文