jerha202
jerha202

Reputation: 357

Tab height does not reflect high DPI on custom/userpaint TabControl

I have written a custom TabControl class, but I'm unable to make the tabs adapt their heights on high DPI screens. On a screen with 200% scaling, the tabs are halfway covered by the actual tab page and its controls, like this:

enter image description here

Apparently the TabControl doesn't adapt the tab heights to fit the larger font, and as a result, the top of the actual page is too high up and covers my tabs. What can I do to enforce the tabs to adapt?

The form has AutoScaleMode set to Dpi, and everything else looks fine, except for this. I'm targeting .NET 4.5.2, and the dpiAware setting is set to true in the manifest file.

Here is my code for the custom TabControl:

/// <summary>
/// A TabControl without 3D borders and other annoyances. Taken from
/// https://stackoverflow.com/questions/27469886/change-color-of-unused-space-of-tabcontrol/27472230
/// and modified.
/// </summary>
public class CleanTabControl : TabControl
{
    private class NativeMethods
    {
        [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    }
    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;

    private int hoverTab = -1;

    public event MouseEventHandler CloseClick;

    public CleanTabControl()
    {
        // Take over the painting completely, we want transparency and double-buffering
        SetStyle(ControlStyles.UserPaint | ControlStyles.SupportsTransparentBackColor, true);
        DoubleBuffered = ResizeRedraw = true;
    }

    protected override void OnCreateControl()
    {
        // Necessary to give tabs the correct width
        base.OnCreateControl();
        OnFontChanged(EventArgs.Empty);
    }

    protected override void OnFontChanged(EventArgs e)
    {
        // Necessary to give tabs the correct width
        base.OnFontChanged(e);
        IntPtr hFont = Font.ToHfont();
        NativeMethods.SendMessage(Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        NativeMethods.SendMessage(Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        UpdateStyles();
    }

    public override Color BackColor
    {
        // Override TabControl.BackColor, we need transparency
        get { return Color.Transparent; }
        set { base.BackColor = Color.Transparent; }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        // ... lot of painting code
    }

    protected override void OnMouseClick(MouseEventArgs e)
    {
        base.OnMouseClick(e);
        if (SelectedTab != null)
        {
            if (GetImageRectangle(SelectedIndex).Contains(e.Location))
                CloseClick(this, e);
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        hoverTab = -1;
        for (int i = 0; i < TabCount; i++)
        {
            if (GetTabRect(i).Contains(e.Location))
            {
                if (GetImageRectangle(i).Contains(e.Location))
                    TabPages[i].ImageIndex = 1;
                else
                {
                    hoverTab = i;
                    TabPages[i].ImageIndex = 0;
                }
            }
            else
                TabPages[i].ImageIndex = 0;
        }
        Invalidate();
    }

    protected override void OnMouseLeave(EventArgs e)
    {
        base.OnMouseLeave(e);
        hoverTab = -1;
        for (int i = 0; i < TabCount; i++)
        {
            TabPages[i].ImageIndex = 0;
        }
        Invalidate();
    }

    private Rectangle GetImageRectangle(int index)
    {
        Rectangle r = GetTabRect(index);
        int width = ImageList.ImageSize.Width;
        int height = ImageList.ImageSize.Height;
        int x = r.Right - width - Padding.X;
        int y = (r.Top + r.Height - height) / 2 + 1;
        if (index != SelectedIndex)
            y += 1;
        return new Rectangle(x, y, width, height);
    }
}

}

Upvotes: 3

Views: 666

Answers (1)

jerha202
jerha202

Reputation: 357

I found the solution. In OnCreateControl(), add:

ItemSize = new Size(ItemSize.Width, ItemSize.Height * DpiRatio);

where DpiRatio is the scale factor (e.g. 2 for 200% scaling).

Upvotes: 2

Related Questions