动态 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 已停靠,这将强制它评估新值并生成新布局,重新计算所有值以满足停靠要求。
结果的视觉样本:
在示例代码中,添加和删除按钮被命名为btnAddControl
和btnRemoveControl
,组合框命名为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
默认情况下,我有一个包含 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 已停靠,这将强制它评估新值并生成新布局,重新计算所有值以满足停靠要求。
结果的视觉样本:
在示例代码中,添加和删除按钮被命名为btnAddControl
和btnRemoveControl
,组合框命名为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