I recommend the OP here forget about using a TableLayoutPanel and instead just create a "grid" of TextBoxes, or RichTextBoxes, in a Panel.
Summary: the more I work with the TableLayoutPanel and discover its quirks the less I like it and want to use it. The auto-flow behavior of the TableLayoutPanel seems to be not controllable in a way that you can reliably add new Rows with Controls, and not have the Row/Column order altered in strange ways.
The "last straw" today was discovering that the TableLayoutPanel's method to find a Control based on its Column, Row position (GetControlFromPosition) will not work if the Control is not 'visible; so, to make Controls visible on demand, you have to parse the entire ControlCollection of the TableLayoutPanel.
But,
as promised, here's working code that will create a TableLayoutPanel with 51 rows, and 8 columns. The first Row will have Labels showing the Row Number; the first Column will have Labels showing the Column Number and Cells 1,1~7,50 will contain TextBoxes.
I will not support, or answer questions about this code; I may, when and if I have time, extend it, or modify it, and, perhaps, if I reach something usable, and reliable, post a Tip, or article, on CP showing what I've learned.
Initially, only one Row of TextBoxes is displayed: when the user presses the Return/Enter key in the TextBox in the last (eighth) column of ANY Row, the next Row will be made visible.
Because of the side-effects of the auto-flow behavior of the TableLayoutPanel, I do not recommend anyone take on the task of trying to actually delete Rows.
WinForms
Controls
Single Main-Form named 'MainForm
TableLayoutPanel named 'TestTableLayoutPanel
Button named 'btnCreateTableLayoutPanel
ContextMenuStrip named 'ctxMnuStripDeleteRow
0. Design-time settings:
TestTableLayoutPanel:
'RowCount 1
'ColumnCount 1
'GrowStyle AddRows
'CellBorderStyle TableLayoutPanelCellBorderStyle.None
ctxMnuStripDeleteRow: add one Menu Item: 'Delete Row', and create a 'Clicked EventHandler for it. Note: code to delete a row not implemented here.
1. Form-scoped constants:
private const int NRows = 50;
private const int NCols = 7;
private const int RwHeight = 40;
private const int ClWidth = 100;
2. Form-scoped variables:
private int currentRow;
private int currentCol;
private int count = 1;
private TextBox currentTextBox;
private List<textbox> selectedLabels = new List<textbox>();</textbox></textbox>
3. Form Load code:
private void Form1_Load(object sender, EventArgs e)
{
TestTableLayoutPanel.RowCount = NRows + 1;
TestTableLayoutPanel.ColumnCount = NCols + 1;
TestTableLayoutPanel.AutoScroll = true;
}
4. Initializing the TableLayoutPanel:
private void btnCreateLayoutPanel_Click(object sender, EventArgs e)
{
TestTableLayoutPanel.SuspendLayout();
TextBox newTextBox;
Label newLabel;
TestTableLayoutPanel.RowStyles[0].Height = RwHeight;
TestTableLayoutPanel.ColumnStyles[0].Width = ClWidth;
for (int row = 0; row <= NRows; row++)
{
TestTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, RwHeight));
for (int col = 0; col < NCols; col++)
{
TestTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, ClWidth));
if (row == 0)
{
newLabel = new Label
{
Margin = new Padding(0),
AutoSize = false,
Dock = DockStyle.Fill,
BackColor = Color.AliceBlue,
Text = string.Format("column {0}", (col + 1).ToString()),
TextAlign = ContentAlignment.MiddleCenter,
TabStop = false
};
TestTableLayoutPanel.Controls.Add(newLabel, col + 1, 0);
continue;
}
if (col == 0)
{
newLabel = new Label()
{
Margin = new Padding(0),
AutoSize = false,
Dock = DockStyle.Fill,
BackColor = Color.AliceBlue,
Text = string.Format("row {0}", row.ToString()),
TextAlign = ContentAlignment.MiddleCenter,
TabStop = false
};
newLabel.ContextMenuStrip = ctxMnuStripDeleteRow;
TestTableLayoutPanel.Controls.Add(newLabel, 0, row);
newLabel.Click += RowHeader_Click;
}
newTextBox = new TextBox()
{
AutoSize = false,
Dock = DockStyle.Fill,
Margin = new Padding(0),
Padding = new Padding(0),
TextAlign = HorizontalAlignment.Center
};
newTextBox.Click += CellLabel_Click;
if (col + 1 == NCols)
{
newTextBox.AcceptsReturn = true;
newTextBox.KeyUp += NewLabelOnKeyUp;
}
TestTableLayoutPanel.Controls.Add(newTextBox, col + 1, row);
if (row > 1) newTextBox.Hide();
}
}
TestTableLayoutPanel.ResumeLayout();
TestTableLayoutPanel.Refresh();
}
5. Making a new Row visible when the user presses Return in the 7th. TextBox:
private void NewLabelOnKeyUp(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.KeyCode != Keys.Enter) return;
var position = TestTableLayoutPanel.GetPositionFromControl(currentTextBox);
if (position.Column == NCols)
{
currentTextBox.AcceptsReturn = false;
int row = position.Row + 1;
TestTableLayoutPanel.RowStyles[row].Height = RwHeight;
for (int i = 1; i < NCols + 1; i++)
{
<big></big>
foreach (Control cntrl in TestTableLayoutPanel.Controls)
{
if (TestTableLayoutPanel.GetCellPosition(cntrl).Column == i)
{
if (TestTableLayoutPanel.GetCellPosition(cntrl).Row == row)
{
cntrl.Visible = true;
break;
}
}
}
}
}
}
private void CellLabel_Click(object sender, EventArgs e)
{
currentTextBox = sender as TextBox;
if (currentTextBox == null) return;
}
Comment: there is a bug in the implementation of TableLayoutPanel: when a Control in a Cell is hidden: it cannot be "found" using GetControlFromPosition ! So you see here the hack necessary to find the hidden Controls.
To be continued ?