简体   繁体   中英

How to center a bunch of controls programmatically in c#

I'm pretty new to this community, and I have this app that adds controls programmatically. I would like to center all of the added controls sort of like selecting them and pressing center on Visual Studio. And no I don't want to center each one aside. Here's the code I used to get all the controls:

    private void GetAllControl(Control c, List<Control> list)
    {
        //gets all controls and saves them to a list
        foreach (Control control in c.Controls)
        {
            list.Add(control);
        }
    }

    //And then call it like this

        List<Control> list = new List<Control>();
        GetAllControl(PNL_custom, list);
        foreach (Play_panel m in list)
        {
            //And here I want to insert that center code
        }

Thanks in advance,

VBTheory

"And no I don't want to center each one aside."

So you want to "Align" the List of Controls?...as in:

Format --> Align --> Centers

Format --> Align --> Middles

If yes , then compute the center of each control and add up the X, Y coords so you can compute an "average" point (the center of mass). Now you can iterate over the controls and use that as the aligning X or Y value, depending on your desired direction. Simply subtract half the width or height and keep the other value.

Something like:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        List<Control> list = new List<Control>();
        GetAllControl(PNL_custom, list);
        CenterControls(list, Direction.Vertical);
    }

    public enum Direction
    {
        Vertical,
        Horizontal
    }

    private void CenterControls(List<Control> controls, Direction direction)
    {
        if (controls.Count > 1)
        {
            int xSum = 0;
            int ySum = 0;
            Point center;
            foreach (Control ctl in controls)
            {
                center = new Point(ctl.Location.X + ctl.Width / 2, ctl.Location.Y + ctl.Height / 2);
                xSum = xSum + center.X;
                ySum = ySum + center.Y;
            }
            Point average = new Point(xSum / controls.Count, ySum / controls.Count);
            foreach (Control ctl in controls)
            {
                switch (direction)
                {
                    case Direction.Vertical:
                        ctl.Location = new Point(average.X - ctl.Width / 2, ctl.Location.Y);
                        break;

                    case Direction.Horizontal:
                        ctl.Location = new Point(ctl.Location.X, average.Y - ctl.Height / 2);
                        break;
                }
            }
        }
    }

    private void GetAllControl(Control c, List<Control> list)
    {
        //gets all controls and saves them to a list
        foreach (Control control in c.Controls)
        {
            list.Add(control);
        }
    }
}

Here's how to center the controls as a GROUP . It's almost the same as before except we compute how far the center of mass for the group has to move to become the center of the parent control. Then we iterate over all the controls and offset their locations by that much. This centers them all while maintaining their positions relative to each other:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        List<Control> list = new List<Control>();
        GetAllControl(PNL_custom, list);
        CenterControlsAsGroup(list, Direction.Both); // center group in the center of the parent
    }

    public enum Direction
    {
        Vertical,
        Horizontal,
        Both
    }

    private void CenterControlsAsGroup(List<Control> controls, Direction direction)
    {
        if (controls.Count > 1)
        {
            int xSum = 0;
            int ySum = 0;
            Point center;
            foreach (Control ctl in controls)
            {
                center = new Point(ctl.Location.X + ctl.Width / 2, ctl.Location.Y + ctl.Height / 2);
                xSum = xSum + center.X;
                ySum = ySum + center.Y;
            }
            Point average = new Point(xSum / controls.Count, ySum / controls.Count);

            center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
            int xOffset = center.X - average.X;
            int yOffset = center.Y - average.Y;

            foreach (Control ctl in controls)
            {
                switch (direction)
                {
                    case Direction.Vertical:
                        ctl.Location = new Point(ctl.Location.X + xOffset, ctl.Location.Y);
                        break;

                    case Direction.Horizontal:
                        ctl.Location = new Point(ctl.Location.X, ctl.Location.Y + yOffset);
                        break;

                    case Direction.Both:
                        ctl.Location = new Point(ctl.Location.X + xOffset, ctl.Location.Y + yOffset);
                        break;
                }
            }
        }
    }

    private void GetAllControl(Control c, List<Control> list)
    {
        //gets all controls and saves them to a list
        foreach (Control control in c.Controls)
        {
            list.Add(control);
        }
    }

}

Get the width and height of the control's container (which is either another control or the form). The coordinates of controls are distances in pixels, relative to the upper left corner of their containers (which is (0,0)). So all you have to do is set a control's x coordinate to be (form width - control width) / 2 . Same goes for height.

All your controls will be added to a container (most likely the Form) though they could easily be in a group box etc.

To align them center inside the container you need to do a little maths and position them yourself :)

