.Net C# How to dynamically add controls to a form (and hook up their events)

Dynamically Adding Controls to a Windows Form

I have a project right now who’s requirements are to dynamically create sections on a form, dynamically create items in those sections (checkboxes, text boxes, etc), and store the values entered in those boxes in a database.

Now this is no small task, but it had me thinking. It’s been a while since I wrote something that used dynamic controls. Since I’m writing the code anyway I thought I would share the technical details as to how you too can create your own controls on the fly, and hook up the events, and read the data. So many web demo’s seem to show the first and miss the next two (which would be the same as missing the point entirely..)

Here is the code (C# this time!):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DynamicControls
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //This block dynamically creates a TextBox and adds it to the form
            TextBox tb = new TextBox();
            tb.Name = "tbTest";
            tb.Location = new Point(10, 10);
            tb.Text = "Some text";
            //Hook our textbox up to our generic textbox handler
            tb.TextChanged += new System.EventHandler(this.tb_TextChanged);
            this.Controls.Add(tb);

            //This block dynamically creates a Button and adds it to the form
            Button b = new Button();
            b.Name = "bTest";
            b.Location = new Point(10, 40);
            b.Text = "My Button";
            //Hook our button up to our generic button handler
            b.Click += new System.EventHandler(this.b_Click);
            this.Controls.Add(b);
        }

        //Our generic textbox TextChanged() handler:
        private void tb_TextChanged(object sender, EventArgs e)
        {
            TextBox tb;
            tb = (TextBox)sender;
            //Prove we can get the name of the TextBox that had its text changed
            MessageBox.Show(tb.Name + " TextChanged");
        }

        private void b_Click(object sender, EventArgs e)
        {
            Button b;
            b = (Button)sender;
            //Prove we can get the name of the Button that had its click event called
            MessageBox.Show(b.Name + " Click");
        }

        private void bGetValues_Click(object sender, EventArgs e)
        {
            //Getting the events to fire is all well and good, but how do we collect
            //the data from the form when Save is clicked? I am presuming that there
            //are dozens of dynamically created controls on this form. Well, this is
            //how
            TextBox tb;
            //Find the TextBox Control named "tbTest"
            tb = (TextBox)this.Controls.Find("tbTest", true)[0];
            //Prove we can get the Text that was entered in that textbox
            MessageBox.Show(tb.Text);
        }

    }
}

So that’s how you dynamically add controls to a windows form in C# .Net.  Feel free to post your questions / comments below.

Note: In vb the line:

b.Click = new System.EventHandler(this.b_Click);

looks like this:

AddHandler b.Click, AddressOf b_Click

among other differences…

EDIT:
If you’re looking for a way to add controls relatively be sure to check out the response I gave to Frank regarding his question on further automation (It’s below in the comments section).  In my response I put together a quick class to manage adding controls relatively to each other.

