winforms 如何调整TableLayoutPanel以按高度和重量换行内容?(动态)

sf6xfgos  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(405)

我创建了一个窗体,我将使用它作为弹出窗口。
我需要这个弹出窗口包含标题,ProgressBar和百分比文本。
因此,出于此目的,我选择使用TableLayoutPanel作为Form中的控件。
所有这些UI元素都是动态创建的。为了测试,我现在使用Label而不是真实的的ProgressBar。

public partial class TestFormDeleteIt : Form
{
    public TestFormDeleteIt()
    {
        InitializeComponent();

        AutoSize = true;
        var flp = CreateTableLayoutPanel();
        Controls.Add(flp);
    }

    private Control CreateTableLayoutPanel()
    {
        // TableLayoutPanel Initialization
        var panel = new TableLayoutPanel
        {
            ColumnCount = 2,
            RowCount = 2,
            Dock = DockStyle.Fill
        };

        //Adjust column size in percentage
        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70F));
        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F));

        //Adjust row size in percentage
        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));

        //Fill content
        panel.Controls.Add(
            new Label()
            {
                Text = "state",
                Dock = DockStyle.Fill
            }, 
            /*column*/ 0, /*row*/ 0);

        panel.Controls.Add(
            new Label()
            {
                Text = "ProgressBar",
                Dock = DockStyle.Fill
            },
            /*column*/ 0, /*row*/ 1);

        panel.Controls.Add(
            new Label()
            {
                Text = "100%",
                Dock = DockStyle.Fill
            }, 
            /*column*/ 1, /*row*/ 1);
        return panel;
    }
}

因此,正如您所看到的,我创建了TableLayoutPanel,并按行和列划分,并为它包含的每个视图设置Dock = DockStyle.Fill,因为我不想硬编码固定的大小。
我期望的是每个UI元素填充TableLayoutPanel中的Cell,父窗体本身将通过包含所有其他Controlss的TableLayoutPanel调整其大小。
但实际上我得到这样的结果:

父窗体不换行内容,看起来它具有固定大小,并且TableLayoutPanel尝试填充窗体ClientArea。我已经设置了AutoSize = true;,但它没有明显的影响。
我做错了什么?

编辑

谢谢,@吉米现在看起来好多了

我还补充说

MinimumSize = new System.Drawing.Size(600, Height)

到TLP,但问题是-正如你所看到的,每行都有最小高度。看起来像是每一行之间的空间。为什么会这样?

EDIT2

我想指出如果我改变这个

panel.Controls.Add(
    new Label()
    {
        Text = "state",
        Dock = DockStyle.Fill
    },
    /*column*/ 0, /*row*/ 0);

对此:

var label1 = new Label()
{
    Text = "state",
    Dock = DockStyle.Fill
};

panel.SetRow(label1, 0);
panel.SetColumn(label1, 0);

它不工作...我做错了什么?
这是我的代码

public partial class TestFormDeleteIt : Form
{
    public TestFormDeleteIt()
    {
        InitializeComponent();

        AutoSize = true;
        AutoSizeMode = AutoSizeMode.GrowAndShrink;
        MinimumSize = new System.Drawing.Size(200, 0);

        var flp = CreateTableLayoutPanel();
        Controls.Add(flp);
    }

    private TableLayoutPanel CreateTableLayoutPanel()
    {
        // TableLayoutPanel Initialization
        var panel = new TableLayoutPanel
        {
            ColumnCount = 2,
            RowCount = 2,
            Dock = DockStyle.Fill
        };

        //Adjust column size in percentage
        panel.ColumnStyles.Clear();
        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 90F));
        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10F));

        //Adjust row size in percentage
        panel.RowStyles.Clear();
        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));

        //Fill content
        panel.Controls.Add(
            new Label()
            {
                Text = "state",
                Dock = DockStyle.Fill
            },
            /*column*/ 0, /*row*/ 0);

        panel.Controls.Add(
            CreateProgressBar(),
            /*column*/ 0, /*row*/ 1);

        panel.Controls.Add(
            new Label()
            {
                Text = "100%",
                Dock = DockStyle.Fill
            }, 
            /*column*/ 1, /*row*/ 1);

        panel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
        panel.AutoSize = true;

        return panel;
    }

    private Control CreateProgressBar() => new ProgressBar()
    {
        Visible = true,
        Dock = DockStyle.Top
    };
}

问题是-即使我在Form中将静态宽度设置为200 -> MinimumSize = new Size(200, 0),当我打开它时,我看到它需要1000

为什么会这样?

gcuhipw9

gcuhipw91#

    • 场景**:
  • 一个简单的表单,没有应用特定的尺寸。无子控件。
  • TableLayoutPanel是在运行时从头开始构建的。无具体尺寸
  • 将创建两行和两列,并将大小模式设置为百分比
  • 从头开始创建三个控件,并将其添加到TableLayoutPanel的三个单元格中

