Search code examples
c#winformstablelayouttablelayoutpanel

C# WinForms TableLayoutPanel acts weird when adding two elements on one row


Wanna use WinForms' TableLayoutPanel in such way: Each row has two columns, one takes 80%, the second one 20%. On the first column, there's a TextBox, on the second: a button. I have made a button that adds such row to the table, but, as seen in the video, this goes awful. Before that i used FlowLayoutPanel, but was advised to use Table- instead. Here's a demo video: https://youtu.be/4hcBAeWCtTQ | And here's the Add button code:

private void manual_packages_add_Click(object sender, EventArgs e) {
  static Size MulSize(Size sz, Size accord, int percent) {
    static float Percent(float n, int p) => n * (p / 100);

    SizeF origF = (SizeF)sz;
    SizeF accordF = (SizeF)accord;
    origF.Height += Percent(accordF.Height, percent);
    return origF.ToSize();
  }

  TextBox tbx = new() {
    Name = $"manual_packages_pkg{manual_nextPkgTbxIndex++}",
    Size = new((int)Math.Round(manual_packages_list.ColumnStyles[0].Width),
               (int)Math.Round(manual_packages_list.RowStyles[0].Height)),
    MaxLength = 34,
    BorderStyle = BorderStyle.Fixed3D,
  };
  Button rm = new() { Name = $"manual_packages_rm{manual_nextPkgTbxIndex}",
                      Text = "i suffer 💀💀💀💀💀" };
  manual_packages_list.Controls.Add(tbx, 0, manual_packages_list.RowCount++);
  manual_packages_list.Controls.Add(rm, 1, manual_packages_list.RowCount++);

  // sizing
  Control? container = tbx.Parent;
  bool firstIter = true;
  while (true) {
    container = firstIter ? container : container.Parent;
    if (container == null)
      break;
    container.Size = MulSize(container.Size, tbx.Size, 100);
    firstIter = false;
  }
}

Initial: a normally aligned two elements on a row.

Initial state

Three times clicked the Add button: textbox on one row, button on the another

Clicked the Add button 3 times

Clicked 4 times and more: no pairs except the initial one

Clicked the Add button 4 times and more

I tried removing the sizing (which didn't result in any good), i coded it to align everything, but it didn't work.


Solution

  • What you should have done is displayed the borders in the TableLayoutPanel and you would have seen that you were actually adding only one control per row, not two. The issue is here:

    manual_packages_list.Controls.Add(tbx, 0, manual_packages_list.RowCount++);
    manual_packages_list.Controls.Add(rm, 1, manual_packages_list.RowCount++);
    

    You are incrementing manual_packages_list.RowCount in both of those lines, so every control will be added to a different row. If you expect both those controls to be added to the same row then you can't increment the row count after adding the first control. At the very least, remove the increment from the first line:

    manual_packages_list.Controls.Add(tbx, 0, manual_packages_list.RowCount);
    manual_packages_list.Controls.Add(rm, 1, manual_packages_list.RowCount++);
    

    That said, I don't think that's really the way to do it. I would think that you would be starting with a row count of 1 and then, when you want to add controls to a second row, you would increment the row count first, then add the two controls. That would actually look like this:

    manual_packages_list.Controls.Add(tbx, 0, ++manual_packages_list.RowCount);
    manual_packages_list.Controls.Add(rm, 1, manual_packages_list.RowCount);
    

    The way you're (intending to be) doing it, you already have an empty row in the table with no controls in it, then you add controls to that row, then you add another empty row. It would make more sense to not have the empty row by default and only add an empty row when you need one, then add controls to it.