PropertyGrid 和对象的动态类型

发布于 2024-08-14 18:13:59 字数 1760 浏览 5 评论 0原文

我正在编写一个 GUI 应用程序,我需要在其中启用编辑任意对象的属性(它们的类型仅在运行时已知)。

我决定使用 PropertyGrid 控件来启用此功能。 我创建了以下类:

[TypeConverter(typeof(ExpandableObjectConverter))]
[DefaultPropertyAttribute("Value")]
public class Wrapper
{
    public Wrapper(object val)
    {
        m_Value = val;
    }

    private object m_Value;

    [NotifyParentPropertyAttribute(true)]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public object Value
    {
        get { return m_Value; }
        set { m_Value = value; }
    }
}

当我获得需要编辑的对象的实例时,我为它创建一个包装器并将其设置为选定的对象:

Wrapper wrap = new Wrapper(obj);
propertyGrid.SelectedObject = wrap;

但我遇到了以下问题 - 上面的内容仅在以下情况下才按预期工作: obj 的类型是某种自定义类型(即我自己定义的类,或内置的复杂类型),但当 obj 是基元时则不是。

例如,如果我定义:

[TypeConverter(typeof(ExpandableObjectConverter))]
public class SomeClass
{
    public SomeClass()
    {
        a = 1;
        b = 2;
    }

    public SomeClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }

    private int a;

    [NotifyParentPropertyAttribute(true)]
    public int A
    {
        get { return a; }
        set { a = value; }
    }

    private int b;

    [NotifyParentPropertyAttribute(true)]
    public int B
    {
        get { return b; }
        set { b = value; }
    }
}

并执行:

Wrapper wrap = new Wrapper(new SomeClass());
propertyGrid.SelectedObject = wrap;

那么一切都会顺利进行。另一方面,当我执行以下操作时:

int num = 1;
Wrapper wrap = new Wrapper(num);
propertyGrid.SelectedObject = wrap;

然后我可以在网格中看到值“1”(并且它不是灰度的),但我无法编辑该值。我注意到,如果我将 Wrapper 的“Value”属性的类型更改为 int 并删除 TypeConverter 属性,它就会起作用。 对于其他原始类型和字符串,我得到相同的行为。

问题是什么?

提前致谢!

I'm writing a GUI application where I need to enable editing properties of arbitrary objects (their types are only known at run-time).

I've decided to use the PropertyGrid control to enable this functionality.
I created the following class:

[TypeConverter(typeof(ExpandableObjectConverter))]
[DefaultPropertyAttribute("Value")]
public class Wrapper
{
    public Wrapper(object val)
    {
        m_Value = val;
    }

    private object m_Value;

    [NotifyParentPropertyAttribute(true)]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public object Value
    {
        get { return m_Value; }
        set { m_Value = value; }
    }
}

When I get an instance of an object I need to edit, I create a Wrapper for it and set it as the selected object:

Wrapper wrap = new Wrapper(obj);
propertyGrid.SelectedObject = wrap;

But I've run into the following problem - the above works as expected only when the type of obj is some custom type (i.e a class that I defined by myself, or a built in complex type) but not when obj is a primitive.

For example, if I define:

[TypeConverter(typeof(ExpandableObjectConverter))]
public class SomeClass
{
    public SomeClass()
    {
        a = 1;
        b = 2;
    }

    public SomeClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }

    private int a;

    [NotifyParentPropertyAttribute(true)]
    public int A
    {
        get { return a; }
        set { a = value; }
    }

    private int b;

    [NotifyParentPropertyAttribute(true)]
    public int B
    {
        get { return b; }
        set { b = value; }
    }
}

And do:

Wrapper wrap = new Wrapper(new SomeClass());
propertyGrid.SelectedObject = wrap;

Then everything works swell. On the other hand, when I perform the following:

int num = 1;
Wrapper wrap = new Wrapper(num);
propertyGrid.SelectedObject = wrap;

Then I can see the value "1" in the grid (and it's not grayscaled) but I can't edit the value. I noticed that if I change Wrapper's "Value" property's type to int and remove the TypeConverter attribute, it works.
I get the same behavior for other primitive types and strings.

What is the problem?

Thanks in advance!

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

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

发布评论

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

评论(2

小帐篷 2024-08-21 18:13:59

如果将 ExpandableObjectConverter 设置为 Value 属性,则它将不可编辑,这是正常的,因为 CanConvertFrom 将返回 false。如果删除类型转换器,PropertyGrid 将使用通用 TypeConverter,并且您将再次遇到相同的情况。因此,解决方法是附加一个更智能的 TypeConverter,它将充当正确 TypeConverter 的包装器。这是一个脏的(我没有太多时间,你将根据需要完成它,因为我刚刚实现了 ConvertFrom 部分):

public class MySmartExpandableObjectConverter : ExpandableObjectConverter
{
    TypeConverter actualConverter = null;

    private void InitConverter(ITypeDescriptorContext context)
    {
        if (actualConverter == null)
        {
            TypeConverter parentConverter = TypeDescriptor.GetConverter(context.Instance);
            PropertyDescriptorCollection coll = parentConverter.GetProperties(context.Instance);
            PropertyDescriptor pd = coll[context.PropertyDescriptor.Name];

            if (pd.PropertyType == typeof(object))
                actualConverter = TypeDescriptor.GetConverter(pd.GetValue(context.Instance));
            else
                actualConverter = this;
        }
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        InitConverter(context);

        return actualConverter.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        InitConverter(context); // I guess it is not needed here

        return actualConverter.ConvertFrom(context, culture, value);
    }
}

如果你需要微调某些东西,请告诉我。

尼古拉斯

If you set ExpandableObjectConverter to your Value property, it won't be editable and this is normal because CanConvertFrom will return false. IF you remove the type converter, the PropertyGrid will use the generic TypeConverter and you are again in the same case. So the workaround is to attach a smarter TypeConverter that will act as a wrapper to the correct TypeConverter. Here is a dirty one (I had not much time, you will complete it as needed since I just implemented the ConvertFrom part):

public class MySmartExpandableObjectConverter : ExpandableObjectConverter
{
    TypeConverter actualConverter = null;

    private void InitConverter(ITypeDescriptorContext context)
    {
        if (actualConverter == null)
        {
            TypeConverter parentConverter = TypeDescriptor.GetConverter(context.Instance);
            PropertyDescriptorCollection coll = parentConverter.GetProperties(context.Instance);
            PropertyDescriptor pd = coll[context.PropertyDescriptor.Name];

            if (pd.PropertyType == typeof(object))
                actualConverter = TypeDescriptor.GetConverter(pd.GetValue(context.Instance));
            else
                actualConverter = this;
        }
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        InitConverter(context);

        return actualConverter.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        InitConverter(context); // I guess it is not needed here

        return actualConverter.ConvertFrom(context, culture, value);
    }
}

Let me know if you need to finetune something.

Nicolas

烏雲後面有陽光 2024-08-21 18:13:59

从属性“Value”中删除“TypeConverter”,属性网格将从属性中的值的类型中读取“TypConverter”。

Remove the "TypeConverter" from property "Value", the propertygrid will read the the "TypConverter" from typeo of value which is in property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文