c# - 如何调整 TableLayoutPanel 以按高度和重量包装内容?(动态)
问题描述
我创建了一个将用作弹出窗口的表单。
我需要这个弹出窗口包含标题、ProgressBar 和百分比文本。
因此,为此,我选择TableLayoutPanel
在我的 Form 中用作控件。
所有这些 UI 元素都是动态创建的。为了测试,我现在使用标签而不是真正的 ProgressBar。
public partial class TestFormDeleteIt : Form
{
public TestFormDeleteIt()
{
InitializeComponent();
AutoSize = true;
var flp = CreateTableLayoutPanel();
Controls.Add(flp);
}
private Control CreateTableLayoutPanel()
{
// TableLayoutPanel Initialization
var panel = new TableLayoutPanel
{
ColumnCount = 2,
RowCount = 2,
Dock = DockStyle.Fill
};
//Adjust column size in percentage
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70F));
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F));
//Adjust row size in percentage
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
//Fill content
panel.Controls.Add(
new Label()
{
Text = "state",
Dock = DockStyle.Fill
},
/*column*/ 0, /*row*/ 0);
panel.Controls.Add(
new Label()
{
Text = "ProgressBar",
Dock = DockStyle.Fill
},
/*column*/ 0, /*row*/ 1);
panel.Controls.Add(
new Label()
{
Text = "100%",
Dock = DockStyle.Fill
},
/*column*/ 1, /*row*/ 1);
return panel;
}
}
因此,正如您所看到的,我创建的内容TableLayoutPanel
是按行和列划分的,并且对于我设置的每个视图Dock = DockStyle.Fill
,因为我不想硬编码固定大小。
我期望的是每个 UI 元素都填充 TableLayoutPanel 中的一个单元格,并且父窗体本身将通过包含所有其他控件的 TableLayoutPanel 调整其大小。
但实际上我得到了这个结果:
父表单不包装内容,看起来它具有固定大小,并且 TableLayoutPanel 尝试填充表单 ClientArea。我已经设置AutoSize = true;
了,但它没有明显的影响。
我究竟做错了什么?
编辑
谢谢,@Jimi 现在看起来好多了
我还添加了
MinimumSize = new System.Drawing.Size(600, Height)
到 TLP,但问题是 - 如您所见,每一行都有最小高度。它看起来像每行之间的空间。为什么会这样?
编辑2
我想注意到,如果我改变这个
panel.Controls.Add(
new Label()
{
Text = "state",
Dock = DockStyle.Fill
},
/*column*/ 0, /*row*/ 0);
对此:
var label1 = new Label()
{
Text = "state",
Dock = DockStyle.Fill
};
panel.SetRow(label1, 0);
panel.SetColumn(label1, 0);
它不起作用......我做错了什么?
这是我现在的代码
public partial class TestFormDeleteIt : Form
{
public TestFormDeleteIt()
{
InitializeComponent();
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
MinimumSize = new System.Drawing.Size(200, 0);
var flp = CreateTableLayoutPanel();
Controls.Add(flp);
}
private TableLayoutPanel CreateTableLayoutPanel()
{
// TableLayoutPanel Initialization
var panel = new TableLayoutPanel
{
ColumnCount = 2,
RowCount = 2,
Dock = DockStyle.Fill
};
//Adjust column size in percentage
panel.ColumnStyles.Clear();
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 90F));
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10F));
//Adjust row size in percentage
panel.RowStyles.Clear();
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
//Fill content
panel.Controls.Add(
new Label()
{
Text = "state",
Dock = DockStyle.Fill
},
/*column*/ 0, /*row*/ 0);
panel.Controls.Add(
CreateProgressBar(),
/*column*/ 0, /*row*/ 1);
panel.Controls.Add(
new Label()
{
Text = "100%",
Dock = DockStyle.Fill
},
/*column*/ 1, /*row*/ 1);
panel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
panel.AutoSize = true;
return panel;
}
private Control CreateProgressBar() => new ProgressBar()
{
Visible = true,
Dock = DockStyle.Top
};
}
问题是 - 即使我MinimumSize = new Size(200, 0)
在我的表单中将静态宽度设置为 200 ->,当我打开它时,我看到它需要1000
.
为什么会这样?
解决方案
场景:
- 一个简单的表格,没有应用特定的尺寸。没有子控件。
- TableLayoutPanel是在运行时从头开始构建的。没有具体尺寸
- 创建两行两列,大小模式设置为百分比
- 三个控件从头开始创建并添加到三个 TableLayoutPanel 的单元格
TableLayoutPanel 必须将自身调整为没有特定大小的新内容(应用到 Windows 窗体控件的默认大小除外),而不会被挤出存在新的子控件
- 在将 TableLayoutPanel 添加到其 Controls 集合后,Form 必须自动调整其(唯一)子 Control 的大小,保持新设置的最小宽度,但没有最小或最大高度(因此它可以垂直扩展,如果需要,但不是水平的)
- 最后,自动调整大小的 TableLayoutPanel 必须停靠在自动调整大小的 Form 容器内,所有部件需要配合才能生成预期的 Layout
如何进行:
操作的顺序将清楚地确定最终结果。
- 表单初始化:它的构造函数调用标准的 InitializeComponent() 方法。没有子控件,因此实际上没有什么可布局的。表单将简单地设置在设计器中定义的大小。
- 我们现在指示表单根据其内容自动调整大小,并且允许它扩大和缩小其大小以完成此任务。表单现在实际上不会做任何事情,因为此时它无法执行其布局(除非明确指示,使用该
PerformLayout()
方法) - 我们设置了一个最小尺寸,限制了它的宽度,但不限制它的高度(设置
MinimumSize = new Size(200, 0)
,我们定义了一个最小宽度,但是高度尺寸可以自由增长也可以收缩,这里 - 设置0
为值,意味着没有限制)。 - 现在使用默认大小创建 TableLayoutPanel 及其子控件(Windows 窗体控件在创建时具有默认大小)。标签将自动调整其文本的大小:这是默认行为。
- 将所有子控件添加到 TLP 后,TLP 被指示自动调整大小,还指定它可以增长和缩小以执行布局。6 TableLayoutPanel 被添加到 Form 容器中。现在,当一个 Control 添加到 Controls 集合中时,内部方法指示 ContainerControl
SuspendLayout()
定位新 Control 和ResumeLayou()
(不执行子 Control 的布局,因为这刚刚发生):此时,Form,指示自动调整其内容的大小,将计算其PreferredSize
大小,然后根据新内容自动调整大小。 - 现在
TableLayoutPanel
设置为停靠到父容器。Container 已经自动调整为 的大小TableLayoutPanel
,因此 TLP 只是将其锚点固定为表单的当前大小。
最终的 Layout 将在 Form 显示之前执行:无论如何,此时,所有涉及的 Controls 都已按照指示调整大小和定位。
额外:
► TableLayoutPanel 的SetColumn()和SetRow()被显式调用。原因在这两种方法的备注部分有部分解释:
此方法将表格布局重新应用到 TableLayoutPanel 中的所有控件。
在当前上下文中,调用这些方法是一个加号。这不是严格需要的,因为我们只是将控件添加到 TLP,从不删除。
但是,当在 TLP 的内容设置为 AutoSize(这适用于当前上下文)时,在 TLP 中添加和删除控件时,TLP 实际上不会按预期执行布局:在大多数情况下,单元格会(是的,并非总是...)在删除控件时保持其原始大小(删除或处置,结果是相同的)。
关于此事的一些说明可以在这里找到:
这段代码(不同的语言,但易于阅读)可以轻松测试以重现问题
Dynamic TableLayoutPanel Controls Keep Border Width
►清除ColumnStyles和RowStyles并添加新样式:
- 原始代码实际上添加了新样式而没有删除现有的默认样式:这在某些情况下会产生布局问题。这里关于此事的一些注意事项:
删除 TableLayoutPanel 中的 Row 会导致布局问题
在 FlowLayoutPanel 中将多行控件居中
public partial class TestFormDeleteIt : Form
{
protected internal ProgressBar pBar = null;
protected internal Label lbl2 = null;
public TestFormDeleteIt()
{
InitializeComponent();
this.AutoSize = true;
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
this.MinimumSize = new Size(200, 0);
var tlp = CreateTableLayoutPanel();
tlp.AutoSizeMode = AutoSizeMode.GrowAndShrink;
tlp.AutoSize = true;
this.Controls.Add(tlp);
tlp.Dock = DockStyle.Fill;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Run(() => UpdateProgress());
}
private TableLayoutPanel CreateTableLayoutPanel()
{
var panel = new TableLayoutPanel { ColumnCount = 2, RowCount = 2,
CellBorderStyle = TableLayoutPanelCellBorderStyle.Single
};
panel.ColumnStyles.Clear();
panel.RowStyles.Clear();
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70F));
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
var lbl1 = new Label() { Text = "state", Dock = DockStyle.Fill };
panel.Controls.Add(lbl1);
panel.SetColumn(lbl1, 0);
panel.SetRow(lbl1, 0);
pBar = new ProgressBar() { Dock = DockStyle.Top };
panel.Controls.Add(pBar);
panel.SetColumn(pBar, 0);
panel.SetRow(pBar, 1);
lbl2 = new Label() { Text = "0%", Dock = DockStyle.Fill };
panel.Controls.Add(lbl2);
panel.SetColumn(lbl2, 1);
panel.SetRow(lbl2, 1);
return panel;
}
private async Task UpdateProgress()
{
for (int i = 1; i <= 100; i++) {
BeginInvoke(new Action(() => {
pBar.Value = i;
lbl2.Text = (i / 100.0).ToString("P");
}));
await Task.Delay(50);
}
}
}
推荐阅读
- python - Python Plotly:文本未出现在饼图的所有部分
- sql - 从客户订单表中查找最近的重复订单 - 慢速交叉应用
- reactjs - 如何添加缺少的模块“./lib/constants”?
- linux - 如何解决 linux 中 docker 卷挂载期间的文件处理问题?
- javascript - Npm run deploy 使用 GHpages 覆盖我试图推送到我的网站的内容
- tensorflow - TensorFlow 训练错误:在 105475273 处截断记录
- azure-devops - 无法使用机器人移动 Azure Devops 区域
- javascript - 链接不会保持打开和活动状态
- python - 使用 Google Drive API 创建嵌套文件夹
- little-man-computer - 如何在小人电脑中检查一个数字是奇数还是偶数