C# PropertyGrid:更改属性不起作用?

发布于 2024-08-18 21:56:20 字数 2956 浏览 3 评论 0原文

我的 World 类上有一个如下所示的属性:

    public Vec2 Gravity
    {
        get {
            Console.WriteLine("Getting gravity!");
            return GetGravity(); 
        }
        set {
            Console.WriteLine("Setting gravity!");
            SetGravity(value); 
        }
    }

“获得重力!”当 PropertyGrid 尝试读取该值时,字符串按预期显示,但是当我尝试更改重力矢量并按 Enter 键时,没有任何反应。为什么不呢?


我的 Vec2 类具有以下属性:

    public float X
    {
        get
        {
            return x;
        }
        set
        {
            x = value;
        }
    }

    public float Y
    {
        get
        {
            return y;
        }
        set
        {
            y = value;
        }
    }

这些属性在网格上可见,这要归功于:

public class Vec2Converter : ExpandableObjectConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
    {
        if (destinationType == typeof(Vec2)) return true;
        else return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
    {
        if (destinationType == typeof(string) && value is Vec2)
        {
            Vec2 vec = (Vec2)value;
            return string.Format("{0}, {1}", vec.X, vec.Y);
        }
        else return base.ConvertTo(context, culture, value, destinationType);
    }
}

我刚刚将这两个方法添加到 Vec2Converter 中,现在它可以工作了:

    public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
    {
        if (sourceType == typeof(string)) return true;
        else return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is string)
        {
            try
            {
                string strVal = value as string;
                var parts = strVal.Split(',');
                float x = float.Parse(parts[0]);
                float y = float.Parse(parts[1]);
                return new Vec2(x, y);
            }
            catch
            {
                throw new ArgumentException("Can not convert '" + (string)value + "'to type Vec2");
            }
        }
        else return base.ConvertFrom(context, culture, value);
    }

它可以通过更改字符串表示形式和各个属性。为什么thix要针对具有单独属性的情况修复它?


认为答案Vec2是一个结构体,所以当World返回Gravity 向量,它按值传递它,然后更改副本,而不是实际的 Gravity 向量。

我认为解决方案是保留 CanConvertFromConvertFrom 方法。我假设它们能够正常工作,因为它们修改了重力矢量的字符串表示形式,然后依次更新了实际的重力矢量。那,或者使 Vec2 成为一个类应该可以工作,但我现在无法真正测试它,因为我的应用程序非常依赖于它是一个结构。

I have a property on my World class that looks like this:

    public Vec2 Gravity
    {
        get {
            Console.WriteLine("Getting gravity!");
            return GetGravity(); 
        }
        set {
            Console.WriteLine("Setting gravity!");
            SetGravity(value); 
        }
    }

The "Getting gravity!" string is displayed as expected, when the PropertyGrid tries to read the value, but when I try to change the gravity vector and press enter, nothing happens. Why not?


My Vec2 class has properties:

    public float X
    {
        get
        {
            return x;
        }
        set
        {
            x = value;
        }
    }

    public float Y
    {
        get
        {
            return y;
        }
        set
        {
            y = value;
        }
    }

Which are visible on the grid, thanks to:

public class Vec2Converter : ExpandableObjectConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
    {
        if (destinationType == typeof(Vec2)) return true;
        else return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
    {
        if (destinationType == typeof(string) && value is Vec2)
        {
            Vec2 vec = (Vec2)value;
            return string.Format("{0}, {1}", vec.X, vec.Y);
        }
        else return base.ConvertTo(context, culture, value, destinationType);
    }
}

I just added these two methods to the Vec2Converter and now it works:

    public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
    {
        if (sourceType == typeof(string)) return true;
        else return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is string)
        {
            try
            {
                string strVal = value as string;
                var parts = strVal.Split(',');
                float x = float.Parse(parts[0]);
                float y = float.Parse(parts[1]);
                return new Vec2(x, y);
            }
            catch
            {
                throw new ArgumentException("Can not convert '" + (string)value + "'to type Vec2");
            }
        }
        else return base.ConvertFrom(context, culture, value);
    }

It works both by changing the string representation, and the individual properties. Why does thix fix it for the case with individual properties??


I think the answer is that Vec2 is a struct, so when World returns the Gravity vector, it passes it by value, and then the copy is changed, rather than the actual Gravity vector.

The solution, I think is either to keep the CanConvertFrom and ConvertFrom methods in place. I'm assuming those make it work because they modify the string representation of the gravity vector, and then that in turn updates the actual gravity vector. That, or making Vec2 a class ought to work, but I can't really test that now because my app is very dependent on it being a struct.

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

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

发布评论

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

