[c#] Winforms TableLayoutPanel adding rows programmatically

I've been fighting with this for a while, and have found that a number of other people struggle with the TableLayoutPanel (.net 2.0 Winforms) as well.

Problem

I am attempting to take a 'blank' tablelayoutpanel, which has 10 columns defined, then at runtime programmatically add rows of controls (i.e. one control per cell).

One might have thought that it should be as simple as

myTableLayoutPanel.Controls.Add(myControl, 0 /* Column Index */, 0 /* Row index */);

But that (for me) doesn't add the rows. So maybe adding in a row style

myTableLayoutPanel.RowStyles.Clear();
myTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 30F));

But that doesn't work either. I've dug around and found out that the myTableLayoutPanel.RowCount usage changes from design time to run time, hence doing myTableLayoutPanel.RowCount++; doesn't actually add another row, not even before/after adding a RowStyle entry for it!

Another related issue I am encountering is that the controls will be added to the display, but they all simply get rendered at point 0,0 of the TableLayoutPanel, additionally they are not even constrained to be within the Cell bounds that they are supposed to be displayed within (i.e. with Dock = DockStyle.Fill they still appear way too large/small).

Does someone have a working example of adding rows & controls at runtime?

This question is related to c# .net winforms runtime tablelayoutpanel

The answer is


I just had a related problem (which is how I found this thread), where my dynamically added row and column styles were not taking effect. I usually consider SuspendLayout()/ResumeLayout() as optimizations, but in this case, wrapping my code in them made the rows and columns behave correctly.


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim dt As New DataTable
        Dim dc As DataColumn
        dc = New DataColumn("Question", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)

        dc = New DataColumn("Ans1", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)
        dc = New DataColumn("Ans2", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)
        dc = New DataColumn("Ans3", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)
        dc = New DataColumn("Ans4", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)
        dc = New DataColumn("AnsType", System.Type.GetType("System.String"))
        dt.Columns.Add(dc)


        Dim Dr As DataRow
        Dr = dt.NewRow
        Dr("Question") = "What is Your Name"
        Dr("Ans1") = "Ravi"
        Dr("Ans2") = "Mohan"
        Dr("Ans3") = "Sohan"
        Dr("Ans4") = "Gopal"
        Dr("AnsType") = "Multi"
        dt.Rows.Add(Dr)

        Dr = dt.NewRow
        Dr("Question") = "What is your father Name"
        Dr("Ans1") = "Ravi22"
        Dr("Ans2") = "Mohan2"
        Dr("Ans3") = "Sohan2"
        Dr("Ans4") = "Gopal2"
        Dr("AnsType") = "Multi"
        dt.Rows.Add(Dr)
        Panel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows
        Panel1.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single
        Panel1.BackColor = Color.Azure
        Panel1.RowStyles.Insert(0, New RowStyle(SizeType.Absolute, 50))
        Dim i As Integer = 0

        For Each dri As DataRow In dt.Rows



            Dim lab As New Label()
            lab.Text = dri("Question")
            lab.AutoSize = True

            Panel1.Controls.Add(lab, 0, i)


            Dim Ans1 As CheckBox
            Ans1 = New CheckBox()
            Ans1.Text = dri("Ans1")
            Panel1.Controls.Add(Ans1, 1, i)

            Dim Ans2 As RadioButton
            Ans2 = New RadioButton()
            Ans2.Text = dri("Ans2")
            Panel1.Controls.Add(Ans2, 2, i)
            i = i + 1

            'Panel1.Controls.Add(Pan)
        Next

I just looked into my code. In one application, I just add the controls, but without specifying the index, and when done, I just loop through the row styles and set the size type to AutoSize. So just adding them without specifying the indices seems to add the rows as intended (provided the GrowStyle is set to AddRows).

