Search code examples
c#winformsuser-controls

UserControl generate sub-controls at design-time


I'm trying to create a user control in Windows Forms, with will have the function of "generating" subcontrols. Example:

Let say that we have UC with only flowlayoutpanel, and ButtonText property:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<ButtonBlueprint> ButtonText { get; set; } = new List<ButtonBlueprint>();

ButtonBlueprint is just wrapper for string, doesn't matter:

public class ButtonBlueprint
{
    public string Name { get; set; }
}

Now, I got a nice editor in designer for this collection: enter image description here

What I want and I don't know how to achieve is to generate buttons from items in this collection. It's easy when you kick in into runtime:

enter image description here

But i want to have those controls generated and being visible in designer while being during design-time. Is this possible? I know that some advanced controls, for example from Telerik has a similar feature and can generated controls from a class schema, so it should be possible. Any advice on how to do this?


Solution

  • Well, you need not only generating the subcontrols during designtime, but you also need to keep the changes by serializing them to *.Designer.cs.. If I understood your problem well, you can get inspired by following piece of code. Just a very simple sample (persistency done only by adding a comment to the *.Designer.cs).

    using System;
    using System.CodeDom;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.ComponentModel.Design.Serialization;
    using System.Drawing;
    using System.Windows.Forms;
    using ControlDesigner = System.Windows.Forms.Design.ControlDesigner;
    
    namespace WindowsFormsApp1
    {
        [DesignerSerializer(typeof(TestSerializer), typeof(CodeDomSerializer))]
        [Designer(typeof(TestEditor), typeof(IDesigner))]
        public partial class TestControl1 : UserControl
        {
            public TestControl1()
            {
                InitializeComponent();
            }
        }
    
        public class TestEditor : ControlDesigner
        {
            private static int _counter;
    
            public TestEditor()
            {
                Verbs.Add(new DesignerVerb("Add button", Handler));
            }
    
            private void Handler(object sender, EventArgs e)
            {
                var button = new Button
                             {
                                 Enabled = true,
                                 Text = "Hello",
                                 Name = string.Format("Button{0}", ++_counter)
                             };
                button.Location = new Point(0, _counter * button.Size.Height);
                ((TestControl1)Component).Controls.Add(button);
            }
        }
    
        public class TestSerializer : CodeDomSerializer
        {
            public override object Serialize(IDesignerSerializationManager manager, object value)
            {
                if (value.GetType() == typeof(TestControl1))
                {
                    var serializer = manager.GetSerializer(typeof(TestControl1).BaseType, typeof(CodeDomSerializer)) as CodeDomSerializer;
                    if (serializer != null)
                    {
                        var coll = serializer.Serialize(manager, value) as CodeStatementCollection;
                        if (coll != null)
                        {
                            var tc = (TestControl1)value;
                            foreach (Control control in tc.Controls)
                            {
                                coll.Insert(0, new CodeCommentStatement("Component " + control.Name));
                            }
                        }
    
                        return coll;
                    }
                }
    
                return base.Serialize(manager, value);
            }
        }
    }