如何调整 TableLayoutPanel 按高度和重量包裹内容? (动态地)
How to adjust TableLayoutPanel to wrap content by height and weight? (dynamically)
我创建了一个表单,我将用作 pop-up。
我需要这个 pop-up 包含标题、ProgressBar 和百分比文本。
因此,为此,我选择使用 TableLayoutPanel
作为表单中的控件。
所有这些 UI 元素都是动态创建的。为了进行测试,我现在使用的是 Label 而不是真正的 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 中的一个单元格,并且 parent 表单本身将通过包含所有其他控件的 TableLayoutPanel 调整其大小。
但实际上我得到了这个结果:
Parent 表单不换行内容,看起来它具有固定大小并且 TableLayoutPanel 试图填充表单 ClientArea。我设置了 AutoSize = true;
,但没有明显的影响。
我做错了什么?
编辑
谢谢,@Jimi 现在看起来好多了
我也加了
MinimumSize = new System.Drawing.Size(600, Height)
到 TLP,但问题是 - 如您所见,每一行都有类似的最小高度。每行之间看起来像 space。为什么会这样?
EDIT2
我想注意,如果我改变这个
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
};
}
问题是 - 即使我在我的表单中将静态宽度设置为 200 -> MinimumSize = new Size(200, 0)
,当我打开它时我发现它需要 1000
.
为什么会这样?
场景:
- 一个简单的表格,没有应用特定的尺寸。没有儿童控制。
- A TableLayoutPanel 是在 运行 时从头开始构建的。无具体尺寸
- 创建了两行和两列,大小模式设置为百分比
- 从头开始创建三个控件并将其添加到 TableLayoutPanel 的三个单元格中
TableLayoutPanel 必须根据新内容调整自身大小,新内容没有特定大小(应用到 Windows 表单控件的默认大小除外),不会 挤压 不存在新的子控件
- 窗体在将 TableLayoutPanel 添加到其控件集合后,必须自动调整自身大小以适应其(唯一的)子控件的大小,保持新设置的最小宽度,但没有最小或最大高度(因此它如果需要,可以垂直扩展,但不能水平扩展)
- 最后,自动调整大小的 TableLayoutPanel 必须停靠在自动调整大小的 Form 容器内,所有部分需要配合以生成预期的布局
如何进行:
操作的顺序将明确决定最终结果。
- 表单初始化:它的构造函数调用标准的 InitializeComponent() 方法。没有子控件,因此实际上无需布局。表单将简单地设置在设计器中定义的大小。
- 我们现在指示表单根据其内容自动调整大小,并且允许它增大和缩小其大小以完成此任务。表单现在实际上不会做任何事情,因为它此时无法执行其布局(除非明确指示,使用
PerformLayout()
方法)
- 我们设置一个最小尺寸,限制它的宽度,但不限制它的高度(设置
MinimumSize = new Size(200, 0)
,我们定义了一个最小宽度,但是高度尺寸是自由的增长和收缩,这里 - 设置 0
作为值,意味着 没有限制 )。
- 现在使用默认大小创建了 TableLayoutPanel 及其子控件(Windows 表单控件在创建时具有默认大小)。标签将自动调整到其文本的大小:这是默认行为。
- 将所有子控件添加到 TLP 后,指示 TLP 自动调整自身大小,还指定它可以增长和收缩以执行布局。
6 TableLayoutPanel 添加到 Form 容器中。现在,当控件添加到控件集合时,内部方法指示容器控件
SuspendLayout()
,定位新控件并ResumeLayou()
(不执行子控件的布局,因为这刚刚发生):此时,指示自动调整其内容的表单将计算其 PreferredSize
,然后自动调整大小本身到新的内容。
TableLayoutPanel
现在设置为停靠到父 Container。 Container 已经自动调整为 TableLayoutPanel
的大小,因此 TLP 只是将其锚点固定为 Form 的当前大小。
最后的Layout 将在Form 显示之前执行:无论如何,在这一点上,所有涉及的Controls 都已经按照说明调整了大小和位置。
额外:
► TableLayoutPanel 的 SetColumn() and SetRow() 被显式调用。原因在两种方法的备注部分有部分解释:
This method reapplies the table layout to all controls in the
TableLayoutPanel.
在当前上下文中,调用这些方法是一个加。这不是严格需要的,因为我们只是将控件添加到 TLP,从不删除控件。
但是,当控件被添加到 TLP 或从 TLP 中删除时,同时 TLP 设置为其内容自动调整大小(这适用于当前上下文),TLP 实际上不会按预期执行布局:在大多数情况下,单元格会(是的,不总是......)当一个控件被移除(移除或处置,结果是一样的)时保持它们原来的尺寸。
有关此事的一些注释可在此处找到:
此代码(不同语言但易于阅读)可以轻松测试以重现 问题
► 清除ColumnStyles and RowStyles并添加新样式:
- 原始代码实际上添加了新样式而不删除现有的默认样式:在某些情况下这会产生布局问题。这里有一些关于此事的注释:
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);
}
}
}
我创建了一个表单,我将用作 pop-up。
我需要这个 pop-up 包含标题、ProgressBar 和百分比文本。
因此,为此,我选择使用 TableLayoutPanel
作为表单中的控件。
所有这些 UI 元素都是动态创建的。为了进行测试,我现在使用的是 Label 而不是真正的 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 中的一个单元格,并且 parent 表单本身将通过包含所有其他控件的 TableLayoutPanel 调整其大小。
但实际上我得到了这个结果:
Parent 表单不换行内容,看起来它具有固定大小并且 TableLayoutPanel 试图填充表单 ClientArea。我设置了 AutoSize = true;
,但没有明显的影响。
我做错了什么?
编辑
谢谢,@Jimi 现在看起来好多了
我也加了
MinimumSize = new System.Drawing.Size(600, Height)
到 TLP,但问题是 - 如您所见,每一行都有类似的最小高度。每行之间看起来像 space。为什么会这样?
EDIT2
我想注意,如果我改变这个
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
};
}
问题是 - 即使我在我的表单中将静态宽度设置为 200 -> MinimumSize = new Size(200, 0)
,当我打开它时我发现它需要 1000
.
为什么会这样?
场景:
- 一个简单的表格,没有应用特定的尺寸。没有儿童控制。
- A TableLayoutPanel 是在 运行 时从头开始构建的。无具体尺寸
- 创建了两行和两列,大小模式设置为百分比
- 从头开始创建三个控件并将其添加到 TableLayoutPanel 的三个单元格中
TableLayoutPanel 必须根据新内容调整自身大小,新内容没有特定大小(应用到 Windows 表单控件的默认大小除外),不会 挤压 不存在新的子控件
- 窗体在将 TableLayoutPanel 添加到其控件集合后,必须自动调整自身大小以适应其(唯一的)子控件的大小,保持新设置的最小宽度,但没有最小或最大高度(因此它如果需要,可以垂直扩展,但不能水平扩展)
- 最后,自动调整大小的 TableLayoutPanel 必须停靠在自动调整大小的 Form 容器内,所有部分需要配合以生成预期的布局
如何进行:
操作的顺序将明确决定最终结果。
- 表单初始化:它的构造函数调用标准的 InitializeComponent() 方法。没有子控件,因此实际上无需布局。表单将简单地设置在设计器中定义的大小。
- 我们现在指示表单根据其内容自动调整大小,并且允许它增大和缩小其大小以完成此任务。表单现在实际上不会做任何事情,因为它此时无法执行其布局(除非明确指示,使用
PerformLayout()
方法) - 我们设置一个最小尺寸,限制它的宽度,但不限制它的高度(设置
MinimumSize = new Size(200, 0)
,我们定义了一个最小宽度,但是高度尺寸是自由的增长和收缩,这里 - 设置0
作为值,意味着 没有限制 )。 - 现在使用默认大小创建了 TableLayoutPanel 及其子控件(Windows 表单控件在创建时具有默认大小)。标签将自动调整到其文本的大小:这是默认行为。
- 将所有子控件添加到 TLP 后,指示 TLP 自动调整自身大小,还指定它可以增长和收缩以执行布局。
6 TableLayoutPanel 添加到 Form 容器中。现在,当控件添加到控件集合时,内部方法指示容器控件
SuspendLayout()
,定位新控件并ResumeLayou()
(不执行子控件的布局,因为这刚刚发生):此时,指示自动调整其内容的表单将计算其PreferredSize
,然后自动调整大小本身到新的内容。 TableLayoutPanel
现在设置为停靠到父 Container。 Container 已经自动调整为TableLayoutPanel
的大小,因此 TLP 只是将其锚点固定为 Form 的当前大小。
最后的Layout 将在Form 显示之前执行:无论如何,在这一点上,所有涉及的Controls 都已经按照说明调整了大小和位置。
额外:
► TableLayoutPanel 的 SetColumn() and SetRow() 被显式调用。原因在两种方法的备注部分有部分解释:
This method reapplies the table layout to all controls in the TableLayoutPanel.
在当前上下文中,调用这些方法是一个加。这不是严格需要的,因为我们只是将控件添加到 TLP,从不删除控件。
但是,当控件被添加到 TLP 或从 TLP 中删除时,同时 TLP 设置为其内容自动调整大小(这适用于当前上下文),TLP 实际上不会按预期执行布局:在大多数情况下,单元格会(是的,不总是......)当一个控件被移除(移除或处置,结果是一样的)时保持它们原来的尺寸。
有关此事的一些注释可在此处找到:
此代码(不同语言但易于阅读)可以轻松测试以重现 问题
► 清除ColumnStyles and RowStyles并添加新样式:
- 原始代码实际上添加了新样式而不删除现有的默认样式:在某些情况下这会产生布局问题。这里有一些关于此事的注释:
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);
}
}
}