Удаление строки внутри TableLayoutPanel создает проблемы с макетом - PullRequest
1 голос
/ 13 марта 2019

У меня есть приложение WinForms с TableLayoutPanel;это код определения:

tableLayoutPanel1 = new TableLayoutPanel();
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.AutoScroll = true;

tableLayoutPanel1.RowCount = users.Count + 1;
tableLayoutPanel1.ColumnCount = 1;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.FixedSize;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));

foreach (String user in users)
{
    tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 600F));
}
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 600F));

int index = 0;
foreach (String user in users)
{
    AddDockedControl(index, user);
    index++;
}
AddDockedControl(index, null);

panel1.Controls.Add(tableLayoutPanel1);

private void AddDockedControl(int row, String userName)
{
    AccountRowUC newUser = new AccountRowUC(this, userName, row);
    newUser.BorderStyle = BorderStyle.FixedSingle;
    newUser.Dock = DockStyle.Top;
    tableLayoutPanel1.Controls.Add(newUser, 0, row);
}

Теперь, когда я хочу удалить одну из строк, я использую этот код:

public void RemoveRowAtIndex(int index)
{
    if (index >= tableLayoutPanel1.RowCount)
        return;

    // delete all controls of row that we want to delete
    for (int i = 0; i < tableLayoutPanel1.ColumnCount; i++)
    {
        var control = tableLayoutPanel1.GetControlFromPosition(i, index);
        tableLayoutPanel1.Controls.Remove(control);
    }

    // move up row controls that comes after row we want to remove
    for (int i = index + 1; i < tableLayoutPanel1.RowCount; i++)
    {
        for (int j = 0; j < tableLayoutPanel1.ColumnCount; j++)
        {
            var control = tableLayoutPanel1.GetControlFromPosition(j, i);
            if (control != null)
                tableLayoutPanel1.SetRow(control, i - 1);
        }
    }

    // remove last row

    tableLayoutPanel1.RowStyles.RemoveAt(tableLayoutPanel1.RowCount - 1);
    //tableLayoutPanel1.RowStyles.RemoveAt(index);
    tableLayoutPanel1.RowCount--;
}

Проблема заключается в том, что при удаленииСтрока, в нижней части таблицы остается большое пространство: TableLayoutPanel не будет возвращать размер panel1.

1 Ответ

1 голос
/ 18 марта 2019

Решение основывается на макете, описанном в комментариях и ранее опубликованном ответе:
Центрирование нескольких строк элементов управления в FlowLayoutPanel

Описание:
(Полныйкод тестовой формы, предоставленной внизу этого сообщения)

  1. Создайте новую форму (здесь, с именем frmTLPTest1)
  2. Добавьте две панели.Одна используется для размещения некоторых кнопок, другая будет контейнером TableLayoutPanel.
  3. Установите для панели «Контейнер» значение AutoScroll = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, установите все якоря (слева, сверху, справа, снизу)
  4. Внутри панели «Контейнер» перетащите новую панель TableLayoutPanel: установите еев AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Dock = DockStyle.Top
  5. Удалите все строки и столбцы из TableLayoutPanel, кроме одной из каждой (вы не можете удалить все).Установите размеры обоих AutoSize.

Важное примечание (также сообщается в связанном ответе):

В конструкторе Form один из RowStyles удаляется.Это важно: TLP сохранит 2 RowStyles.Один применяется к существующей строке;второй стиль будет применен к первой добавляемой строке: только к первой, а не к другим.Если этот стиль не будет удален, это нарушит компоновку.

Основные методы, используемые для добавления строк в / удаления строк из TableLayoutPanel, использования FlowLayoutPanel в качестве содержимого строки TLP и могут такжебыть использованы в качестве контейнера других элементов управления, в конце концов.