Before you can center the control in the container you need to find the midpoint of the container. This will be the container's width / 2 : container's height / 2.

I will use a single control called cmdButton1 to highlight - you will want to iterate through your list of controls and do this to all of them in turn.

int midParentX = cmdButton1.Parent.width / 2;
int midParentX = cmdButton1.Parent.height / 2;

Then you can position a control at this midpoint:

cmdButton1.Location.X = midParentX;
cmdButton1.Location.Y = midParentY;

However, your control (cmdButton in my example) is anchored at (0,0) being the top left corner of the control, so we need to move it back and up by half its own width and height

cmdButton1.Location.X -= (cmdButton1.width / 2);
cmdButton1.Location.Y -= (cmdButton1.height / 2);

To be more relevant:

foreach (Play_panel m in list)
{
    int pX = m.Parent.width;
    int pY = m.Parent.height;

    m.Location.X = (pX / 2) - (m.width / 2);
    m.Location.Y = (pY / 2) - (m.height / 2);
}

So i did it on this way:

    public enum ArrangeOrientation : int {
        None,
        Horizonatal,
        Vertical,
        HorizontalGrid,
        VerticalGrid,
        TopLeftGrid,
        TopRightGrid,
        BottomLeftGrid,
        BottomRightGrid
    }

    private void ArrangeButtons(List<Control> controls, List<Control> parents, ArrangeOrientation orientation, double shrinkFactor = 1d) {
        if(controls == null) return;
        if(parents == null) parents = new List<Control>();
        List<Control> childs = new List<Control>();
        Control parent = null;

        foreach(Control ctrl in controls) {
            if(parent == null && !parents.Contains(ctrl.Parent)) {
                parents.Add(ctrl.Parent);
                parent = ctrl.Parent;
            }
            if(parent == ctrl.Parent)
                childs.Add(ctrl);
        }
        if(parent != null && childs.Count > 0) {
            ArrangeControlsToGridLayout(childs, orientation, shrinkFactor);
            ArrangeButtons(controls, parents, orientation, shrinkFactor);
        }
    }

    private void ArrangeControlsToGridLayout(List<Control> controls, ArrangeOrientation orientation, double shrinkFactor = 1d) {
        // do nothing if nothing set
        if(orientation == ArrangeOrientation.None) return;
        if(shrinkFactor == 0d|| shrinkFactor > 1d) shrinkFactor = 1d;

        // buffer controls in separate list to avoid manipulating parameter
        List<Control> ctrl = new List<Control>(controls.ToArray());
        // remove invisible controls
        int j = 0;
        while(j < ctrl.Count) {
            if(!ctrl[j].Visible) ctrl.RemoveAt(j);
            else j++;
        }

        // loop arrangement
        int count = ctrl.Count;
        int xDelta, yDelta, xOffs, yOffs, y, x, columns, rows, parentWidth, parentHeight, xShrinkOffs, yShrinkOffs;
        if(count >= 1) {

            // parents size
            parentWidth = ctrl[0].Parent.Width;
            parentHeight = ctrl[0].Parent.Height;
            // shrink factor offset
            parentWidth = Convert.ToInt32(parentWidth * shrinkFactor);
            parentHeight = Convert.ToInt32(parentHeight * shrinkFactor);
            // shrink factor offset
            xShrinkOffs = Convert.ToInt32((ctrl[0].Parent.Width - parentWidth) / 2d);
            yShrinkOffs = Convert.ToInt32((ctrl[0].Parent.Height - parentHeight) / 2d);

            // calculate columns rows grid layout                            
            if(orientation == ArrangeOrientation.Horizonatal) {
                rows = 1;
                columns = count;
            }
            else if(orientation == ArrangeOrientation.Vertical) {
                rows = count;
                columns = 1;
            }
            else if(orientation == ArrangeOrientation.TopLeftGrid
                || orientation == ArrangeOrientation.TopRightGrid
                || orientation == ArrangeOrientation.BottomLeftGrid
                || orientation == ArrangeOrientation.BottomRightGrid) {
                rows = 1;
                columns = count;
            }
            else {
                rows = Convert.ToInt32(Math.Floor(Math.Sqrt(count)));
                if(Math.Sqrt(count) % 1d != 0d) rows++;
                columns = count / rows + (count % rows != 0 ? 1 : 0);
            }
            if(orientation == ArrangeOrientation.HorizontalGrid) {
                int swap = columns;
                columns = rows;
                rows = columns;
            }

            // calculate position offsets, grid distance
            xDelta = parentWidth / count;
            yDelta = parentHeight / count;
            xOffs = xDelta / 2;
            yOffs = yDelta / 2;                
            if(orientation == ArrangeOrientation.TopLeftGrid) {                                                     
            }
            else if(orientation == ArrangeOrientation.TopRightGrid) {
                xOffs = parentWidth - xOffs;
                xDelta = -xDelta;
            }
            else if(orientation == ArrangeOrientation.BottomLeftGrid) {
                yOffs = parentHeight - yOffs;
                yDelta = -yDelta;
            }
            else if(orientation == ArrangeOrientation.BottomRightGrid) {
                xOffs = parentWidth - xOffs;
                yOffs = parentHeight - yOffs;
                xDelta = -xDelta;
                yDelta = -yDelta;
            }
            else {
                xDelta = parentWidth / columns;
                yDelta = parentHeight / rows;
                xOffs = xDelta / 2;
                yOffs = yDelta / 2;
            }

            // fit controls in grid layout               
            Point pRoot = new Point(/*ctrl[0].Parent.Location.X + */xOffs, /*ctrl[0].Parent.Location.Y + */yOffs);
            y = 0; x = 0;
            for(int i = 0; i < count; i++) {

                if(orientation == ArrangeOrientation.VerticalGrid) {
                    // actual x/y - points zero based index
                    y = Convert.ToInt32(Math.Floor((double)i % rows));
                    // next row? zero based index
                    if(i % rows == 0 && i != 0) x++;
                }
                else {
                    // actual x/y - points zero based index
                    x = Convert.ToInt32(Math.Floor((double)i % columns));
                    // next row? zero based index
                    if(i % columns == 0 && i != 0) y++;
                    if(orientation == ArrangeOrientation.TopLeftGrid
                        || orientation == ArrangeOrientation.TopRightGrid
                        || orientation == ArrangeOrientation.BottomLeftGrid
                        || orientation == ArrangeOrientation.BottomRightGrid)
                        y = x;
                }                   // assign controls to grid
                ctrl[i].Location = new Point(pRoot.X + x * xDelta - ctrl[i].Size.Width / 2 + xShrinkOffs, pRoot.Y + y * yDelta - ctrl[i].Size.Height / 2 + yShrinkOffs);

            }
        }
    }

