Remove Row inside TableLayoutPanel makes a layout problem

A solution base on the layout described in the comments and this answer, previously posted:
Center multiple rows of controls in a FlowLayoutPanel

Description:
(Full code of a test Form provided at bottom of this post)

  1. Create a new Form (here, named frmTLPTest1)
  2. Add two Panels. One is used to host some buttons, the other one will be the Container of a TableLayoutPanel.
  3. Set the Container panel to AutoScroll = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, set all the Anchors (Left, Top, Right, Bottom)
  4. Inside the Container panel, drop a new TableLayoutPanel: set it to AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Dock = DockStyle.Top
  5. Remove all Rows and Columns from the TableLayoutPanel except one of each (you cannot remove all). Set the dimensions of both to AutoSize.

Important note (also reported in the linked answer):

In the Form constructor, one of the RowStyles is removed. This is
important: the TLP will keep 2 RowStyles. One is applied to the
existing Row; the second style will be applied to the first Row you
add: to the first one only, not the others. If this style is not
removed, it will compromise the layout.

The core methods used to Add Rows to/Remove Rows from the TableLayoutPanel, make use of a FlowLayoutPanel as the TLP Row content and can also be used as Container of other controls, eventually.

TlpAddRow(TableLayoutPanel tlp, bool addRowCount) method:
Adds a new FlowLayoutPanel to the Cell of the TableLayoutPanel specified and adds a new Row if requested.
Since the Designer won’t allow to remove all the Rows, the First Row (FlowLayoutPanel) must not increment the Rows count: the addRowCount argument will be set to false.

private Control TlpAddRow(TableLayoutPanel tlp, bool addRowCount)
{
    var flp = new FlowLayoutPanel() {
        Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
        AutoSize = true,
        AutoSizeMode = AutoSizeMode.GrowAndShrink,
    };

    tlp.SuspendLayout();
    if (addRowCount) tlp.RowCount += 1;
    tlp.Controls.Add(flp, 0, tlp.RowCount - 1);
    tlp.ResumeLayout(true);
    return flp;
}

TLPRemoveRow(TableLayoutPanel tlp, Control control) method (overloaded):

Allows to remove a Row from the specified TableLayoutPanel. The Row to be removed can be derived from the Control that is used as the Row Container (a FlowLayoutPanel, here, but it could be a Panel, another TableLayoutPanel, or some other type of Container control).
The Row can also be removed by directly specifying the Row index.

private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
{
    int ctlRow = tlp.GetRow(control);
    TLPRemoveRow(tlp, ctlRow);
}

private void TLPRemoveRow(TableLayoutPanel tlp, int row)
{
    if (row < tlp.RowCount - 1) {
        for (int i = row; i < tlp.RowCount - 1; i++) {
            tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
        }
    }
    tlp.RowCount -= 1;
}

Visual results of this Layout:

TableLayoutPanel dynamic Flow

Since it’s easier to understand how it work by testing rather than explaining, here’s the full layout of the Form:

Test Form (frmTLPTest1):

using System.Drawing;
using System.Linq;
using System.Windows.Forms;

public partial class frmTLPTest1 : Form
{
    public frmTLPTest1()
    {
        InitializeComponent();
        tlp1.RowStyles.RemoveAt(1);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        TlpAddRow(tlp1, false);
    }

    Random rnd = new Random();
    Size[] sizes = new Size[] { new Size(75, 75), new Size(100, 100), new Size(125, 125)};
    Color[] colors = new Color[] { Color.Red, Color.LightGreen, Color.YellowGreen, Color.SteelBlue };
    Control selectedObject = null;
    Control selectedParent = null;

