Search code examples
c#winformsvisual-studio-2010visual-studio-designer

Visual Studio designer does not generate code for `List<T>` members of `Windows.Forms.Control`


The following class (a Windows Forms Control) is a type of list control, and the ListControlItem don't inherit any Windows Control class.

public class ListControl : Control
{
    private List<ListControlItem> items;

    public ListControl()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }

    void InitializeComponent()
    {
        this.components = new System.ComponentModel.Container();
        this.items = new List<ListControlItem>();
    }

    public List<ListControlItem> Items
    {
        get { return items; }
        set { items = value; }
    }
}

The problem is, at Design Time, Visual Studio tries to serialize the list content to the resource file of the Form, instead of creating the code for instantiating each item and then adding to the control like with ListView and ListViewItem.

Visual Studio design generated code for ListControl:

        this.listControl1.Items = ((System.Collections.Generic.List<ListControlItem>)(resources.GetObject("listControl1.Items")));

For ListView:

        System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("");
        System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("");
        System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("");
        System.Windows.Forms.ListViewItem listViewItem4 = new System.Windows.Forms.ListViewItem("");
        System.Windows.Forms.ListViewItem listViewItem5 = new System.Windows.Forms.ListViewItem("");
        System.Windows.Forms.ListViewItem listViewItem6 = new System.Windows.Forms.ListViewItem("");

        this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
        listViewItem1,
        listViewItem2,
        listViewItem3,
        listViewItem4,
        listViewItem5,
        listViewItem6});

I tried to search the ListView and ListViewItem to solve the problem, ListView has its "own list class" named ListViewItemCollection that implements the interfaces IList, ICollection, IEnumerable, but List<T> implements the same interfaces.

Does I need to implement a custom serialization for it? Maybe this would just serialize to the resources file. I can't find much documentation as it don't inherit any Windows Forms Control base classes.

UPDATE

Putting the attribute [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] in the List<T> property gives one resource for each List<T> item.

        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items"))));
        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items1"))));
        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items2"))));
        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items3"))));
        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items4"))));
        this.listControl1.Items.Add(((ListControlItem)(resources.GetObject("listControl1.Items5"))));

Thats like implementing a custom serialization for ListControlItem can help.


Solution

  • It's necessary to implement a TypeConverter for the class. What this TypeConverter do is just returning a constructor descriptor for the class.

    Also, specify the TypeConverter of the class using the parameter [TypeConverter(typeof(typeConverter))].

    According to MSDN How to: Implement a Type Converter in this case more specifically on the Type Converters That Generate Code for Property Initialization at Run Time.

    The .NET Framework provides the capability to generate dynamic property initialization code at design time that will initialize a property at run time.

    Developers can build a type converter that produces constructor-based initialization code. These type converters can generate constructor code dynamically using values set at design time in order to configure properties of a type at run time. The type converter implements the logic to configure the type and values of a constructor for the property.

    The resulting code of the Visual Studio designer

            this.listControl1.Items.Add(new ListControlItem());
            this.listControl1.Items.Add(new ListControlItem());
            this.listControl1.Items.Add(new ListControlItem());
            this.listControl1.Items.Add(new ListControlItem());
            this.listControl1.Items.Add(new ListControlItem());
            this.listControl1.Items.Add(new ListControlItem());
    

    The TypeConverter return just a no parameter constructor, but that can be changed in the TypeConverter code.

    About the CodeDomSerializer

    If you need to produce code besides a constructor to initialize a property, it is possible to dynamically generate code by implementing a custom CodeDomSerializer and applying a DesignerSerializerAttribute that associates your CodeDomSerializer for a type with the type. This approach is typically used only for scenarios in which dynamically controlled or customized code generation for component initialization is important. For more information on this approach, see the documentation for CodeDomSerializer.

    That is, with the answer method, the properties values need to be passed on the constructor parameters. If is not sufficient (like setting properties not on the constructor), then it may be necessary to use CodeDomSerializer like @Octopoid said.