在 FlowLayoutPanel 中居中多行控件 [英] Center multiple rows of controls in a FlowLayoutPanel
问题描述
我正在尝试制作一个可以托管动态添加控件的面板.有两个警告:
I'm trying to make a panel that would host dynamically added controls. There are two caveats:
- 将会有很多控件,因此面板应该在元素达到其宽度限制时将元素包装到新行中并垂直滚动.
- 控件可以改变大小,这会改变元素的数量
可以放入一行.
我已经看到了一些提议的解决方案,以将动态控件居中在一个表单中,但由于以下原因被拒绝:
I've seen a couple proposed solutions to center dynamic controls in a Form and rejected those for following reasons:
- TableLayoutPanel - 我使用这个的主要问题是当元素增长并且必须从 3-2 网格转移到 2-4,如TableLayoutPanel 似乎不能很好地处理这些.
- AutoSize FlowLayoutPanel 可以在内部增长和缩小TableLayoutControl - 这个解决方案的主要问题是它仅在 Form 中居中一行,一旦它换行到新行,元素开始与右侧对齐.我想我可以动态将新的 FlowLayoutPanels 添加到 TableLayoutControl 的新行,但随后我有一个与第一个场景类似的问题,我需要手动如果元素的大小增加/缩小,则在行之间重新分配元素.
我想知道我是否缺少一些可以帮助我处理增长/收缩事件而无需创建我自己的 TableLayoutPanel 变体的功能?
I was wondering if I'm missing some functionality that can help me handle grows/shrink event without creating my own variation of TableLayoutPanel?
以下是功能草图:
Below is a draft of functionality:
- A - 面板中居中的两个元素
- B - 添加了第三个元素,三个元素都居中
- C - 添加第四个元素,换行并居中
- D - 元素放大,现在包裹在第二个元素上,居中
推荐答案
这是一个重现您描述的行为的示例.
它使用承载多个 FlowLayoutPanel 的 TableLayoutPanel.
Here's an example that reproduces the behaviour you described.
It makes use of a TableLayoutPanel which hosts multiple FlowLayoutPanels.
一个重要的细节是子 FlowLayoutPanel 的锚定:它们需要锚定到 Top-Bottom:这会导致面板定位在 TableLayoutPanel Row 的中心.
One important detail is the anchoring of the child FlowLayoutPanels: they need to be anchored to Top-Bottom: this causes the panel to be positioned in the center of a TableLayoutPanel Row.
请注意,在 Form 构造函数中,删除了 RowStyles
之一.这也很重要:TLP
(这是个很古怪的家伙),即使你只有一行(或一列,同样的事情),也会保留 2 个 RowStyles
.第二种样式将应用于您添加的第一行;只针对第一个,而不是其他:这可能会搞砸布局.
Note that, in the Form constructor, one of the RowStyles
is removed. This is also very important: the TLP
(which is quite the eccentric guy), even if you have just one Row (or one Column, same thing), will keep 2 RowStyles
. The second style will be applied to the first Row you add; just to the first one, not the others: this can screw up the layout.
另一个异常,它没有提供删除行的方法,所以我做了一个.它功能强大,但很简单,需要扩展,包括进一步的验证.
Another anomaly, it doesn't provide a method to remove a Row, so I've made one. It's functional but bare-bones and needs to be extended, including further validations.
请参阅有关当前功能的图形示例.如果您在实施其他方面需要帮助,请发表评论.
See the graphic sample about the current functionality. If you need help in implementing something else, leave a comment.
要构建它,请将以下控件添加到表单(此处称为 FLPTest1
):
To build this add the following controls to a Form (here, called FLPTest1
):
- 添加一个面板,设置
Dock.Bottom
.右键单击并SendToBack()
, - 添加一个
TableLayoutPanel
(这里称为tlp1
),设置:AutoScroll = true
,AutoSize = true
,AutoSizeMode = GrowAndShrink
,Dock.Fill
- 保留 1 列,设置为 AutoSize 和一行,设置为 AutoSize
- Add one Panel, set
Dock.Bottom
. Right click andSendToBack()
, - Add a
TableLayoutPanel
(here, calledtlp1
), set:AutoScroll = true
,AutoSize = true
,AutoSizeMode = GrowAndShrink
,Dock.Fill
- Keep 1 Column, set to AutoSize and one Row, set to AutoSize
- 将其锚点设置为
Top,Bottom <=
这是!important
,如果没有它,布局将无法正常工作:它允许将FLP
置于TLP
行内, AutoSize = true
,AutoSizeMode = GrowAndShrink
- Set its Anchor to
Top, Bottom <=
this is!important
, the layout won't work correctly without it: it allows to center theFLP
inside theTLP
Row, AutoSize = true
,AutoSizeMode = GrowAndShrink
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public partial class TLPTest1 : Form
{
public TLPTest1()
{
InitializeComponent();
tlp1.RowStyles.RemoveAt(1);
}
private void TLPTest1_Load(object sender, EventArgs e)
{
PictureBox pBox = new PictureBox() {
Anchor = AnchorStyles.None,
BackColor = Color.Orange,
MinimumSize = new Size(125, 125),
Size = new Size(125, 125),
};
flp1.Controls.Add(pBox);
tlp1.Controls.Add(flp1);
}
Random rnd = new Random();
Size[] sizes = new Size[] { new Size(75, 75), new Size(100, 100), new Size(125, 125)};
Color[] colors = new Color[] { Color.Red, Color.LightGreen, Color.YellowGreen, Color.SteelBlue };
Control selectedObject = null;
private void btnAddControl_Click(object sender, EventArgs e)
{
Size size = new Size(125, 125);
if (chkRandom.Checked) size = sizes[rnd.Next(sizes.Length)];
var pBox = new PictureBox() {
Anchor = AnchorStyles.None,
BackColor = colors[rnd.Next(colors.Length)],
MinimumSize = size,
Size = size
};
bool drawborder = false;
// Just for testing - use standard delegates instead of Lambdas in real code
pBox.MouseEnter += (s, evt) => { drawborder = true; pBox.Invalidate(); };
pBox.MouseLeave += (s, evt) => { drawborder = false; pBox.Invalidate(); };
pBox.MouseDown += (s, evt) => { selectedObject = pBox; pBox.Invalidate(); };
pBox.Paint += (s, evt) => { if (drawborder) {
ControlPaint.DrawBorder(evt.Graphics, pBox.ClientRectangle,
Color.White, ButtonBorderStyle.Solid);
}
};
var ctl = tlp1.GetControlFromPosition(0, tlp1.RowCount - 1);
int overallWith = ctl.Controls.OfType<Control>().Sum(c => c.Width + c.Margin.Left + c.Margin.Right);
overallWith += (ctl.Margin.Right + ctl.Margin.Left);
if ((overallWith + pBox.Size.Width + pBox.Margin.Left + pBox.Margin.Right) >= tlp1.Width) {
var flp = new FlowLayoutPanel() {
Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
};
flp.Controls.Add(pBox);
tlp1.SuspendLayout();
tlp1.RowCount += 1;
tlp1.Controls.Add(flp, 0, tlp1.RowCount - 1);
tlp1.ResumeLayout(true);
}
else {
ctl.Controls.Add(pBox);
}
}
private void btnRemoveControl_Click(object sender, EventArgs e)
{
if (selectedObject is null) return;
Control parent = selectedObject.Parent;
selectedObject.Dispose();
if (parent?.Controls.Count == 0) {
TLPRemoveRow(tlp1, parent);
parent.Dispose();
}
}
private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
{
int ctlPosition = tlp.GetRow(control);
if (ctlPosition < tlp.RowCount - 1) {
for (int i = ctlPosition; i < tlp.RowCount - 1; i++) {
tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
}
}
tlp.RowCount -= 1;
}
}
这篇关于在 FlowLayoutPanel 中居中多行控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!