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
):
- 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
- Add a
FlowLayoutPanel
(here, calledflp1
), positioned inside theTableLayoutPanel
. 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 theFLP
inside theTLP
Row, AutoSize = true
,AutoSizeMode = GrowAndShrink
- Set its Anchor to
- Add a Button (called
btnAddControl
) - Add a second Button (called
btnRemoveControl
) - Add a CheckBox (called
chkRandom
) - 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();
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
ofFlowLayoutPanel
totrue
- Set
AutoSizeMode
ofFlowLayoutPanel
toGrowAndShrink
- Set
Anchor
property ofFlowLayoutPanel
toTop, Bottom
- Set
- Use a
TableLayoutPanel
for hosting theFlowLayoutPanel
- Use a single
Column
and a singleRow
inTableLayoutPanel
. - Set
Dock
property ofTableLayoutPanel
toBottom
.
- Use a single
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);
}
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:
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.
- Using
Anchor
property - Using the layout control with help of
Docking
andAnchor
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
- Add TableLayout Control, Set it’s Dock property to Fill.
- Add 1 Row with Size Type style Percent 100%
- Add 3 Columns Column1(Size Type – Percent(100%)), Column2(Size Type – Absolute(200px)), Column3(Size Type – Percent(100%)).
- Now Add Panel Control to Column2 and Set it’s Dock property to Fill
- 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
Why Must We Define Both == and != in C#
Expression.Lambda and Query Generation at Runtime, Simplest "Where" Example
What Method in the String Class Returns Only the First N Characters
How to Generate a Hashcode from a Byte Array in C#
Simulate Steady CPU Load and Spikes
Httpwebrequest Not Passing Credentials
Entity Framework 6 Code First Custom Functions
Viewing PDF in Windows Forms Using C#
How to Pass Values Between Forms in C# Windows Application
Reading a Key from the Web.Config Using Configurationmanager
When Should I Use the Hashset<T> Type
How to Loop Through a List<T> and Grab Each Item
Wait Some Seconds Without Blocking UI Execution
Button Inside a Winforms Textbox