Search code examples
c#.netwinformsuser-controlstypeconverter

A custom TypeConverter for generates AddRange() in WinForms designer not working


From question: Get designer to generate AddRange for simple List property of UserControl

Following TypeConverter doesn't generate AddRange(). While I see Some Controls generate it with same code manner.

Form1.designer.cs

        // userControl11
        // 
        this.userControl11.BackColor = System.Drawing.Color.Red;
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Fruits.Add(new WindowsFormsApp2.Fruit(false, null));
        this.userControl11.Location = new System.Drawing.Point(278, 224);
        this.userControl11.Name = "userControl11";

It should be

this.userControl1.Fruits.AddRange(new Fruite[]{new Fruit(X, Y), new Fruit(X, Y), etc})

While I see standard controls do same but it generates well:

    // listBox1
    // 
    this.listBox1.FormattingEnabled = true;
    this.listBox1.Items.AddRange(new object[] {   // WORKS in standard controls...
    "123",
    "123",
    "123"});

My code:

 [TypeConverter(typeof(FruitConverter))]
    public class Fruit
    {
        public Fruit() { }
        public Fruit(bool edible, string name) : this()
        {
            Edible = edible;
            Name = name;
        }
        public bool Edible { get; set; }
        public string Name { get; set; }
    }

    public class FruitConverter : ExpandableObjectConverter
    {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor)) return true;
            return base.CanConvertTo(context, destinationType);
        }
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor) && (value is Fruit pi))
            {
                ConstructorInfo ctor = typeof(Fruit).GetConstructor(new Type[] { typeof(bool), typeof(string) });
                object[] pars = new object[] { pi.Edible, pi.Name };
                return new InstanceDescriptor(ctor, pars);
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

Test in UserControl

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }


    private Collection<Fruit> fruits = new Collection<Fruit>();
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Collection<Fruit> Fruits => fruits;

}

What should I do now?


Solution

  • Its really too hard. But here's article explain some: https://www.codeproject.com/Articles/5372/How-to-Edit-and-Persist-Collections-with-Collectio

    Third, the collection class must implement one or both of the following methods: Add and AddRange. Although IList interface has an Add member and CollectionBase implements IList, you still have to implement an Add method for your collection, given that CollectionBase declares an explicit member implementation of the IList’s Add member. The designer serializes the collection according to what method you have implemented. If you have implemented both, the AddRange is preferred.

    When serializing with the Add method, for every item it uses a new line.

    this.tc.SimpleItems.Add(new Test.Items.SimpleItem_FullTc(-1, "Item1"));
    this.tc.SimpleItems.Add(new Test.Items.SimpleItem_FullTc(-1, "Item2"));`
    

    When the designer uses the AddRange method, all the items are added on a single line.

    this.tc.SimpleItems.AddRange
      (new Test.Items.SimpleItem[]{new Test.Items.SimpleItem_FullTc(-1, "Item1"),
    new Test.Items.SimpleItem_FullTc(-1, "Item2")});
    

    So you must implement a class inherits from Collection<T> or CollectionBase then add AddRange() Method.