In another application, I clear the controls and set the RowCount property to the needed value. This does not add the RowStyles. Then I add my controls, this time specifying the indices, and add a new RowStyle (RowStyles.Add(new RowStyle(...)) and this also works.

So, pick one of these methods, they both work. I remember the headaches the table layout panel caused me.


It's a weird design, but the TableLayoutPanel.RowCount property doesn't reflect the count of the RowStyles collection, and similarly for the ColumnCount property and the ColumnStyles collection.

What I've found I needed in my code was to manually update RowCount/ColumnCount after making changes to RowStyles/ColumnStyles.

Here's an example of code I've used:

    /// <summary>
    /// Add a new row to our grid.
    /// </summary>
    /// The row should autosize to match whatever is placed within.
    /// <returns>Index of new row.</returns>
    public int AddAutoSizeRow()
    {
        Panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        Panel.RowCount = Panel.RowStyles.Count;
        mCurrentRow = Panel.RowCount - 1;
        return mCurrentRow;
    }

Other thoughts

  • I've never used DockStyle.Fill to make a control fill a cell in the Grid; I've done this by setting the Anchors property of the control.

  • If you're adding a lot of controls, make sure you call SuspendLayout and ResumeLayout around the process, else things will run slow as the entire form is relaid after each control is added.


Create a table layout panel with two columns in your form and name it tlpFields.

Then, simply add new control to table layout panel (in this case I added 5 labels in column-1 and 5 textboxes in column-2).

tlpFields.RowStyles.Clear();  //first you must clear rowStyles

for (int ii = 0; ii < 5; ii++)
{
    Label l1= new Label();
    TextBox t1 = new TextBox();

    l1.Text = "field : ";

    tlpFields.Controls.Add(l1, 0, ii);  // add label in column0
    tlpFields.Controls.Add(t1, 1, ii);  // add textbox in column1

    tlpFields.RowStyles.Add(new RowStyle(SizeType.Absolute,30)); // 30 is the rows space
}

Finally, run the code.


This works perfectly for adding rows and controls in a TableLayoutPanel.

Define a blank Tablelayoutpanel with 3 columns in the design page

    Dim TableLayoutPanel3 As New TableLayoutPanel()

    TableLayoutPanel3.Name = "TableLayoutPanel3"

    TableLayoutPanel3.Location = New System.Drawing.Point(32, 287)

    TableLayoutPanel3.AutoSize = True

    TableLayoutPanel3.Size = New System.Drawing.Size(620, 20)

    TableLayoutPanel3.ColumnCount = 3

    TableLayoutPanel3.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single

    TableLayoutPanel3.BackColor = System.Drawing.Color.Transparent

    TableLayoutPanel3.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 26.34146!))

    TableLayoutPanel3.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 73.65854!))

    TableLayoutPanel3.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 85.0!))

    Controls.Add(TableLayoutPanel3)

Create a button btnAddRow to add rows on each click

     Private Sub btnAddRow_Click(sender As System.Object, e As System.EventArgs) Handles btnAddRow.Click

          TableLayoutPanel3.GrowStyle = TableLayoutPanelGrowStyle.AddRows

          TableLayoutPanel3.RowStyles.Add(New RowStyle(SizeType.Absolute, 20))

          TableLayoutPanel3.SuspendLayout()

          TableLayoutPanel3.RowCount += 1

          Dim tb1 As New TextBox()

          Dim tb2 As New TextBox()

          Dim tb3 As New TextBox()

          TableLayoutPanel3.Controls.Add(tb1 , 0, TableLayoutPanel3.RowCount - 1)

          TableLayoutPanel3.Controls.Add(tb2, 1, TableLayoutPanel3.RowCount - 1)

          TableLayoutPanel3.Controls.Add(tb3, 2, TableLayoutPanel3.RowCount - 1)

          TableLayoutPanel3.ResumeLayout()

          tb1.Focus()

 End Sub

Here's my code for adding a new row to a two-column TableLayoutColumn:

private void AddRow(Control label, Control value)
{
    int rowIndex = AddTableRow();
    detailTable.Controls.Add(label, LabelColumnIndex, rowIndex);
    if (value != null)
    {
        detailTable.Controls.Add(value, ValueColumnIndex, rowIndex);
    }
}

private int AddTableRow()
{
    int index = detailTable.RowCount++;
    RowStyle style = new RowStyle(SizeType.AutoSize);
    detailTable.RowStyles.Add(style);
    return index;
}

The label control goes in the left column and the value control goes in the right column. The controls are generally of type Label and have their AutoSize property set to true.

I don't think it matters too much, but for reference, here is the designer code that sets up detailTable:

