2013-04-01 4 views
6

Почему SomeClass.ClassField.StructField свойство не изменяется в propertyGrid? Кажется, propertyGrid не звонит SomeClass.ClassField.set после SomeStruct экземпляр был изменен. Но тот же код хорошо работает с Point вместо SomeStruct.Изменение структуры собственности в PropertyGrid

[TypeConverter(typeof(ExpandableObjectConverter))] 
public struct SomeStruct 
{ 
    private int structField; 

    public int StructField 
    { 
     get 
     { 
      return structField; 
     } 
     set 
     { 
      structField = value; 
     } 
    } 

    public override string ToString() 
    { 
     return "StructField: " + StructField; 
    } 
} 

[TypeConverter(typeof(ExpandableObjectConverter))] 
public sealed class SomeClass 
{ 
    public SomeStruct ClassField 
    { 
     get; 
     set; 
    } 
} 

... 

var someClass = new SomeClass 
{ 
    ClassField = new SomeStruct 
    { 
     StructField = 42 
    } 
}; 
propertyGrid.SelectedObject = someClass; 
+0

Структуры должны быть неизменны – Filip

+0

Это один изменчиво. То же, что и Point/Rectangle/etc. – nitrocaster

+0

хорошая точка (дополнительные символы) – Filip

ответ

7

Вам нужен специальный TypeConverter, который переопределяет TypeConverter.GetCreateInstanceSupported, потому что в противном случае копирование по значению/бокс магия происходит за сценой в том, как сетка свойств обрабатывает все это.

Вот один из них, который должен работать для большинства типов значений. Вы объявляете это так:

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

Примечание не поддерживает чистые поля только, только структур один со свойствами, но ExpandableObjectConverter не поддерживает их либо, потребуется больше кода, чтобы сделать это.

3

Я отлажен ответ Симона Mourier, чтобы избежать необходимости ValueTypeTypeConverter быть общим:

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

В моем случае, общий аргумент не известен во время компиляции (варианты структуры для плагина). Вы можете получить копию текущего значения с помощью 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; 
    } 
Смежные вопросы