Search code examples
c#winformsdock

Groupbox created in code doesn't dock correctly to parent TableLayoutPanel


In my C# WinForm application I have a tableLayoutPanel with only one row and two columns. The left column contains a TreeView, the right one another tableLayoutPanel which was set to Dock: Fill in the designer. I want to create several GroupBoxes programmatically that should use the full width of this second column also when the form is resized.

In the designer of Visual Studio it looks like this:

Designer View of my tableLayout Panel

But when the application runs, the GroupBoxes don't use the complete width and it looks like this:

Dock: Fill is not used

I'm using two methods to create the GroupBoxes (you can ignore the int values):

private void addDashboardRow(int i, int s)
{
     // Add one row to dashboardTableLayoutPanel
     sensorTableLayoutPanel.RowCount++;
     // Maybe this one is wrong, but SizeType.AutoSize doesn't work either
     sensorTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 75f));
     sensorTableLayoutPanel.Controls.Add(createSensorGroupbox(i, s));
}
private GroupBox createSensorGroupbox(int i, int sen)
{
     GroupBox g = new GroupBox();
     //g.Width = 500;          // I don't want fixed sizes, height of 75px is sufficient
     //g.Height = 75;
     g.Dock = DockStyle.Fill;
     g.Text = "Sensor " + sen.ToString();

     // just one label as example...
     Label temp = new Label();
     temp.Text = "Temperature: ";
     temp.Location = new Point(5, 20);
            
     Label tempVal = new Label();
     tempVal.Text = "°C";
     tempVal.Location = new Point(5, 50);
     tempVal.Name = "Sensor" + sen.ToString() + "_temp";

     //[... more labels]
     g.Controls.Add(temp);
     g.Controls.Add(tempVal);

     return g;
}

My questions are:

  1. How can I fit the GroupBox into the complete width of the parent container?
  2. How can I delete all rows? E.g. when looking up the sensors during run time all GroupBoxes are dublicated, but everything should be created newly.
  3. Layout of the question: how should I format terms like TableLayoutPanel correctly in this forum? It's my second question here and I haven't found out, yet.

For the second question. Currently I'm using this method that doesn't do what I'm expecting:

private void clearDashboardTable()
{
     if (sensorTableLayoutPanel.Controls.Count == sensorList.Count) {
          //MessageBox.Show(sensorTableLayoutPanel.Controls.Count.ToString());
          foreach (Control con in sensorTableLayoutPanel.Controls)
          {
               sensorTableLayoutPanel.Controls.Remove(con);
               con.Dispose();
          }
          //sensorTableLayoutPanel.Dispose();
          sensorList.Clear();
     }
}

I commented out what I've tried so far in the above code or added additional comments. I used this topic to try to understand the basics of dynamic groupbox creation: Add Row Dynamically in TableLayoutPanel


Solution

  • One way to achieve the outcome you describe is to make a custom UserControl containing a docked GroupBox that contains a docked TableLayoutPanel.

    user control designer

    To adjust the width when the container changes, attach to the SizeChanged event of the parent.

    public partial class CustomGroupBox : UserControl
    {
        public CustomGroupBox() => InitializeComponent();
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            if((Parent != null) &&!DesignMode)
            {
                Parent.SizeChanged += onParentSizeChanged;
            }
        }
        private void onParentSizeChanged(object? sender, EventArgs e)
        {
            if(sender is FlowLayoutPanel flowLayoutPanel)
            {
                Debug.WriteLine($"{flowLayoutPanel.Width}");
                Width = 
                    flowLayoutPanel.Width - 
                    SystemInformation.VerticalScrollBarWidth;
            }
        }
        public new string Text
        {
            get=>groupBox.Text;
            set=>groupBox.Text = value;
        }
    }
    

    Main Form

    The main form is laid out similar to your image except to substitute a FlowLayoutPanel on the right side.

    main form designer

    Now test it out with this minimal code in the method that loads the Main Form:

    public partial class MainForm : Form
    {
        int _id = 0;
        public MainForm()
        {
            InitializeComponent();
            flowLayoutPanel.AutoScroll= true;
            for (int i = 0; i < 5; i++)
            {
                var customGroupBox = new CustomGroupBox
                {
                    Text = $"Sensor {++_id}",
                    Width = flowLayoutPanel.Width - SystemInformation.VerticalScrollBarWidth,
                    Padding = new Padding(),
                    Margin = new Padding(),
                };
                flowLayoutPanel.Controls.Add(customGroupBox);
            }
        }
    }
    

    resizing


    Clear

    An example of clearing the sensors would be to add a button and set a handler in the same Load method:

    buttonClear.Click += (sender, e) => flowLayoutPanel.Controls.Clear();