TlpAddRow(TableLayoutPanel tlp, bool addRowCount) метод:
Добавляет новую FlowLayoutPanel в ячейку указанной TableLayoutPanel и добавляет новую строку, если требуется.
Поскольку конструктор не разрешаетудалить все строки, первая строка (FlowLayoutPanel) не должна увеличивать количество строк: для аргумента addRowCount будет установлено значение false.

private Control TlpAddRow(TableLayoutPanel tlp, bool addRowCount)
{
    var flp = new FlowLayoutPanel() {
        Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
        AutoSize = true,
        AutoSizeMode = AutoSizeMode.GrowAndShrink,
    };

    tlp.SuspendLayout();
    if (addRowCount) tlp.RowCount += 1;
    tlp.Controls.Add(flp, 0, tlp.RowCount - 1);
    tlp.ResumeLayout(true);
    return flp;
}

TLPRemoveRow(TableLayoutPanel tlp, Control control) метод (перегружен):

Позволяет удалить строку из указанной TableLayoutPanel.Удаляемая строка может быть получена из элемента управления, который используется в качестве контейнера строки (здесь FlowLayoutPanel, но это может быть Panel, другой элемент TableLayoutPanel или какой-либо другой тип элемента управления Container).
Row также можетбыть удаленным путем непосредственного указания индекса строки.

private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
{
    int ctlRow = this.tlp1.GetRow(control);
    TLPRemoveRow(tlp, ctlRow);
}

private void TLPRemoveRow(TableLayoutPanel tlp, int row)
{
    if (row < this.tlp1.RowCount - 1) {
        for (int i = row; i < this.tlp1.RowCount - 1; i++) {
            tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
        }
    }
    tlp.RowCount -= 1;
}

Визуальные результаты этого макета:

TableLayoutPanel dynamic Flow

Поскольку легче понять, как это работает, тестируя, а не объясняя,Вот полный макет формы:

Тестовая форма (frmTLPTest1):

using System.Drawing;
using System.Linq;
using System.Windows.Forms;

public partial class frmTLPTest1 : Form
{
    public frmTLPTest1()
    {
        InitializeComponent();
        this.tlp1.RowStyles.RemoveAt(1);
    }

    private void SOfrmTest1_Load(object sender, EventArgs e)
    {
        TlpAddRow(this.tlp1, false);
    }

    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;
    Control selectedParent = null;

    private void btnAddControl_Click(object sender, EventArgs e)
    {
        Size size = new Size(125, 125);
        if (this.chkRandom.Checked)
            size = sizes[rnd.Next(sizes.Length)];

        PictureBox pBox = new PictureBox()
        {
            Anchor = AnchorStyles.None,
            BackColor = colors[rnd.Next(colors.Length)],
            MinimumSize = size,
            Size = size
        };

        bool drawborder = false;
        pBox.MouseEnter += (s, evt) => { drawborder = true;  pBox.Invalidate(); };
        pBox.MouseLeave += (s, evt) => { drawborder = false; pBox.Invalidate(); };
        pBox.MouseDown += (s, evt) => { selectedParent = pBox.Parent;
                                        selectedObject = pBox;  pBox.Invalidate();
        };
        pBox.Paint += (s, evt) => {
            if (drawborder) {
                ControlPaint.DrawBorder(evt.Graphics, pBox.ClientRectangle, 
                                        Color.White, ButtonBorderStyle.Solid);
            }
        };

        if (this.tlp1.RowCount == 0) TlpAddRow(this.tlp1, true); 

        var ctl = this.tlp1.GetControlFromPosition(0, this.tlp1.RowCount - 1);
        int overallWith = 0;
        if (ctl.Controls?.Count > 0)
            overallWith = ctl.Controls.OfType<Control>().Sum(c => c.Width + c.Margin.Left + c.Margin.Right);
        overallWith += ctl.Margin.Right + ctl.Margin.Left + pBox.Size.Width + pBox.Margin.Left + pBox.Margin.Right;

        if (overallWith >= this.tlp1.Width) {
            ctl = TlpAddRow(this.tlp1, true);
        }
        ctl.Controls.Add(pBox);
    }