this.detailTable.ColumnCount = 2;
this.detailTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.detailTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.detailTable.Dock = System.Windows.Forms.DockStyle.Fill;
this.detailTable.Location = new System.Drawing.Point(0, 0);
this.detailTable.Name = "detailTable";
this.detailTable.RowCount = 1;
this.detailTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.detailTable.Size = new System.Drawing.Size(266, 436);
this.detailTable.TabIndex = 0;

This all works just fine. You should be aware that there appear to be some problems with disposing controls from a TableLayoutPanel dynamically using the Controls property (at least in some versions of the framework). If you need to remove controls, I suggest disposing the entire TableLayoutPanel and creating a new one.


I just did this last week. Set the GrowStyle on the TableLayoutPanel to AddRows or AddColumns, then your code should work:

// Adds "myControl" to the first column of each row
myTableLayoutPanel.Controls.Add(myControl1, 0 /* Column Index */, 0 /* Row index */);
myTableLayoutPanel.Controls.Add(myControl2, 0 /* Column Index */, 1 /* Row index */);
myTableLayoutPanel.Controls.Add(myControl3, 0 /* Column Index */, 2 /* Row index */);

Here is some working code that seems similar to what you are doing:

    private Int32 tlpRowCount = 0;

    private void BindAddress()
    {
        Addlabel(Addresses.Street);
        if (!String.IsNullOrEmpty(Addresses.Street2))
        {
            Addlabel(Addresses.Street2);
        }
        Addlabel(Addresses.CityStateZip);
        if (!String.IsNullOrEmpty(Account.Country))
        {
            Addlabel(Address.Country);
        }
        Addlabel(String.Empty); // Notice the empty label...
    }

    private void Addlabel(String text)
    {            
        label = new Label();
        label.Dock = DockStyle.Fill;
        label.Text = text;
        label.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
        tlpAddress.Controls.Add(label, 1, tlpRowCount);
        tlpRowCount++;
    }

The TableLayoutPanel always gives me fits with size. In my example above, I'm filing an address card that might grow or shrink depending on the account having an address line two, or a country. Because the last row, or column, of the table layout panel will stretch, I throw the empty label in there to force a new empty row, then everything lines up nicely.

Here is the designer code so you can see the table I start with:

        //
        // tlpAddress
        // 
        this.tlpAddress.AutoSize = true;
        this.tlpAddress.BackColor = System.Drawing.Color.Transparent;
        this.tlpAddress.ColumnCount = 2;
        this.tlpAddress.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 25F));
        this.tlpAddress.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
        this.tlpAddress.Controls.Add(this.pictureBox1, 0, 0);
        this.tlpAddress.Dock = System.Windows.Forms.DockStyle.Fill;
        this.tlpAddress.Location = new System.Drawing.Point(0, 0);
        this.tlpAddress.Name = "tlpAddress";
        this.tlpAddress.Padding = new System.Windows.Forms.Padding(3);
        this.tlpAddress.RowCount = 2;
        this.tlpAddress.RowStyles.Add(new System.Windows.Forms.RowStyle());
        this.tlpAddress.RowStyles.Add(new System.Windows.Forms.RowStyle());
        this.tlpAddress.Size = new System.Drawing.Size(220, 95);
        this.tlpAddress.TabIndex = 0;

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to winforms

How to set combobox default value? Get the cell value of a GridView row Getting the first and last day of a month, using a given DateTime object Check if a record exists in the database Delete a row in DataGridView Control in VB.NET How to make picturebox transparent? Set default format of datetimepicker as dd-MM-yyyy Changing datagridview cell color based on condition C# Inserting Data from a form into an access Database How to use ConfigurationManager

Examples related to runtime

how can I debug a jar at runtime? How to check whether java is installed on the computer Excel VBA Code: Compile Error in x64 Version ('PtrSafe' attribute required) Excel VBA Run-time error '424': Object Required when trying to copy TextBox Execute external program An unhandled exception of type 'System.IO.FileNotFoundException' occurred in Unknown Module Error when checking Java version: could not find java.dll Change the location of an object programmatically java.lang.NoClassDefFoundError: Could not initialize class XXX How permission can be checked at runtime without throwing SecurityException?

Examples related to tablelayoutpanel

Winforms TableLayoutPanel adding rows programmatically