When there are multiple controls,above code was placing all controls on top of each other.

All i am trying to do is to center all the controls inside a panel but to put them next to each other instead of putting one over the other.

Here is my modified code (FYI this will center controls in 1 line not multiple line):

   public enum Direction
    {
        Vertical,
        Horizontal,
        Both
    }

    public void CenterControls(List<Control> controls, Direction direction)
    {
        if (controls.Count > 1)
        {
             int controls_sum_width = 0;
            int controls_seperation = 20;
            int parentwidth = 0;               

            Point center;
            foreach (Control ctl in controls)
            {
                 controls_sum_width = controls_sum_width + ctl.Width + controls_seperation;
            }

            Point Container_center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
            parentwidth = controls[0].Parent.Width;
            int xoffset = (parentwidth - controls_sum_width) / 2;


            int Location_X = 0;
            foreach (Control ctl in controls)
            {
                center = new Point( ctl.Width / 2,  ctl.Height / 2);
                int yOffset = Container_center.Y - center.Y;
                switch (direction)
                {
                    case Direction.Vertical:
                        ctl.Location = new Point(ctl.Location.X + xoffset, ctl.Location.Y);
                        break;

                    case Direction.Horizontal:
                        ctl.Location = new Point(ctl.Location.X, yOffset);
                        break;

                    case Direction.Both:
                        ctl.Location = new Point(Location_X + xoffset,  yOffset);
                        break;
                }
                Location_X = Location_X + ctl.Width+ controls_seperation;
            }
        }
        else
        {
            Point parent_center;
            Point center;
            parent_center = new Point(controls[0].Parent.Width / 2, controls[0].Parent.Height / 2);
            center = new Point(controls[0].Location.X + controls[0].Width / 2, controls[0].Location.Y + controls[0].Height / 2);
            int xOffset = parent_center.X - center.X;
            int yOffset = parent_center.Y - center.Y;
            switch (direction)
            {
                case Direction.Vertical:
                    controls[0].Location = new Point(controls[0].Location.X + xOffset, controls[0].Location.Y);
                    break;

                case Direction.Horizontal:
                    controls[0].Location = new Point(controls[0].Location.X, controls[0].Location.Y + yOffset);
                    break;

                case Direction.Both:
                    controls[0].Location = new Point(controls[0].Location.X + xOffset, controls[0].Location.Y + yOffset);
                    break;
            }
        }
    }

    public void GetAllControl(Control c, List<Control> list)
    {
        //gets all controls and saves them to a list
        foreach (Control control in c.Controls)
        {
            list.Add(control);
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM