简体   繁体   English

如何调整 TableLayoutPanel 以按高度和重量包裹内容? (动态)

[英]How to adjust TableLayoutPanel to wrap content by height and weight? (dynamically)

I created a Form that I am going to use as pop-up.我创建了一个将用作弹出窗口的表单。
I need that this pop-up contains title, ProgressBar and percentage text.我需要这个弹出窗口包含标题、进度条和百分比文本。

So, for this purpose, I choose to use TableLayoutPanel as a control in my Form.因此,为此,我选择在我的 Form 中使用TableLayoutPanel作为控件。

All these UI elements are created in dynamically.所有这些 UI 元素都是动态创建的。 For test now I am using Label instead of real ProgressBar.为了测试,我现在使用 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;
    }
}

So, as you can see I create TableLayoutPanel divided by rows and columns and for each view that it holds I set Dock = DockStyle.Fill , because I don't want to hardcode a fixed size.因此,正如您所看到的,我创建了TableLayoutPanel按行和列划分,并为它持有的每个视图设置Dock = DockStyle.Fill ,因为我不想对固定大小进行硬编码。

What am I expecting is that each UI element fills a Cell in the TableLayoutPanel and the parent Form itself will adjust its size by the TableLayoutPanel that holds all the other Controlss.我期望的是每个 UI 元素都填充 TableLayoutPanel 中的一个单元格,并且父窗体本身将通过包含所有其他 Controlss 的 TableLayoutPanel 调整其大小。

But actually I get this result:但实际上我得到了这个结果:

在此处输入图片说明

The Parent Form doesn't wrap content, it looks like it has fixed size and the TableLayoutPanel tries to fill the Form ClientArea.父窗体不包装内容,它看起来像固定大小,并且 TableLayoutPanel 尝试填充窗体 ClientArea。 I've set AutoSize = true;我已经设置了AutoSize = true; , but it has no visible affect. ,但它没有明显的影响。

What am I doing wrong?我究竟做错了什么?

EDIT编辑

Thanks, @Jimi now it looks like much better谢谢,@Jimi 现在看起来好多了

在此处输入图片说明

I also added我还加了

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

to TLP, but question is - as you can see each row has like min height.到 TLP,但问题是 - 正如您所看到的,每一行都有最小高度。 And it looks like space between each row.它看起来像每行之间的空间。 Why is it happens?为什么会发生?

EDIT2编辑2

I would like to notice that if I change this我想注意,如果我改变这个

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

to this:对此:

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

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

it doesn't work... What am I doing wrong?它不起作用......我做错了什么?

and here is my code now这是我的代码

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
    };
}

Problem is - that even if I set static width as 200 -> MinimumSize = new Size(200, 0) in my Form, when I open it I see that it takes like 1000 .问题是 - 即使我将静态宽度设置为 200 -> MinimumSize = new Size(200, 0)在我的表单中,当我打开它时,我看到它需要1000

在此处输入图片说明

Why is it happens?为什么会发生?

The scenario :场景

  • A bare-bones Form, no specific dimensions applied.一个简单的表格,没有应用特定的尺寸。 No child controls.没有子控件。
  • A TableLayoutPanel is build from scratch at run-time. TableLayoutPanel在运行时从头开始构建。 No specific dimensions无具体尺寸
    • Two Rows and two Columns are created with size mode set to percentage创建两行两列,大小模式设置为百分比
    • Three Control are created from scratch and added to three of the TableLayoutPanel's Cells三个 Control 从头开始​​创建并添加到 TableLayoutPanel 的三个 Cell 中
      The TableLayoutPanel must resize itself to the new content, which doesn't have a specific size (except the default size applied to the Windows Forms Controls), without squeezing out of existence the new child Controls TableLayoutPanel 必须将自身调整为新内容的大小,该内容没有特定大小(除了应用于 Windows 窗体控件的默认大小),而不会挤压新子控件的存在
  • The Form, after the TableLayoutPanel is added to its Controls collection, must auto-size itself to the size of its (only) child Control, keeping a newly set minimum Width, but no minimum or maximum Height (so it can expand vertically, if needed, but not horizontally)在将 TableLayoutPanel 添加到其 Controls 集合后,Form 必须将自身的大小自动调整为其(唯一的)子控件的大小,保持新设置的最小宽度,但没有最小或最大高度(因此它可以垂直扩展,如果需要,但不是水平的)
  • Finally, the auto-sized TableLayoutPanel must be docked inside the auto-sized Form container and all parts need to cooperate to generate the expected Layout最后,自动调整大小的 TableLayoutPanel 必须停靠在自动调整大小的 Form 容器内,所有部分都需要配合才能生成预期的 Layout

How to proceed :如何进行

The sequence of the operations will clearly determine the final result.操作的顺序将明确判断最终结果。

  1. The Form initializes: it's construtor calls the standard InitializeComponent() method. Form 初始化:它的构造函数调用标准的 InitializeComponent() 方法。 There are no child Controls, so there's actually nothing to layout.没有子控件,所以实际上没有什么可布局的。 The Form will simply set the Size defined in the Designer.表单将简单地设置在设计器中定义的大小。
  2. We now instruct the Form to AutoSize to its content and also that it's allowed to both grow and shrink its Size to accomplish this task.我们现在指示 Form 自动调整其内容的大小,并且允许它增大和缩小其大小以完成此任务。 The Form won't actually do anything right now, since it cannot perform its Layout at this time (unless explicitly instructed, using the PerformLayout() method) Form 现在实际上不会做任何事情,因为它此时无法执行其布局(除非明确指示,使用PerformLayout()方法)
  3. We set a minimum Size, constraining its Width, but not its Height (setting MinimumSize = new Size(200, 0) , we define a minimum Width, but the Height dimension is free to grow and also shrink, here - setting 0 as value, means no restrictions ).我们设置了一个最小尺寸,限制了它的宽度,而不是它的高度(设置MinimumSize = new Size(200, 0) ,我们定义了一个最小宽度,但高度尺寸可以自由增长和收缩,这里 - 设置0作为值, 表示没有限制)。
  4. The TableLayoutPanel and its child Controls are now created, using default Sizes (Windows Forms Controls have a default Size when created).现在使用默认大小创建了 TableLayoutPanel 及其子控件(Windows 窗体控件在创建时具有默认大小)。 The Labels will AutoSize to the size of their Text: this is the default behavior.标签将自动调整到其文本的大小:这是默认行为。
  5. After all child Controls are added to the TLP, the TLP is instructed to AutoSize itself, also specifying that it can grow and shrink to perform the Layout.在将所有子控件添加到 TLP 后,TLP 被指示自动调整大小,同时指定它可以增长和收缩以执行布局。 6 The TableLayoutPanel is added to the Form container. 6 TableLayoutPanel 被添加到 Form 容器中。 Now, when a Control is added to the Controls collection, the internal method instructs the ContainerControl to SuspendLayout() , position the new Control and ResumeLayou() (without also performing the Layout of the child Controls, since this has just happened): at this point, the Form, instructed to AutoSize to its content, will calculate its PreferredSize and then auto-size itself to the new content.现在,当 Control 被添加到 Controls 集合时,内部方法指示 ContainerControl 到SuspendLayout() ,定位新的 Control 和ResumeLayou() (不执行子控件的布局,因为这刚刚发生):在这一点上,被指示根据其内容自动调整大小的表单将计算其PreferredSize ,然后根据新内容自动调整大小。
  6. The TableLayoutPanel is now set to Dock to the parent Container. TableLayoutPanel现在设置为停靠到父容器。 The Container is already auto-sized to the size of the TableLayoutPanel , so the TLP just fixes its anchors to the current Size of the Form. Container 已经自动调整到TableLayoutPanel的大小,所以 TLP 只是将其锚点固定到 Form 的当前大小。

The final Layout will be performed before the Form is shown: anyway, at this point, all the Controls involved are already sized and positioned as instructed.最终的 Layout 将在 Form 显示之前执行:无论如何,此时,所有涉及的 Controls 都已经按照指示调整了大小和位置。

Extra :额外的

► The TableLayoutPanel's SetColumn() and SetRow() are called explicitly. ► TableLayoutPanel 的SetColumn()SetRow()被显式调用。 The reason is partailly explained in the Remarks section of the two methods:原因在两种方法的备注部分有部分说明:

This method reapplies the table layout to all controls in the TableLayoutPanel.此方法将表格布局重新应用于TableLayoutPanel 中的所有控件。

In the current context, calling these methods is a plus .在当前上下文中,调用这些方法是加分项 It's not strictly needed, because we just add Controls to the TLP, never remove one.它不是严格需要的,因为我们只是将控件添加到 TLP,从不删除。
But, when Controls are added to and removed from the TLP while the TLP is set to AutoSize to its content (this applies to the current context), the TLP won't actually perform the Layout as expected: the Cells will, in most cases (yes, not always...) maintain their original Size when a Control is removed (removed or disposed, the result is the same).但是,当将控件添加到 TLP 或从 TLP 中删除时,TLP 设置为其内容的 AutoSize(这适用于当前上下文),TLP 实际上不会按预期执行布局:在大多数情况下,单元格会(是的,并不总是......)当控件被移除(移除或处置,结果相同)时保持其原始大小。

Some notes on the matter are found here:可以在此处找到有关此问题的一些说明:
This code (different language but simple to read) can easily tested to reproduce the problem此代码(不同语言但易于阅读)可以轻松测试以重现问题
Dynamic TableLayoutPanel Controls Keep Border Width动态 TableLayoutPanel 控件保持边框宽度

► The ColumnStyles and RowStyles are cleared and new styles are added: ► 清除了ColumnStylesRowStyles并添加了新样式:
- The original code actually adds new styles without removing the existing, default, ones: this can and will generate a layout problem in some situations. - 原始代码实际上添加了新样式而不删除现有的默认样式:这在某些情况下会并且会产生布局问题。 Some notes on the matter here:关于此事的一些说明:

Remove Row inside TableLayoutPanel makes a layout problem删除 TableLayoutPanel 内的 Row 会导致布局问题
Center multiple rows of controls in a FlowLayoutPanel在 FlowLayoutPanel 中居中多行控件


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);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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