    private void btnAddControl_Click(object sender, EventArgs e)
    {
        Size size = new Size(125, 125);
        if (chkRandom.Checked) size = sizes[rnd.Next(sizes.Length)];
        
        var pBox = new PictureBox() {
            Anchor = AnchorStyles.None,
            BackColor = colors[rnd.Next(colors.Length)],
            MinimumSize = size,
            Size = size
        };

        bool drawborder = false;
        pBox.MouseEnter += (s, evt) => { drawborder = true;  pBox.Invalidate(); };
        pBox.MouseLeave += (s, evt) => { drawborder = false; pBox.Invalidate(); };
        pBox.MouseDown += (s, evt) => { selectedParent = pBox.Parent;
                                        selectedObject = pBox;  pBox.Invalidate();
        };
        pBox.Paint += (s, evt) => {
            if (drawborder) {
                ControlPaint.DrawBorder(evt.Graphics, pBox.ClientRectangle, 
                                        Color.White, ButtonBorderStyle.Solid);
            }
        };

        if (tlp1.RowCount == 0) TlpAddRow(tlp1, true); 

        var ctl = tlp1.GetControlFromPosition(0, tlp1.RowCount - 1);
        int overallWith = 0;
        if (ctl.Controls?.Count > 0) {
            overallWith = ctl.Controls.OfType<Control>().Sum(c => c.Width + c.Margin.Left + c.Margin.Right);
        }
        overallWith += ctl.Margin.Right + ctl.Margin.Left + pBox.Size.Width + pBox.Margin.Left + pBox.Margin.Right;

        if (overallWith >= tlp1.Width) {
            ctl = TlpAddRow(tlp1, true);
        }
        ctl.Controls.Add(pBox);
    }

    private void btnRemoveRow_Click(object sender, EventArgs e)
    {
        if (selectedParent is null) return;
        if (selectedParent.Controls.Count > 0) {
            for (int i = 0; i == selectedParent.Controls.Count - 1; i++) {
                selectedParent.Controls[i].Dispose();
            }
        }
        TLPRemoveRow(tlp1, selectedParent);
        selectedParent.Dispose();
    }

    private void btnRemoveControl_Click(object sender, EventArgs e)
    {
        if (selectedObject is null) return;
        Control parent = selectedObject.Parent;
        selectedObject.Dispose();

        if (parent?.Controls.Count == 0) {
            TLPRemoveRow(tlp1, parent);
            parent.Dispose();
        }
    }

