动态 TableLayoutPanel 控件保持边框宽度

Dynamic TableLayoutPanel Controls Keep Border Width

默认情况下,我有一个包含 1 列和 3 行的 TableLayoutPanel。顶行和底行有包含按钮的子 TableLayoutPanel,而中间行有一个 TextBox。顶部和底部行中的按钮根据 My.Settings 中的值在加载时动态显示,默认情况下有五个按钮(因此有五列)。

我动态设置按钮的文本以及是否要删除它们的方式是这样的(再重复四次):

Dim visibleButtonCount As Integer = {My.Settings.ValueVisible1, My.Settings.ValueVisible2, My.Settings.ValueVisible3, My.Settings.ValueVisible4, My.Settings.ValueVisible5}.Where(Function(setting) setting).Count()
Dim buttonWidth As Double = 100 / visibleButtonCount

ButtonValueDown1.Text = $"- {My.Settings.Value1.ToString("N3")}"
ButtonValueUp1.Text = $"+ {My.Settings.Value1.ToString("N3")}"
If (Not My.Settings.ValueVisible1) Then
    ButtonValueDown1.Parent.Controls.Remove(ButtonValueDown1)
    ButtonValueUp1.Parent.Controls.Remove(ButtonValueUp1)

    With TableLayoutPanelDown.ColumnStyles.Item(0)
        .SizeType = SizeType.Absolute
        .Width = 0
    End With
    With TableLayoutPanelUp.ColumnStyles.Item(0)
        .SizeType = SizeType.Absolute
        .Width = 0
    End With
Else
    TableLayoutPanelDown.ColumnStyles.Item(0).Width = buttonWidth
    TableLayoutPanelUp.ColumnStyles.Item(0).Width = buttonWidth
End If

我 运行 遇到的问题是,只要所有 5 个按钮都可见,顶部和底部行中最右边的按钮与中间行中的文本框齐平 但是 每当移除一个或多个按钮时,最右边的按钮将不再齐平(见图)。

这可能是什么原因造成的?值得一提的是所有控件的margin/padding都是0.

要允许 TableLayoutPanel 在 运行 时动态调整其列的大小,当未定义大小的控件添加到 TableLayoutPanel.Controls 集合时,一种有效的方法是在设计时设置-时间,专栏的 TableLayoutStyle.SizeType to SizeType.Percent.
使用设计器添加 Columns 时,我们可以将每个新 Column 的 Percent 值设置为 100%。 TableLayoutPanel 将根据其当前大小和添加的列数自动确定正确的百分比值。

在当前情况下,我们有一个外部 TableLayoutPanel,其中一个列在其某些单元格中托管其他 TableLayoutPanel。
→ 内部 TableLayoutPanels 设置为 Dock = Fill,填充它们占据的 Cell。
→ 内部 TLP 将托管数量可变的控件,因此它们需要动态调整子控件的大小以填充外部 TLP 容器的宽度。

设计时的示例布局:

上面浅灰色的TableLayoutPanel代表示例代码中的tableLayoutPanelUp控件

由于在 运行 时添加 and/or 删除了子控件(此处为按钮),内部 TableLayoutPanel 需要均匀调整它们的大小以填充外部 TableLayoutPanel 容器大小,以保留布局。

► TableLayoutPanel 可以按预期执行此布局,前提是我们指定在将控件添加到其 Controls 集合时将包含控件的列和行(单元格)。
如果我们不这样做,当 TableLayoutPanel 需要调整其子控件的大小以填充可用空间 space 时,它无法正确确定其子控件的新大小。


在示例代码中,将子按钮添加到 List(Of Button) 集合(为方便起见),然后将按钮添加到 TableLayoutPanel(在问题中命名为 tableLayoutPanelUp)。

■ 在SuspendLayout() / PerformLayout()部分添加子控件,暂停布局,直到所有控件都添加完毕,然后在所有到位后执行布局.

■ 对于每个新控件,其单元格位置是使用 TableLayoutPanel 的 SetRow() and SetColumn() 方法明确设置的。

■ 要删除一个Control,使用Controls.Remove(Control)方法指定Control实例(Control没有被销毁,所以它仍然是它的容器List中)和相应的Column的ColumnStyles.Width(代表一个Percent值)设置为0

■ 添加子控件时,再次调用 SetRow()SetColumn() 来定义将包含新控件的单元格。在本例中,对应Column的Size(百分比)设置为100 / tableLayoutPanelUp.ColumnCount。由于 TableLayoutPanel 已停靠,这将强制它评估新值并生成新布局,重新计算所有值以满足停靠要求。

结果的视觉样本:

在示例代码中,添加和删除按钮被命名为btnAddControlbtnRemoveControl,组合框命名为cboControlsIndexes

Private tlpButtons As List(Of Button) = Nothing

Protected Overrides Sub OnLoad(e As EventArgs)
    MyBase.OnLoad(e)

    tlpButtons = New List(Of Button)() From {
        New Button() With {.Dock = DockStyle.Fill, .FlatStyle = FlatStyle.Flat, .Text = "Button1"},
        New Button() With {.Dock = DockStyle.Fill, .FlatStyle = FlatStyle.Flat, .Text = "Button2"},
        New Button() With {.Dock = DockStyle.Fill, .FlatStyle = FlatStyle.Flat, .Text = "Button3"},
        New Button() With {.Dock = DockStyle.Fill, .FlatStyle = FlatStyle.Flat, .Text = "Button4"},
        New Button() With {.Dock = DockStyle.Fill, .FlatStyle = FlatStyle.Flat, .Text = "Button5"}
    }

    cboControlsIndexes.DisplayMember = "Text"
    cboControlsIndexes.DataSource = tlpButtons

    tableLayoutPanelUp.SuspendLayout()

    For i As Integer = 0 To tlpButtons.Count - 1
        tableLayoutPanelUp.Controls.Add(tlpButtons(i))
        tableLayoutPanelUp.SetRow(tlpButtons(i), 0)
        tableLayoutPanelUp.SetColumn(tlpButtons(i), i)
    Next
    tableLayoutPanelUp.ResumeLayout(True)
    tableLayoutPanelUp.PerformLayout()
End Sub

Private Sub btnRemoveControl_Click(sender As Object, e As EventArgs) Handles btnRemoveControl.Click
    Dim removeAtIndex As Integer = cboControlsIndexes.SelectedIndex

    tableLayoutPanelUp.Controls.Remove(tlpButtons(removeAtIndex))
    tableLayoutPanelUp.ColumnStyles(removeAtIndex).Width = 0
End Sub

Private Sub btnAddControl_Click(sender As Object, e As EventArgs) Handles btnAddControl.Click
    Dim addAtIndex As Integer = cboControlsIndexes.SelectedIndex

    tableLayoutPanelUp.Controls.Add(tlpButtons(addAtIndex))
    tableLayoutPanelUp.SetRow(tlpButtons(addAtIndex), 0)
    tableLayoutPanelUp.SetColumn(tlpButtons(addAtIndex), addAtIndex)
    tableLayoutPanelUp.ColumnStyles(addAtIndex).Width = 100 / tableLayoutPanelUp.ColumnCount
End Sub