Center Multiple Rows of Controls in a Flowlayoutpanel

Center multiple rows of controls in a FlowLayoutPanel

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

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

How to set FlowLayoutPanel contents at Center of Form

Using a single cell TableLayoutPanel which is suitable for centering the content and an auto-size FlowLayoutPanel you can achieve what you are looking for:

Perform these settings on the controls:

  • Add your images to a FlowLayoutPanel
    • Set AutoSize of FlowLayoutPanel to true
    • Set AutoSizeMode of FlowLayoutPanel to GrowAndShrink
    • Set Anchor property of FlowLayoutPanel to Top, Bottom
  • Use a TableLayoutPanel for hosting the FlowLayoutPanel
    • Use a single Column and a single Row in TableLayoutPanel.
    • Set Dock property of TableLayoutPanel to Bottom.

This way, when you add or remove images dynamically, all images will be shown at bottom center of the form.

How to center align multiple radio buttons in FlowLayoutPanel?

To fine-tune positions of controls in their containers you can modify their Margin property.

Assuming you have the controls to center in a list:

List<Control> ctls = new List<Control>();
foreach (Control c in flowLayoutPanel1.Controls) ctls.Add(c);

You can call a function to align them:

void centerControls(List<Control> ctls, Control container)
{
int w = container.ClientSize.Width;
int marge = (w - ctls.Sum(x => x.Width)) / 2;
Padding oldM = ctls[0].Margin;
ctls.First().Margin = new Padding(marge, oldM.Top, oldM.Right, oldM.Bottom);
ctls.Last().Margin = new Padding(oldM.Left, oldM.Top, oldM.Right, marge);
}

Sample Image

Sample Image

Call the function whenever you have added or removed a control:

centerControls(ctls, flowLayoutPanel1);

You will need to reset the Margins when you add new Buttons..

Note that I only change the outer Margins, not the space between. To do the latter you can calculate the space and change Margins for all controls:

Sample Image

void spaceControls(List<Control> ctls, Control container)
{
int w = container.ClientSize.Width;
int marge = (w - ctls.Sum(x => x.Width)) / (ctls.Count * 2 );
Padding oldM = ctls[0].Margin;
Padding newM = new Padding(marge, oldM.Top, marge, oldM.Bottom);
foreach (Control c in ctls) c.Margin = newM;
}

Also do think of what shall happen when more than one row of RadioButtons is there! You may want to put more effort in maintinang the List(s)..

Also note that users do not like their controls to jump around a lot!

Update: Do have a look ar Reza's post here and here for ways to achieve something like the 1st layout in a code-free way!

Align controls to center in a FlowLayout

You can do it two ways but with some limitation of each one.

  1. Using Anchor property
  2. Using the layout control with help of Docking and Anchor properties.

Method 1: Anchor Property

Controls are anchored by default to the top left of the form which
means when the form size will be changed, their distance from the top
left side of the form will remain constant. If you change the control
anchor to bottom left, then the control will keep the same distance
from the bottom and left sides of the form when the form if resized.

Turning off the anchor in a direction will keep the control centred in
that direction when resizing.

Example :

public TestForm12()
{
InitializeComponent();

Button btn = new Button();
btn.Width = this.Width - 10;
btn.Height = 20;
btn.Left = (this.ClientSize.Width - btn.Width) / 2;
btn.Top = (this.ClientSize.Height - btn.Height) / 2;
btn.Text = "click me";
this.Controls.Add(btn);
btn.Anchor = AnchorStyles.None;

}

2. Using the layout control

  1. Add TableLayout Control, Set it’s Dock property to Fill.
  2. Add 1 Row with Size Type style Percent 100%
  3. Add 3 Columns Column1(Size Type – Percent(100%)), Column2(Size Type – Absolute(200px)), Column3(Size Type – Percent(100%)).
  4. Now Add Panel Control to Column2 and Set it’s Dock property to Fill
  5. Add Buttons to this control and set their Size as you want and Set Their Anchor Property to AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top

Example - Designer.cs code snippet of the form.

private void InitializeComponent()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.panel1 = new System.Windows.Forms.Panel();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 200F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(284, 262);
this.tableLayoutPanel1.TabIndex = 0;
//
// panel1
//
this.panel1.Controls.Add(this.button2);
this.panel1.Controls.Add(this.button1);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(45, 3);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(194, 256);
this.panel1.TabIndex = 0;
//
// button1
//
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.button1.Location = new System.Drawing.Point(3, 9);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(188, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
//
// button2
//
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.button2.Location = new System.Drawing.Point(3, 38);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(188, 23);
this.button2.TabIndex = 0;
this.button2.Text = "button1";
this.button2.UseVisualStyleBackColor = true;
//
// TestForm11
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.tableLayoutPanel1);
this.Name = "TestForm11";
this.Text = "TestForm11";
this.tableLayoutPanel1.ResumeLayout(false);
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);

}

#endregion

private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button1;

Hope this help..

Centering a group of Controls

FlowLayoutPanel can wrap controls if doesn't have place for them in current line

flowLayoutPanel1.Anchor = 
AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
for (int i = 0; i < 10; i++)
{
flowLayoutPanel1.Controls.Add(new Panel {BackColor = Color.Green, Width = 75, Height = 100, Margin = new Padding(4)});
}

Dynamically position a control from the center of a panel

The way I get stuff to center in the TableLayoutPanel is to have two extra columns or rows that I have set to 50% each. Any columns or rows between the two on the outside will automatically center based on the percentage. Of course, this option works best with columns or rows that are set to specific sizes or for auto adjust types.

Why flowlayoutPanel is extending horizontally?

Use

this.panel.FlowDirection = System.Windows.Forms.FlowDirection.LeftToRight;

instead of

this.panel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;

if you want only one column than please add below code to your application just after control added to your flowlayoutpanel

this.panel.SetFlowBreak(<<YOUR_ADDED_CONTROL_NAME>>, true);

Example

Button btn1 = new Button();
btn1.Text = "TEST";
btn1.Height = 30;
btn1.Width = 100;

this.panel.Controls.Add(btn1);
this.panel.SetFlowBreak(btn1, true);


Related Topics



Leave a reply



Submit