    private void btnRemoveRow_Click(object sender, EventArgs e)
    {
        if (selectedParent is null) return;
        if (selectedParent.Controls.Count > 0)
        {
            for (int i = 0; i == selectedParent.Controls.Count - 1; i++) {
                selectedParent.Controls[i].Dispose();
            }
        }
        TLPRemoveRow(this.tlp1, selectedParent);
        selectedParent.Dispose();
    }

    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(this.tlp1, parent);
            parent.Dispose();
        }
    }

    private Control TlpAddRow(TableLayoutPanel tlp, bool addRowCount)
    {
        var flp = new FlowLayoutPanel() {
            Anchor = AnchorStyles.Top | AnchorStyles.Bottom,
            AutoSize = true,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
        };

        tlp.SuspendLayout();
        if (addRowCount) tlp.RowCount += 1;
        tlp.Controls.Add(flp, 0, tlp.RowCount - 1);
        tlp.ResumeLayout(true);
        return flp;
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, Control control)
    {
        int ctlRow = this.tlp1.GetRow(control);
        TLPRemoveRow(tlp, ctlRow);
    }

    private void TLPRemoveRow(TableLayoutPanel tlp, int row)
    {
        if (row < this.tlp1.RowCount - 1) {
            for (int i = row; i < this.tlp1.RowCount - 1; i++) {
                tlp.SetRow(tlp.GetControlFromPosition(0, i + 1), i);
            }
        }
        tlp.RowCount -= 1;
    }
}

Дизайнер тестовых форм :

