在 FlowLayoutPanel 中居中多行控件

5

我正在尝试创建一个面板,可以托管动态添加的控件。有两个注意事项:

  • 会有很多控件,因此当面板达到其宽度限制并垂直滚动时,应将元素包装成新行。
  • 控件的大小可能会改变,这将改变单行中适合的元素数量。

我看过一些提议的解决方案来在表单中居中动态控件,并因以下原因拒绝了这些方案:

  • TableLayoutPanel-我使用它的主要问题是当元素增长并且需要从3-2网格转换为2-4网格时的事件,因为TableLayoutPanel似乎不能很好地处理这些情况。
  • AutoSize FlowLayoutPanel可以在TableLayoutControl内部增长和缩小-我对这种解决方案的主要问题是它只在表单中心对齐一行,一旦它换行,元素就开始向右对齐。我想我可以动态地将新的FlowLayoutPanels添加到TableLayoutControl的新行中,但是那么我就有了第一个场景中的类似问题,即如果它们在大小上增长/缩小,则需要手动重新分配元素。
我在想是否有一些功能可以帮助我处理增长/缩小事件,而无需创建自己的TableLayoutPanel变体?
编辑:
以下是功能的草稿:
A - 两个元素居中于面板中
B - 添加第三个元素,三个元素都居中
C - 添加第四个元素,换行并居中
D - 元素被放大,现在在第二个元素上换行,居中

Draft

1个回答

8
以下是一个重现您所描述行为的示例。它使用了一个TableLayoutPanel来承载多个FlowLayoutPanels。
一个重要的细节是子FlowLayoutPanels的锚定:它们需要被锚定到顶部和底部: 这会导致面板定位在TableLayoutPanel行的中心位置。
请注意,在窗体构造函数中,删除了一个RowStyles。这也非常重要:TLP(相当古怪的家伙),即使您只有一行(或一列,同样的事情),也会保留两个RowStyles。第二种样式将应用于您添加的第一行;仅适用于第一行,而不是其他行:这可能会破坏布局。
另一个异常是它不提供方法来删除一个行,所以我做了一个。它是功能性的但还需要改进,包括更多的验证。
查看当前功能的图形样本。如果您需要帮助实施其他内容,请留言。
要构建此项目,请将以下控件添加到表单中(这里称为FLPTest1):
1. 添加一个面板,设置Dock.Bottom。右键单击并SendToBack()
2. 添加一个TableLayoutPanel(这里称为tlp1),设置: - AutoScroll = true, AutoSize = true, - AutoSizeMode = GrowAndShrink, Dock.Fill, - 保留1列,设置为AutoSize, 和1行,设置为AutoSize 3. 在TableLayoutPanel内部放置一个FlowLayoutPanel(这里称为flp1)。其实不是必须的,只是为了这个示例代码。 - 将其锚定设置为Top, Bottom <=,这很重要,没有它布局将无法正常工作:它允许在TLP行内居中FLP, - 设置AutoSize = true, AutoSizeMode = GrowAndShrink。
4. 添加一个按钮(称为btnAddControl)。 5. 添加第二个按钮(称为btnRemoveControl)。 6. 添加一个复选框(称为chkRandom)。 7. 将此处代码粘贴到窗体的代码文件中。

TableLayoutPanel Flow

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;
    }
}

非常感谢。这比我想象的要详细得多,哈哈。 - JagdCrab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接