TableLayoutPanel必须根据新内容调整自身大小,新内容没有特定大小(应用于Windows窗体控件的默认大小除外),而不会 * 挤压 * 新子控件的存在

  • 将TableLayoutPanel添加到其Controls集合后,窗体必须将自身大小自动调整为(唯一的)子控件的大小,保持新设置的最小Width,但不保持最小或最大Height(因此,如果需要,它可以垂直扩展,但不能水平扩展)
  • 最后,自动调整大小的TableLayoutPanel必须停靠在自动调整大小的Form容器内,所有部分都需要协作以生成预期的布局
    • 如何操作**:
  • 操作 * 的顺序将清楚地决定最终结果。

1.表单初始化:它的构造函数调用标准的InitializeComponent()方法。没有子控件,所以实际上没有什么可布局的。表单将简单地设置设计器中定义的大小。
1.我们现在指示窗体根据其内容自动调整大小,并且允许它增大和缩小其大小以完成此任务。Form现在实际上不会做任何事情,因为它此时无法执行其Layout(除非使用PerformLayout()方法显式指示)
1.我们设置一个最小Size,约束它的Width,但不约束它的Height(设置**MinimumSize = new Size(200, 0)**,我们定义了一个最小Width,但Height维度可以自由增长和收缩,这里-将0设置为value,意味着 * 没有限制 *)。

  1. TableLayoutPanel及其子控件现在已使用默认大小创建(Windows窗体控件在创建时具有默认大小)。标签将自动调整到其文本的大小:这是默认行为。
    1.将所有子控件添加到TLP后,TLP将被指示自动调整大小,同时指定它可以放大和缩小以执行布局。
  2. TableLayoutPanel即添加到窗体容器中。现在,当一个Control被添加到Controls集合中时,内部方法指示ContainerControl**SuspendLayout(),定位新的Control和ResumeLayou()**(不执行子控件的布局,因为这刚刚发生):此时,被指示根据其内容自动调整大小的Form将计算其PreferredSize,然后根据新内容自动调整自身大小。
  3. TableLayoutPanel现在设置为停靠到父容器。Container已经自动调整为TableLayoutPanel的大小,因此TLP只是将其锚点固定为Form的当前Size。
    最终布局将在表格显示之前执行:无论如何,在这一点上,所有涉及的控件都已经按照指示调整了大小和位置。
    • 额外**:

▶显式调用TableLayoutPanel的SetColumn()SetRow()。原因在两种方法的备注部分有部分解释:

  • 此方法将表格布局重新应用于TableLayoutPanel中的所有控件。*

在当前上下文中,调用这些方法是一个 * 加号 *。这不是严格需要的,因为我们只是将控件添加到TLP,而不是删除一个。
但是,当在TLP中添加和删除控件时,同时将TLP设置为其内容自动调整大小(这适用于当前上下文),TLP实际上不会按预期执行布局:在大多数情况下(是的,并不总是......),当删除控件时(删除或处置,结果相同),单元格将保持其原始大小。
关于这个问题的一些注解可以在这里找到:
这段代码(不同的语言,但简单易读)可以很容易地测试重现 * 问题 *
Dynamic TableLayoutPanel Controls Keep Border Width
▶清除ColumnStylesRowStyles并添加新样式:

public partial class TestFormDeleteIt : Form
{
    protected internal ProgressBar pBar = null;
    protected internal Label lbl2 = null;

    public TestFormDeleteIt()
    {
        InitializeComponent();

        this.AutoSize = true;
        this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
        this.MinimumSize = new Size(200, 0);

        var tlp = CreateTableLayoutPanel();

        tlp.AutoSizeMode = AutoSizeMode.GrowAndShrink;
        tlp.AutoSize = true;

        this.Controls.Add(tlp);
        tlp.Dock = DockStyle.Fill;
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Task.Run(() => UpdateProgress());
    }

    private TableLayoutPanel CreateTableLayoutPanel()
    {
        var panel = new TableLayoutPanel { ColumnCount = 2, RowCount = 2,
            CellBorderStyle = TableLayoutPanelCellBorderStyle.Single
        };
        panel.ColumnStyles.Clear();
        panel.RowStyles.Clear();

        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70F));
        panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F));

        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
        panel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));

        var lbl1 = new Label() { Text = "state", Dock = DockStyle.Fill };
        panel.Controls.Add(lbl1);
        panel.SetColumn(lbl1, 0);
        panel.SetRow(lbl1, 0);

        pBar = new ProgressBar() { Dock = DockStyle.Top };
        panel.Controls.Add(pBar);
        panel.SetColumn(pBar, 0);
        panel.SetRow(pBar, 1);

        lbl2 = new Label() { Text = "0%", Dock = DockStyle.Fill };
        panel.Controls.Add(lbl2);
        panel.SetColumn(lbl2, 1);
        panel.SetRow(lbl2, 1);

        return panel;
    }

    private async Task UpdateProgress()
    {
        for (int i = 1; i <= 100; i++) {
            BeginInvoke(new Action(() => {
                pBar.Value = i;
                lbl2.Text = (i / 100.0).ToString("P");
            }));
            await Task.Delay(50);
        }
    }
}

相关问题