根据 TabPages 的数量动态调整 TabControl 和 Form 的宽度

Dynamically resize TabControl and Form width to the number of TabPages

我有一个带有 TabControl 和 ListView 的 windows 表单。
当我 运行 应用程序时,我希望 TabControl 的 Width 到 increase/decrease 显示所有没有水平滚动条的 TabPages 并相应地调整表单的大小 Width ,以确保TabControl 和 ListView 可见。

下面是截图。

从该表格开始,我将在 运行 时添加 8 个标签,计算标签中文本的宽度 + 填充大小 x2(标签的两侧),然后调整大小根据需要进行控制。

   public Form1()
    {
        InitializeComponent();
        //Clear our default tabs.
        tabControl1.TabPages.Clear();
        //Add more tabs than would be visible by default
        for (int i=1;i<=8;i++)
        {
            tabControl1.TabPages.Add("Tab " + i.ToString());
        }
        ResizeTabControl();
        ResizeListViewControl();
        ResizeForm();
    }
    void ResizeTabControl()
    {
        int tabCount = tabControl1.TabCount;

        float length = 0;
        using (Graphics g = CreateGraphics())
        {
            //Iterate through the tabs and get the length of the text.
            for (int i = 0; i <= tabCount - 1; i++)
                length += g.MeasureString(tabControl1.TabPages[i].Text, tabControl1.Font).Width;
        }
        //Resize the tab control where X is the length of all text in the tabs plus padding x 2 x total tabs.
        tabControl1.Size = new Size(Convert.ToInt32(length) + (tabCount * 2 * tabControl1.Padding.X), tabControl1.Width);          
    }
    void ResizeListViewControl()
    {
        //Move listview 10 pixels away from tabcontrol's edge
        listView1.Location = new Point(tabControl1.Location.X + tabControl1.Width + 10, listView1.Location.Y);
    }
    void ResizeForm()
    {
        //Resize form to accomodate changes.
        this.Width = listView1.Location.X + listView1.Width + 20;
    }

说完一切后,这就是它的样子:

还有 20 个标签,因为为什么不呢。

要auto-size一个TabControl到它的Headers的大小,你需要计算每个Header的文本宽度。如果 TabControl.SizeMode is set to Fixed, since you can set the ItemSize.Width 和所有 Header 具有相同的宽度,则更简单。

如果TabControl.SizeMode设置为默认Normal,则必须测量每个Header的Text,为边框添加1px2px 如果它是第二个 TabPage - 基本控件中的小 bug)。

第一种情况,TabControl的大小为:

tabControl1.Width = tabControl1.TabPages.Count * (tabControl1.ItemSize.Width + 1);

在第二种情况下,使用TextRendrer.MeasureText测量每个Header的文本:

private int MeasureTabPagesWidth(TabControl tc)
{
    if (tc.TabPages.Count == 0) return tc.Width;
    int newWidth = 0;
    int border = tc.TabPages.Count == 2 ? 2 : 1;
    var flags = TextFormatFlags.LeftAndRightPadding;

    using (var g = tc.CreateGraphics()) {
        foreach (TabPage tab in tc.TabPages) {
            newWidth += TextRenderer.MeasureText(g, tab.Text, tc.Font, 
                new Size(int.MaxValue, tc.Font.Height + 4), flags).Width + border;
        }
    }
    return newWidth;
}

设置布局:

  • 向您的表单添加一个 TableLayoutPanel,具有一行和两列(即,删除一行)
  • 将 TabControl 添加到左侧的 Cell,将 ListBox 添加到另一个 Cell。
  • 将两个单元格的样式设置为 AutoSize 添加控件后)。
  • 将 TableLayoutPanel 设置为:AutoSize = trueAutoSizeMode = GrowAndShrink
  • 同样的方法将Form设置为auto-size
  • 将表格的 MinimumSize and MaximumSize. The former is usually set to the design size, the latter is up to you; you could use the current Screen WorkingArea 设置为参考。
  • 在创建或加载窗体时(即在其构造函数或OnLoad()Form.Load中)计算TabControl的新宽度,因此窗体将auto-size到TableLayoutPanel 的大小,依次 auto-size 到其 child 控件的大小。

现在您可以在 run-time 添加或删除 TabPages 并且表单将 auto-size 到您在 TabControl.ControlAdded and TabControl.ControlRemoved 事件处理程序中计算的宽度(同时检查添加的控件是否是输入 TabPage).

例子:

  • MeasureTabPagesWidth()方法就是上面的那个
  • TableLayoutPanel 名为 tlp1
  • TabControl 名为 tabControl1
  • 视觉示例中使用的按钮具有定义其作用的名称。
public partial class AutoSizeForm : Form
{
    public AutoSizeForm()
    {
        InitializeComponent();
        tabControl1.Width = MeasureTabPagesWidth(tabControl1);
    }

    private void tabControl1_ControlAdded(object sender, ControlEventArgs e)
    {
        // Event notified after the TabPage has been added
        if (e.Control is TabPage) {
            tabControl1.Width = MeasureTabPagesWidth(tabControl1);
        }
    }

    private void tabControl1_ControlRemoved(object sender, ControlEventArgs e)
    {
        if (e.Control is TabPage) {
            // Use deferred execution, since the TabPage is removed after 
            // the event handler method completes.
            BeginInvoke(new Action(()=> tabControl1.Width = MeasureTabPagesWidth(tabControl1)));
        }
    }

    private void btnAddPage_Click(object sender, EventArgs e) 
    {
        tabControl1.TabPages.Add(new TabPage("New TabpPage Text"));
    }

    private void btnRemovePage_Click(object sender, EventArgs e)
    {
        if (tabControl1.TabPages.Count > 0) {
            tabControl1.TabPages.RemoveAt(tabControl1.TabPages.Count - 1);
        }
    }

    private void btnAddCtlToTLP_Click(object sender, EventArgs e)
    {
        tlp1.ColumnCount += 1;
        tlp1.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
        var mc = new MonthCalendar();
        tlp1.SetColumn(mc, tlp1.ColumnCount - 1);
        tlp1.Controls.Add(mc);
    }
}

它是这样工作的:
在 Windows 7 中测试,因为这似乎是正在使用的系统

示例项目:
Sample Project on Google Drive (.Net Framework 4.8 - C# 7.3)
在 运行

之前重建解决方案