在FlowLayoutPanel中将多行控件居中 [英] Center multiple rows of controls in a FlowLayoutPanel

查看:171
本文介绍了在FlowLayoutPanel中将多行控件居中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个面板,以承载动态添加的控件.有两个警告:

I'm trying to make a panel that would host dynamically added controls. There are two caveats:

  • 将会有很多控件,因此面板在达到宽度限制并垂直滚动时应将元素包装到新行中.
  • 控件可以更改大小,这将更改元素的数量
    可以放入一行.
  • There are going to be a lot of controls, so the panel should wrap the elements into new rows as it reaches its width limits and scroll vertically.
  • Controls can change in size, which would change the number of elements
    that can fit into a single row.

我已经看到了一些提议的解决方案,用于将动态控件集中在窗体中,但由于以下原因而拒绝了它们:

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-这个解决方案的主要问题是 仅将表格内的一行居中,一旦换行到新行, 元素开始向右对齐.我想我可以动态地 将新的FlowLayoutPanels添加到TableLayoutControl的新行中,然后 我有一个与第一种情况类似的问题,在这种情况下,我需要手动进行 如果行大小增加/缩小,则在行之间重新分配元素.
  • TableLayoutPanel - main issue I have with using this are the events when elements grown and have to shift from 3-2 grid to 2-4, as TableLayoutPanel does not seem to deal well with those.
  • AutoSize FlowLayoutPanel that can grow and shrink inside of TableLayoutControl - my main problem with this solution is that it only centers one row inside the Form, once it wraps to a new row, the elements start to align to the right side. I suppose I can dynamically add new FlowLayoutPanels to new rows of a TableLayoutControl, but then I have a similar issue as the first scenario where I need to manually redistribute elements between rows if they grow/shrink in size.

我想知道我是否缺少一些可以帮助我处理增长/收缩事件而无需创建自己的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-已添加Forth元素,将其包装到新行并居中
  • D-元素已放大,现在环绕在第二个元素上,居中

推荐答案

以下是一个重现您描述的行为的示例.
它利用了承载多个FlowLayoutPanel的TableLayoutPanel.

Here's an example that reproduces the behaviour you described.
It makes use of a TableLayoutPanel which hosts multiple FlowLayoutPanels.

一个重要的细节是子FlowLayoutPanels的锚定:它们需要锚定到 Top-Bottom :这会使面板位于TableLayoutPanel行的中心.

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):

  1. 添加一个面板,设置为Dock.Bottom.右键单击SendToBack()
  2. 添加TableLayoutPanel(在这里称为 tlp1 ),设置为:
    • AutoScroll = trueAutoSize = true
    • AutoSizeMode = GrowAndShrinkDock.Fill
    • 保留1列,设置为自动调整大小",保留1行,设置为自动调整大小"
  1. Add one Panel, set Dock.Bottom. Right click and SendToBack(),
  2. Add a TableLayoutPanel (here, called tlp1), 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 = trueAutoSizeMode = GrowAndShrink
  • Set its Anchor to Top, Bottom <= this is !important, the layout won't work correctly without it: it allows to center the FLP inside the TLP Row,
  • AutoSize = true, AutoSizeMode = GrowAndShrink



using System.Drawing;
using System.Linq;
using System.Windows.Forms;


public partial class TLPTest1 : Form
{
    public TLPTest1()
    {
        InitializeComponent();
        this.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),
        };
        this.flp1.Controls.Add(pBox);
        this.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 (this.chkRandom.Checked)
            size = sizes[rnd.Next(sizes.Length)];

        PictureBox pBox = new PictureBox() {
            Anchor = AnchorStyles.None,
            BackColor = colors[rnd.Next(colors.Length)],
            MinimumSize = size,
            Size = size
        };

        bool drawborder = false;
        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 = this.tlp1.GetControlFromPosition(0, this.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) >= this.tlp1.Width)
        {
            var flp = new FlowLayoutPanel() {
                Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
                AutoSize = true,
                AutoSizeMode = AutoSizeMode.GrowAndShrink,
            };
            flp.Controls.Add(pBox);

            this.tlp1.SuspendLayout();
            this.tlp1.RowCount += 1;
            this.tlp1.Controls.Add(flp, 0, this.tlp1.RowCount - 1);
            this.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(this.tlp1, parent);
            parent.Dispose();
        }
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
    {
        int ctlPosition = this.tlp1.GetRow(control);
        if (ctlPosition < this.tlp1.RowCount - 1)
        {
            for (int i = ctlPosition; i < this.tlp1.RowCount - 1; i++)
            {
                tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
            }
        }
        tlp.RowCount -= 1;
    }
}

这篇关于在FlowLayoutPanel中将多行控件居中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