Modifying structure property in a PropertyGrid

后端 未结 3 1400
闹比i
闹比i 2020-12-20 20:48

Why SomeClass.ClassField.StructField property doesn\'t change in a propertyGrid? It seems, propertyGrid doesn\'t call SomeClass.

相关标签:
3条回答
  • 2020-12-20 21:40

    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.

    0 讨论(0)
  • 2020-12-20 21:40

    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;
      }
    
    0 讨论(0)
  • 2020-12-20 21:49

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题