WinformsTableLayoutPanel 以编程方式添加行

我已经为此努力了一段时间,并且发现许多其他人也在为 TableLayoutPanel (。Net 2.0 Winform).

问题

我尝试使用一个定义了10列的“空白”tableayoutpanel,然后在运行时以编程方式添加控件行(即每个单元格一个控件)。

人们可能会认为它应该像

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

但是(对我来说)这不会添加行,所以可能会添加一个行样式

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

但那也没用。我深入研究了一下,发现 myTableLayoutPanel.RowCount的使用从设计时到运行时都会发生变化,因此实际上在为 myTableLayoutPanel.RowCount++;添加 RowStyle 条目之前/之后,myTableLayoutPanel.RowCount++;并没有添加另一行!

我遇到的另一个相关问题是,控件将被添加到显示器中,但它们都只是在 TableLayoutPanel 的0点被渲染,此外,它们甚至没有被限制在它们应该被显示的 Cell 界限内(即 Dock = DockStyle)。填充它们仍然显得太大/太小)。

有人有在运行时添加行和控件的工作示例吗?

153194 次浏览

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.

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;

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.

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.

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

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

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.