Custom, Complicated, Dynamic Reflection Solution - C#

后端 未结 2 1871
悲&欢浪女
悲&欢浪女 2020-12-28 22:36

I have many custom classes that I am using that I will explain and post examples of. After Explaining what they all do I will try to clearly describe the conditions under wh

相关标签:
2条回答
  • 2020-12-28 22:53

    Having worked with TypeConverters myself, I can confirm they are a major pain in the bottom parts. You get nada info about what is actually going wrong, only weird output...

    Idk if it helps, but maybe it is a problem that you add an empty (null) array to anything that is not an IEnumerable? Try moving the add instruction into the scope of the if (...). I dont think there is any harm in that.

    Also, are you certain that (in the last example with the EndJoint) the getter does not return a null pointer? Blank entries smell like null pointers being passed from my experiences.

    0 讨论(0)
  • 2020-12-28 22:57

    You don't have to create special classes to use the property grid. Just decorate the properties with the proper attributes. Here is an example:

    Two custom classes:

    public class MyObjType1
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public override string ToString()
        {
            return Name;
        }
    }
    
    public class MyObjType2
    {
        public string Reference { get; set; }
    
        public override string ToString()
        {
            return Reference;
        }
    }
    

    Note the ToString is overriden, that's what the property grid uses by default if no TypeConverter is defined for a given type.

    One "holder" class that has a collection of custom objects:

    public class MyHolder
    {
        public MyHolder()
        {
            Objects = new List<object>();
        }
    
        public string Name { get; set; }
    
        [TypeConverter(typeof(MyCollectionConverter))]
        public List<object> Objects { get; private set; }
    }
    

    Note the custom TypeConverter applied directly to the Objects property. Here is the source:

    public class MyCollectionConverter : ExpandableObjectConverter
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            IEnumerable enumerable = value as IEnumerable;
            if (enumerable == null)
                return base.GetProperties(context, value, attributes);
    
            int i = 0;
            List<PropertyDescriptor> list = new List<PropertyDescriptor>();
            foreach (object obj in enumerable)
            {
                MyItemPropertyDescriptor index = new MyItemPropertyDescriptor(i.ToString(), obj);
                list.Add(index);
                i++;
            }
            return new PropertyDescriptorCollection(list.ToArray());
        }
    
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType != typeof(string))
                return base.ConvertTo(context, culture, value, destinationType);
    
            IEnumerable enumerable = value as IEnumerable;
            if (enumerable == null)
                return base.ConvertTo(context, culture, value, destinationType);
    
            StringBuilder sb = new StringBuilder();
            foreach (object obj in enumerable)
            {
                if (sb.Length > 0)
                {
                    sb.Append(',');
                }
                sb.AppendFormat("{0}", obj);
            }
            return sb.ToString();
        }
    }
    

    Note we override ConvertTo and give it a special string that displays a comma-separated list of objects in the list. The GetProperties is also overriden and uses a special PropertyDescriptor; It adds an ExpandableObjectConverter attribute to sub objects so they can be expanded too:

    public class MyItemPropertyDescriptor : PropertyDescriptor
    {
        private object _value;
    
        public MyItemPropertyDescriptor(string name, object value)
            : base(name, new[] { new TypeConverterAttribute(typeof(ExpandableObjectConverter)) })
        {
            _value = value;
        }
    
        public override bool IsReadOnly
        {
            get { return false; }
        }
    
        public override object GetValue(object component)
        {
            return _value;
        }
    
        public override Type PropertyType
        {
            get { return _value == null ? typeof(object) : _value.GetType(); }
        }
    
        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    
        public override Type ComponentType
        {
            get { return typeof(object); }
        }
    
        public override bool CanResetValue(object component)
        {
            return false;
        }
    
        public override void ResetValue(object component)
        {
        }
    
        public override void SetValue(object component, object value)
        {
        }
    }
    

    Now, here is some sample code:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
    
            MyHolder holder = new MyHolder();
            for (int i = 0; i < 3; i++)
            {
                holder.Objects.Add(new MyObjType1 { Id = i, Name = i + "Name" });
            }
            for (int i = 0; i < 3; i++)
            {
                holder.Objects.Add(new MyObjType2 { Reference = "Ref" + i });
            }
            propertyGrid1.SelectedObject = holder;
        }
    }
    

    And the result:

    enter image description here

    0 讨论(0)
提交回复
热议问题