评论(5

ζ澈沫 2024-08-25 21:56:20

我认为属性网格直接在 Vec2 对象上设置属性(即 XY 属性)。

I think the property grid is setting the properties directly on the Vec2 object (i.e. Xand Y properties).

你是暖光i 2024-08-25 21:56:20

它正在更改 Vec2 的 X 和 Y 属性,因为这就是您要设置的内容。当改变这些属性时,如果你把它翻译成代码,我猜它正在调用 Gravity.X = value;,当你仔细想想,它实际上是一个 Get< /code> 上的 Gravity,然后是 Set 上的 Vec2.X

使用当前代码,为了输入 GravitySet,您需要将其显式更改为新的 Vec2 对象。

编辑:显然,[NotifyParentProperty(true)] 不好,消除了我的不确定性。

It is changing the X and Y properties of Vec2, because that's what you are setting. When changing these properties, if you translate it into code, I'm guessing that it is calling Gravity.X = value;, which, when you think about it, is really a Get on Gravity, and then a Set on Vec2.X.

With your current code, in order to enter the Set of Gravity, you would need to explicitly change it to a new Vec2 object.

EDIT: And apparently, [NotifyParentProperty(true)] is no good, clearing up my uncertainty.

ぶ宁プ宁ぶ 2024-08-25 21:56:20

以下是 PropertyDescriptorGridEntry.SetPropertyValueCore 的代码:

protected void SetPropertyValueCore(object obj, object value, bool doUndo)
{
    if (this.propertyInfo != null)
    {
        Cursor current = Cursor.Current;
        try
        {
            Cursor.Current = Cursors.WaitCursor;
            object component = obj;
            if (component is ICustomTypeDescriptor)
            {
                component = ((ICustomTypeDescriptor) component).GetPropertyOwner(this.propertyInfo);
            }
            bool flag = false;
            if (this.ParentGridEntry != null)
            {
                Type propertyType = this.ParentGridEntry.PropertyType;
                flag = propertyType.IsValueType || propertyType.IsArray;
            }
            if (component != null)
            {
                this.propertyInfo.SetValue(component, value);
                if (flag)
                {
                    GridEntry parentGridEntry = this.ParentGridEntry;
                    if ((parentGridEntry != null) && parentGridEntry.IsValueEditable)
                    {
                        parentGridEntry.PropertyValue = obj;
                    }
                }
            }
        }
        finally
        {
            Cursor.Current = current;
        }
    }
}

在您的情况下,Vec2 是值类型,因此 flag 为 true。在网格中, vec2 实例是您的结构的副本,因此您的原始结构尚未被修改,但当parentGridEntry.PropertyValue = obj;被调用,然后通过容器类中 Gravity 属性的 PropertyDescriptor 分配该值。您添加 CanConvertFrom 的事实指示网格 ParentGridEntry.IsValueEditable 现在为 true。

Here is the code for PropertyDescriptorGridEntry.SetPropertyValueCore:

protected void SetPropertyValueCore(object obj, object value, bool doUndo)
{
    if (this.propertyInfo != null)
    {
        Cursor current = Cursor.Current;
        try
        {
            Cursor.Current = Cursors.WaitCursor;
            object component = obj;
            if (component is ICustomTypeDescriptor)
            {
                component = ((ICustomTypeDescriptor) component).GetPropertyOwner(this.propertyInfo);
            }
            bool flag = false;
            if (this.ParentGridEntry != null)
            {
                Type propertyType = this.ParentGridEntry.PropertyType;
                flag = propertyType.IsValueType || propertyType.IsArray;
            }
            if (component != null)
            {
                this.propertyInfo.SetValue(component, value);
                if (flag)
                {
                    GridEntry parentGridEntry = this.ParentGridEntry;
                    if ((parentGridEntry != null) && parentGridEntry.IsValueEditable)
                    {
                        parentGridEntry.PropertyValue = obj;
                    }
                }
            }
        }
        finally
        {
            Cursor.Current = current;
        }
    }
}

In your case Vec2 is a value type, so flag is true. In the grid, the vec2 instance is a copy of your struct, so your original has not been modified yet but when parentGridEntry.PropertyValue = obj; is called, then the value is assigned through the PropertyDescriptor of the Gravity property in your container class. The fact that you added CanConvertFrom instructs the grid that parentGridEntry.IsValueEditable is now true.

笑梦风尘 2024-08-25 21:56:20

答案取决于您编辑属性的方式:

如果您通过直接在 Gravity 属性中输入文本来编辑属性,那么我怀疑您的 TypeConverter 将被调用,并且 <应该调用 code>Gravity 属性的 setter。这是假设“重力”属性甚至在属性网格中显示为可编辑,我怀疑它应该如此。

如果您通过展开属性(单击小 + 符号)然后直接设置其 XY 属性来编辑属性,则 Gravity 属性的 setter 永远不会被调用。只有 XY 属性的 setter 会被调用。

一般来说,在大多数框架中,复杂的属性是不可设置的。它们通常仅通过其子属性来设置。在这种情况下,我确实认为设置整个属性是合理的,因为这似乎是一个常见的操作。这只是一个设计选择,无论哪种方式都没有对错。

The answer depends on how you are editing the property:

If you edit the property by typing in text directly into the Gravity property then I suspect your TypeConverter will be called and the Gravity property's setter should be called. This is assuming that the Gravity property even displays as editable in the property grid, which I suspect it should.

If you edit the property by expanding the property (clicking the little + sign) and then directly setting its X or Y property then the Gravity property's setter will never be called. Just the X and Y properties will have their setters get called.

In general and in most frameworks complex properties are not settable. They are often set only via their sub-properties. In this case I do think it's reasonable to have the entire property settable since it seems like it would be a common operation. It's just a design choice and it's neither right nor wrong to do it either way.

沩ん囻菔务 2024-08-25 21:56:20

在托管 C++ 中:

[TypeConverter(ExpandableObjectConverter::typeid)]  
public ref struct Vector3
{
    float x, y, z;   

    property float X   
    {   
        float get()             { return x; }
        void set(float value)   { x = value; }
    }   
    property float Y   
    {   
        float get()             { return y; }
        void set(float value)   { y = value; }
    }   
    property float Z
    {   
        float get()             { return z; }
        void set(float value)   { z = value; }
    }
};

In managed C++ :

[TypeConverter(ExpandableObjectConverter::typeid)]  
public ref struct Vector3
{
    float x, y, z;   

    property float X   
    {   
        float get()             { return x; }
        void set(float value)   { x = value; }
    }   
    property float Y   
    {   
        float get()             { return y; }
        void set(float value)   { y = value; }
    }   
    property float Z
    {   
        float get()             { return z; }
        void set(float value)   { z = value; }
    }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文