    private Control TlpAddRow(TableLayoutPanel tlp, bool addRowCount)
    {
        var flp = new FlowLayoutPanel() {
            Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
            AutoSize = true,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
        };

        tlp.SuspendLayout();
        if (addRowCount) tlp.RowCount += 1;
        tlp.Controls.Add(flp, 0, tlp.RowCount - 1);
        tlp.ResumeLayout(true);
        return flp;
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
    {
        int ctlRow = tlp.GetRow(control);
        TLPRemoveRow(tlp, ctlRow);
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, int row)
    {
        if (row < tlp.RowCount - 1) {
            for (int i = row; i < tlp.RowCount - 1; i++) {
                tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
            }
        }
        tlp.RowCount -= 1;
    }
}

Test Form Designer:

partial class frmTLPTest1
{
    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.panToolbar = new System.Windows.Forms.Panel();
        this.btnRemoveRow = new System.Windows.Forms.Button();
        this.chkRandom = new System.Windows.Forms.CheckBox();
        this.btnRemoveControl = new System.Windows.Forms.Button();
        this.btnAddControl = new System.Windows.Forms.Button();
        this.panBackground = new System.Windows.Forms.Panel();
        this.tlp1 = new System.Windows.Forms.TableLayoutPanel();
        this.panToolbar.SuspendLayout();
        this.panBackground.SuspendLayout();
        this.SuspendLayout();
        // 
        // panToolbar
        // 
        this.panToolbar.BackColor = System.Drawing.Color.DarkOliveGreen;
        this.panToolbar.Controls.Add(this.btnRemoveRow);
        this.panToolbar.Controls.Add(this.chkRandom);
        this.panToolbar.Controls.Add(this.btnRemoveControl);
        this.panToolbar.Controls.Add(this.btnAddControl);
        this.panToolbar.Dock = System.Windows.Forms.DockStyle.Bottom;
        this.panToolbar.Location = new System.Drawing.Point(0, 359);
        this.panToolbar.Name = "panToolbar";
        this.panToolbar.Size = new System.Drawing.Size(552, 55);
        this.panToolbar.TabIndex = 2;
        // 
        // btnRemoveRow
        // 
        this.btnRemoveRow.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnRemoveRow.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveRow.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveRow.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnRemoveRow.ForeColor = System.Drawing.Color.White;
        this.btnRemoveRow.Location = new System.Drawing.Point(261, 11);
        this.btnRemoveRow.Name = "btnRemoveRow";
        this.btnRemoveRow.Size = new System.Drawing.Size(119, 34);
        this.btnRemoveRow.TabIndex = 4;
        this.btnRemoveRow.Text = "Remove Row";
        this.btnRemoveRow.UseVisualStyleBackColor = false;
        this.btnRemoveRow.Click += new System.EventHandler(this.btnRemoveRow_Click);
        // 
        // chkRandom
        // 
        this.chkRandom.AutoSize = true;
        this.chkRandom.ForeColor = System.Drawing.Color.White;
        this.chkRandom.Location = new System.Drawing.Point(446, 20);
        this.chkRandom.Name = "chkRandom";
        this.chkRandom.Size = new System.Drawing.Size(94, 19);
        this.chkRandom.TabIndex = 3;
        this.chkRandom.Text = "Random Size";
        this.chkRandom.UseVisualStyleBackColor = true;
        // 
        // btnRemoveControl
        // 
        this.btnRemoveControl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnRemoveControl.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveControl.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveControl.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnRemoveControl.ForeColor = System.Drawing.Color.White;
        this.btnRemoveControl.Location = new System.Drawing.Point(136, 11);
        this.btnRemoveControl.Name = "btnRemoveControl";
        this.btnRemoveControl.Size = new System.Drawing.Size(119, 34);
        this.btnRemoveControl.TabIndex = 2;
        this.btnRemoveControl.Text = "Remove Control";
        this.btnRemoveControl.UseVisualStyleBackColor = false;
        this.btnRemoveControl.Click += new System.EventHandler(this.btnRemoveControl_Click);
        // 
        // btnAddControl
        // 
        this.btnAddControl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnAddControl.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnAddControl.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnAddControl.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnAddControl.ForeColor = System.Drawing.Color.White;
        this.btnAddControl.Location = new System.Drawing.Point(11, 11);
        this.btnAddControl.Name = "btnAddControl";
        this.btnAddControl.Size = new System.Drawing.Size(119, 34);
        this.btnAddControl.TabIndex = 0;
        this.btnAddControl.Text = "Add Control";
        this.btnAddControl.UseVisualStyleBackColor = false;
        this.btnAddControl.Click += new System.EventHandler(this.btnAddControl_Click);
        // 
        // panBackground
        // 
        this.panBackground.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right)));
        this.panBackground.AutoScroll = true;
        this.panBackground.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
        this.panBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.panBackground.Controls.Add(this.tlp1);
        this.panBackground.Location = new System.Drawing.Point(0, 0);
        this.panBackground.Name = "panBackground";
        this.panBackground.Size = new System.Drawing.Size(552, 360);
        this.panBackground.TabIndex = 3;
        // 
        // tlp1
        // 
        this.tlp1.AutoSize = true;
        this.tlp1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
        this.tlp1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.tlp1.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Single;
        this.tlp1.ColumnCount = 1;
        this.tlp1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
        this.tlp1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
        this.tlp1.Dock = System.Windows.Forms.DockStyle.Top;
        this.tlp1.Location = new System.Drawing.Point(0, 0);
        this.tlp1.Name = "tlp1";
        this.tlp1.RowCount = 1;
        this.tlp1.RowStyles.Add(new System.Windows.Forms.RowStyle());
        this.tlp1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 1F));
        this.tlp1.Size = new System.Drawing.Size(552, 2);
        this.tlp1.TabIndex = 4;
        // 
        // frmTLPTest1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
        this.ClientSize = new System.Drawing.Size(552, 414);
        this.Controls.Add(this.panBackground);
        this.Controls.Add(this.panToolbar);
        this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.Name = "frmTLPTest1";
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
        this.Text = "frmTLPTest1";
        this.Load += new System.EventHandler(this.SOfrmTest1_Load);
        this.panToolbar.ResumeLayout(false);
        this.panToolbar.PerformLayout();
        this.panBackground.ResumeLayout(false);
        this.panBackground.PerformLayout();
        this.ResumeLayout(false);
    }

    private System.Windows.Forms.Panel panToolbar;
    private System.Windows.Forms.Button btnAddControl;
    private System.Windows.Forms.Button btnRemoveControl;
    private System.Windows.Forms.CheckBox chkRandom;
    private System.Windows.Forms.Panel panBackground;
    private System.Windows.Forms.TableLayoutPanel tlp1;
    private System.Windows.Forms.Button btnRemoveRow;
}

Leave a Comment