partial class frmTLPTest1
{
    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.panToolbar = new System.Windows.Forms.Panel();
        this.btnRemoveRow = new System.Windows.Forms.Button();
        this.chkRandom = new System.Windows.Forms.CheckBox();
        this.btnRemoveControl = new System.Windows.Forms.Button();
        this.btnAddControl = new System.Windows.Forms.Button();
        this.panBackground = new System.Windows.Forms.Panel();
        this.tlp1 = new System.Windows.Forms.TableLayoutPanel();
        this.panToolbar.SuspendLayout();
        this.panBackground.SuspendLayout();
        this.SuspendLayout();
        // 
        // panToolbar
        // 
        this.panToolbar.BackColor = System.Drawing.Color.DarkOliveGreen;
        this.panToolbar.Controls.Add(this.btnRemoveRow);
        this.panToolbar.Controls.Add(this.chkRandom);
        this.panToolbar.Controls.Add(this.btnRemoveControl);
        this.panToolbar.Controls.Add(this.btnAddControl);
        this.panToolbar.Dock = System.Windows.Forms.DockStyle.Bottom;
        this.panToolbar.Location = new System.Drawing.Point(0, 359);
        this.panToolbar.Name = "panToolbar";
        this.panToolbar.Size = new System.Drawing.Size(552, 55);
        this.panToolbar.TabIndex = 2;
        // 
        // btnRemoveRow
        // 
        this.btnRemoveRow.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnRemoveRow.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveRow.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveRow.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnRemoveRow.ForeColor = System.Drawing.Color.White;
        this.btnRemoveRow.Location = new System.Drawing.Point(261, 11);
        this.btnRemoveRow.Name = "btnRemoveRow";
        this.btnRemoveRow.Size = new System.Drawing.Size(119, 34);
        this.btnRemoveRow.TabIndex = 4;
        this.btnRemoveRow.Text = "Remove Row";
        this.btnRemoveRow.UseVisualStyleBackColor = false;
        this.btnRemoveRow.Click += new System.EventHandler(this.btnRemoveRow_Click);
        // 
        // chkRandom
        // 
        this.chkRandom.AutoSize = true;
        this.chkRandom.ForeColor = System.Drawing.Color.White;
        this.chkRandom.Location = new System.Drawing.Point(446, 20);
        this.chkRandom.Name = "chkRandom";
        this.chkRandom.Size = new System.Drawing.Size(94, 19);
        this.chkRandom.TabIndex = 3;
        this.chkRandom.Text = "Random Size";
        this.chkRandom.UseVisualStyleBackColor = true;
        // 
        // btnRemoveControl
        // 
        this.btnRemoveControl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnRemoveControl.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveControl.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnRemoveControl.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnRemoveControl.ForeColor = System.Drawing.Color.White;
        this.btnRemoveControl.Location = new System.Drawing.Point(136, 11);
        this.btnRemoveControl.Name = "btnRemoveControl";
        this.btnRemoveControl.Size = new System.Drawing.Size(119, 34);
        this.btnRemoveControl.TabIndex = 2;
        this.btnRemoveControl.Text = "Remove Control";
        this.btnRemoveControl.UseVisualStyleBackColor = false;
        this.btnRemoveControl.Click += new System.EventHandler(this.btnRemoveControl_Click);
        // 
        // btnAddControl
        // 
        this.btnAddControl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.btnAddControl.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnAddControl.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(64)))), ((int)(((byte)(0)))));
        this.btnAddControl.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
        this.btnAddControl.ForeColor = System.Drawing.Color.White;
        this.btnAddControl.Location = new System.Drawing.Point(11, 11);
        this.btnAddControl.Name = "btnAddControl";
        this.btnAddControl.Size = new System.Drawing.Size(119, 34);
        this.btnAddControl.TabIndex = 0;
        this.btnAddControl.Text = "Add Control";
        this.btnAddControl.UseVisualStyleBackColor = false;
        this.btnAddControl.Click += new System.EventHandler(this.btnAddControl_Click);
        // 
        // panBackground
        // 
        this.panBackground.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right)));
        this.panBackground.AutoScroll = true;
        this.panBackground.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
        this.panBackground.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.panBackground.Controls.Add(this.tlp1);
        this.panBackground.Location = new System.Drawing.Point(0, 0);
        this.panBackground.Name = "panBackground";
        this.panBackground.Size = new System.Drawing.Size(552, 360);
        this.panBackground.TabIndex = 3;
        // 
        // tlp1
        // 
        this.tlp1.AutoSize = true;
        this.tlp1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
        this.tlp1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
        this.tlp1.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Single;
        this.tlp1.ColumnCount = 1;
        this.tlp1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
        this.tlp1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
        this.tlp1.Dock = System.Windows.Forms.DockStyle.Top;
        this.tlp1.Location = new System.Drawing.Point(0, 0);
        this.tlp1.Name = "tlp1";
        this.tlp1.RowCount = 1;
        this.tlp1.RowStyles.Add(new System.Windows.Forms.RowStyle());
        this.tlp1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 1F));
        this.tlp1.Size = new System.Drawing.Size(552, 2);
        this.tlp1.TabIndex = 4;
        // 
        // frmTLPTest1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
        this.ClientSize = new System.Drawing.Size(552, 414);
        this.Controls.Add(this.panBackground);
        this.Controls.Add(this.panToolbar);
        this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.Name = "frmTLPTest1";
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
        this.Text = "frmTLPTest1";
        this.Load += new System.EventHandler(this.SOfrmTest1_Load);
        this.panToolbar.ResumeLayout(false);
        this.panToolbar.PerformLayout();
        this.panBackground.ResumeLayout(false);
        this.panBackground.PerformLayout();
        this.ResumeLayout(false);

    }

    private System.Windows.Forms.Panel panToolbar;
    private System.Windows.Forms.Button btnAddControl;
    private System.Windows.Forms.Button btnRemoveControl;
    private System.Windows.Forms.CheckBox chkRandom;
    private System.Windows.Forms.Panel panBackground;
    private System.Windows.Forms.TableLayoutPanel tlp1;
    private System.Windows.Forms.Button btnRemoveRow;
}
...