C# PropertyGrid:更改属性不起作用?
我的 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
向量。
我认为解决方案是保留 CanConvertFrom
和 ConvertFrom
方法。我假设它们能够正常工作,因为它们修改了重力矢量的字符串表示形式,然后依次更新了实际的重力矢量。那,或者使 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为属性网格直接在 Vec2 对象上设置属性(即
X
和Y
属性)。I think the property grid is setting the properties directly on the Vec2 object (i.e.
X
andY
properties).它正在更改
Vec2
的 X 和 Y 属性,因为这就是您要设置的内容。当改变这些属性时,如果你把它翻译成代码,我猜它正在调用Gravity.X = value;
,当你仔细想想,它实际上是一个Get< /code> 上的
Gravity
,然后是Set
上的Vec2.X
。使用当前代码,为了输入
Gravity
的Set
,您需要将其显式更改为新的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 callingGravity.X = value;
, which, when you think about it, is really aGet
onGravity
, and then aSet
onVec2.X
.With your current code, in order to enter the
Set
ofGravity
, you would need to explicitly change it to a newVec2
object.EDIT: And apparently,
[NotifyParentProperty(true)]
is no good, clearing up my uncertainty.以下是 PropertyDescriptorGridEntry.SetPropertyValueCore 的代码:
在您的情况下,Vec2 是值类型,因此 flag 为 true。在网格中, vec2 实例是您的结构的副本,因此您的原始结构尚未被修改,但当parentGridEntry.PropertyValue = obj;被调用,然后通过容器类中 Gravity 属性的 PropertyDescriptor 分配该值。您添加 CanConvertFrom 的事实指示网格 ParentGridEntry.IsValueEditable 现在为 true。
Here is the code for PropertyDescriptorGridEntry.SetPropertyValueCore:
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.
答案取决于您编辑属性的方式:
如果您通过直接在
Gravity
属性中输入文本来编辑属性,那么我怀疑您的TypeConverter
将被调用,并且 <应该调用 code>Gravity 属性的 setter。这是假设“重力”属性甚至在属性网格中显示为可编辑,我怀疑它应该如此。如果您通过展开属性(单击小
+
符号)然后直接设置其X
或Y
属性来编辑属性,则Gravity
属性的 setter 永远不会被调用。只有X
和Y
属性的 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 yourTypeConverter
will be called and theGravity
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 itsX
orY
property then theGravity
property's setter will never be called. Just theX
andY
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.
在托管 C++ 中:
In managed C++ :