Why SomeClass.ClassField.StructField
property doesn\'t change in a propertyGrid
?
It seems, propertyGrid
doesn\'t call SomeClass.
You need a special TypeConverter that overrides TypeConverter.GetCreateInstanceSupported because otherwise copy-by-value/boxing magic happens behind the scene in the way the property grid handles all this.
Here is one that should work for most value types. You declare it like this:
[TypeConverter(typeof(ValueTypeTypeConverter<SomeStruct>))]
public struct SomeStruct
{
public int StructField { get; set; }
}
public class ValueTypeTypeConverter<T> : ExpandableObjectConverter where T : struct
{
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
if (propertyValues == null)
throw new ArgumentNullException("propertyValues");
T ret = default(T);
object boxed = ret;
foreach (DictionaryEntry entry in propertyValues)
{
PropertyInfo pi = ret.GetType().GetProperty(entry.Key.ToString());
if (pi != null && pi.CanWrite)
{
pi.SetValue(boxed, Convert.ChangeType(entry.Value, pi.PropertyType), null);
}
}
return (T)boxed;
}
}
Note it doesn't support pure field-only structs, only the one with properties, but the ExpandableObjectConverter doesn't support these either, it would require more code to do it.
In my case, the generic argument isn't known at compile time (options structure for a plugin). You can get a copy of the current value using context.PropertyDescriptor.GetValue(context.Instance);
:
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
if (propertyValues == null)
throw new ArgumentNullException("propertyValues");
object boxed = context.PropertyDescriptor.GetValue(context.Instance);
foreach (DictionaryEntry entry in propertyValues)
{
PropertyInfo pi = boxed.GetType().GetProperty(entry.Key.ToString());
if (pi != null && pi.CanWrite)
pi.SetValue(boxed, Convert.ChangeType(entry.Value, pi.PropertyType), null);
}
return boxed;
}
I tweaked Simon Mourier's answer to avoid the need for ValueTypeTypeConverter to be a generic:
public class ValueTypeTypeConverter : System.ComponentModel.ExpandableObjectConverter
{
public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
if (propertyValues == null)
throw new ArgumentNullException("propertyValues");
object boxed = Activator.CreateInstance(context.PropertyDescriptor.PropertyType);
foreach (System.Collections.DictionaryEntry entry in propertyValues)
{
System.Reflection.PropertyInfo pi = context.PropertyDescriptor.PropertyType.GetProperty(entry.Key.ToString());
if ((pi != null) && (pi.CanWrite))
{
pi.SetValue(boxed, Convert.ChangeType(entry.Value, pi.PropertyType), null);
}
}
return boxed;
}
}