开发者

C# PropertyGrid: Changing properties not working?

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.


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


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.


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.


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.


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