Center multiple rows of controls in a FlowLayoutPanel

后端 未结 1 1042
深忆病人
深忆病人 2020-11-27 08:37

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 wr
相关标签:
1条回答
  • Here's an example that reproduces the behaviour you described.
    It makes use of a TableLayoutPanel which hosts multiple FlowLayoutPanels.

    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.

    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.


    To build this add the following controls to a Form (here, called FLPTest1):

    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
    3. Add a FlowLayoutPanel (here, called flp1), positioned inside the TableLayoutPanel. It's not actually necessary, just for this sample code
      • 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
    4. Add a Button (called btnAddControl)
    5. Add a second Button (called btnRemoveControl)
    6. Add a CheckBox (called chkRandom)
    7. Paste the code here inside a Form's code file

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题