Search code examples
c#winformsautosize

Setting form's width according to text in title bar


When (or after) I call my Form.Show() method, I would like to set the form's width (or minimum width) according to the string in the title bar (Form.Text), since this text is changing according to a user's choice. The form width should end up being the perfect amount so that the title bar text doesn't cut off and end with "...".
Currently, I'm simply setting the MinimumSize property to a value that accommodates for all choices, but an automatic sizing would look slightly neater. My problem here is that the title bar text doesn't have any properties to get its actual width. AutoSize also only changes the size according to the controls in the form.

Is there anything I can do?


Solution

  • You can get the width of the system menu, width of the title text and width of control box buttons, then calculate the preferred width and check if the current width is less that the preferred with, then change the widths of the for to preferred width.

    auto-size based on title text width

    To get the information about titlebar elements, you can send a WM_GETTITLEBARINFOEX message to window and get an instance of TITLEBARINFOEX structure which contains information about titlebar.

    You can also use SystemInformation.CaptionButtonSize to get control button sizes and SystemInformation.SmallIconSize to get system menu icon size and add a bit of extra padding space.

    Example

    You can override OnShown method of the form like this:

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);
    
        var info = NativeMethods.GetTitleBarInfo(this.Handle);
        var systemMenuWidth = info.rcTitleBar.Left - this.Left;
        var controlBoxWidth =
            info.rgrect[(int)NativeMethods.TitleBarRectangles.CloseButton].Right -
            info.rgrect[(int)NativeMethods.TitleBarRectangles.MinimizeButton].Left;
        var titleWidth = TextRenderer.MeasureText(this.Text, SystemFonts.CaptionFont).Width;
    
        //var preferred = titleWidth + systemMenuWidth + controlBoxWidth + 16;
        //if (this.Width < preferred)
        //   this.Width = preferred;
    
        var preferred = titleWidth + systemMenuWidth + controlBoxWidth;
        if (this.Width < preferred)
            this.ClientSize = new Size(preferred, this.ClientSize.Height);
    }
    

    And here is NativeMethods:

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    
    public static class NativeMethods
    {
        public const int WM_GETTITLEBARINFOEX = 0x033F;
        public static TITLEBARINFOEX GetTitleBarInfo(IntPtr hwnd)
        {
            var info = new TITLEBARINFOEX()
            { cbSize = (uint)Marshal.SizeOf(typeof(TITLEBARINFOEX)) };
            SendMessage(hwnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref info);
            return info;
        }
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr SendMessage(
            IntPtr hWnd, int Msg, IntPtr wParam, ref TITLEBARINFOEX lParam);
    
        [StructLayout(LayoutKind.Sequential)]
        public struct TITLEBARINFOEX
        {
            public const int CCHILDREN_TITLEBAR = 5;
            public uint cbSize;
            public RECT rcTitleBar;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
            public uint[] rgstate;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
            public RECT[] rgrect;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left, Top, Right, Bottom;
            public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
        }
        public enum TitleBarRectangles
        {
            TitleBar = 0,
            Reserved = 1,
            MinimizeButton = 2,
            MaximizeButton = 3,
            HelpButton = 4,
            CloseButton = 5
        }
    }
    

    So having such form in designer:

    form in design mode

    But at runtime:

    auto-size based on title text width

    Alternative solution - Set tooltip for titlebar

    As an alternative solution you can add a ToolTip to the Form titlebar:

    Tooltip for titlebar