Search code examples
c#.netwinformstabcontrolautosize

Dynamically resize TabControl and Form width to the number of TabPages


I have a windows form with a TabControl and a ListView.
When I run the application, I want the Width of the TabControl to increase/decrease to show all the TabPages without horizontal scrollbar and have the Form resize it's Width accordingly, to insure that the TabControl and ListView are visible.

A screenshot is below.

enter image description here


Solution

  • To auto-size a TabControl to the size of its Headers, you need to calculate the width of the text of each Header. It's simpler in case the TabControl.SizeMode is set to Fixed, since you can set the ItemSize.Width and all Headers will have the same width.

    If the TabControl.SizeMode is set to the default Normal, you have to measure the Text of each Header, adding 1px for the Border (2px if it's the second TabPage - small bug in the base Control).

    In the first case, the size of the TabControl is:

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

    in the second case, measure the text of each Header using TextRendrer.MeasureText:

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

    Setup the Layout:

    • Add a TableLayoutPanel to your Form, with one Row and two Columns (i.e., remove one Row)
    • Add the TabControl to the Cell on the left and the ListBox to the other Cell.
    • Set both Cells's style to AutoSize (after you have added your Controls).
    • Set the TableLayoutPanel to: AutoSize = true, AutoSizeMode = GrowAndShrink
    • Set the Form to auto-size in the same way
    • Set the Form's 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 as reference.
    • Calculate the new Width of the TabControl when the Form is created or loaded (i.e., in its Constructor or OnLoad() or Form.Load), so the Form will auto-size to the size of the TableLayoutPanel, whici in turn auto-sizes to the size of its child Controls.

    Now you can add or remove TabPages at run-time and the Form will auto-size to the width you calculate in the TabControl.ControlAdded and TabControl.ControlRemoved event handlers (also checking whether the Control added is of Type TabPage).

    Example:

    • The MeasureTabPagesWidth() method is the one shown above.
    • The TableLayoutPanel is named tlp1
    • The TabControl is named tabControl1
    • The Buttons used in the visual example have names that define their role.
    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);
        }
    }
    

    This is how it works:
    Tested in Windows 7, since this appears to be the System in use

    Form TabControl AutoSize

    Sample Project:
    Sample Project on Google Drive (.Net Framework 4.8 - C# 7.3)
    Rebuild the Solution before running