Advertisements
  1. #1 by sattybese on January 2, 2010 - 2:13 am

    What WordPress theme do you use?

    • #2 by Anthony on January 9, 2010 - 2:24 am

      INove by mg12
      I kinda like it.

      • #3 by Frank on June 20, 2010 - 12:24 pm

        Do you know a simple way to check for other controls and their positions?
        I want to be able to create a control each time I click a button. I already managed to do this, and my app now creates a control on the specified position of my GroupBox.

        BUT! I want the controls to appear underneath each other everytime I create one. I figured it might be possible with a collection, but right now my brain is smoking… 😦

      • #4 by Anthony on June 21, 2010 - 11:24 am

        For sure, I can think of a few good ways to accomplish that. Probably the simplest to understand would be to just loop through the controls you’re interested in. The good thing is the control you’re working with, GroupBox, already has a Controls collection that contains all the controls that belong to it. So the easiest way to implement this is probably like so:

        private void button1_Click(object sender, EventArgs e)
        {
            int iBottom = 0;
            foreach (object item in groupBox1.Controls)
            {
                // I added this in case you're interested in one object type or another.  
        	//In my test only TextBoxes are checked.  You could easily add more types.
                if (item.GetType().FullName == "System.Windows.Forms.TextBox")
                {
                    TextBox tb = (TextBox)item;
                    if ((tb.Top + tb.Height) > iBottom)
                    {
                        iBottom = tb.Top + tb.Height;
                    }
                }
            }
            //This line requires a reference to System.Windows.Forms
            System.Windows.Forms.MessageBox.Show("The bottom of the lowest TextBox is:" + iBottom);
        }
        

        Now there’s going to be some issues with this, for example I’m not sure the width of the border is included in the bottom measurement, you would have to check that. The worst part of this implementation is it’s calculating a value that we probably should already have from previous processing. A more intelligent way to write this (and faster) would be to keep the bottom most measurement as the controls are added to the form. You could create a local function and a global variable to figure this out, something like AddControlToGroupBox(…), or you could create a myGroupBox object that inherits from GroupBox and calculate the bottom as boxes are added. Better yet you could create your own add function that handles adding controls relatively to your GroupBox. This is something I quickly came up with:
        The Class:myGroupBox.cs

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        
        namespace WindowsFormsApplication2
        {
            class myGroupBox : System.Windows.Forms.GroupBox
            {
                System.Windows.Forms.GroupBox _baseGroupBox;
                int _theBottom = 0;
                public int theBottom
                {
                    get {return _theBottom;}
                }
                public myGroupBox(System.Windows.Forms.GroupBox baseGroupBox)
                {
                    _baseGroupBox = baseGroupBox;
                }
                public void Add(System.Windows.Forms.Control value)
                {
                    value.Top = _theBottom + value.Top;
                    //We can just set the new bottom here since we know 
                    //the new object is going to be lower than the old one.
                    _theBottom = value.Top + value.Height;
                    _baseGroupBox.Controls.Add(value);
                }
            }
        }

        The code to add controls to that class:

        private void button2_Click(object sender, EventArgs e)
        {
            myGroupBox myGB = new myGroupBox(groupBox2);
            TextBox tb = new TextBox();
            tb.Name = "tbTest";
            tb.Location = new Point(0, 10);
            tb.Text = "Some text";
            myGB.Add(tb);
            tb = new TextBox();
            tb.Name = "tbTest2";
            tb.Location = new Point(0, 10);
            tb.Text = "Some text2";
            myGB.Add(tb);
        }

        I tried to keep it simple, instead of creating the GroupBox on the fly this uses an existing GroupBox. Currently it ignores anything else that is already in the GroupBox so that may need extending, but it does have the feature of automatically calculating the top of each new item relatively to the bottom item and using the passed in Top value as an offset for the top of the new object. Let me know if you find it confusing.. I’ve got to get back to work!

  2. #5 by Cory on February 17, 2011 - 12:18 pm

    I know this topic is somewhat old, but I thought I’d post into it anyways. If you are just creating a simple single line text box with a button click, I’ve achieved a similar goal of lining the textboxes below each other by using a variable in the position of the box and incrementing the variable with each added control.

    Abbreviated code

    //declare variable
    int x = 10;
    // inside code to create textbox
    textbox.Location = new Point (10,x)
    x = (x+30)

    your first textbox position would then be (10,10)
    each textbox after that would then be moved down the page by 30 so
    (10,40)
    (10,70)
    (10,100)

    you would of course need to need to set the increment amount to a high enough space the new box below the previous one.

    • #6 by Anthony on March 31, 2011 - 1:50 pm

      As far as I’m concerned nothing is ‘old’ until it’s considered obsolete, thanks for your post!

  3. #7 by danish on September 12, 2012 - 2:57 pm

    how to save this dynamically created form? so that we can open it in future
    thanxx :))

    • #8 by Anthony on April 16, 2013 - 1:12 pm

      The answer to this is, well, very carefully 🙂

      It’s challenging. You will have to write everything dynamically. The methods you hook up need to know how to also write their values. Since everything is dynamic you have no hope of using a normalized database for this. I suggest either comma delimited or XML. You can dynamically create either of those at run time and then save everything to a text file, or to a de-normalized database.

      Be sure to think it through, whatever method you use has to handle dynamically creating / deleting fields, reading from the ‘database’ and then writing to it again.

      Best of luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: