1

I have enabled these recommended settings in my config file:

<add key="DpiAware" value="true" />
<add key="DpiAwareness" value="PerMonitorV2" />
<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />

and also in the main function:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Everything is top and left anchored, this is how it looks on a 100% scaled display:

enter image description here

When I move the window to a 150 or 175% scaled display:

enter image description here

As you can see, labels are offset downwards, a few pixels.

The container's AutoScaleMode is set to DPI.

How can I preserve the alignment?

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
Daniel
  • 2,318
  • 2
  • 22
  • 53
  • Looks like the labels are aligned to the bottom of the comboboxes. Try to align them center-by center maybe if this is possible. – horotab Jan 17 '23 at 13:11
  • "As you can see, labels are offset downwards, a few pixels." - they were downset a few pixels before. Please look closely and specify what you want to have aligned. – Thomas Weller Jan 17 '23 at 13:11
  • Use a TableLayoutPanel to adjust the layout of your Controls. Different controls scale differently (unfortunately) – Jimi Jan 17 '23 at 13:13

1 Answers1

1

I came to the same problem and after some digging I came with this control, which can be used instead of the label. Known issue: when clicking, the focused control looses the focus. This can be used both in TableLayoutPanel or standalone.

public class LabelTextBox : System.Windows.Forms.TextBox
{
    private const int WM_NCPAINT = 0x85;
    private const int WM_SETFOCUS = 0x07;
    private const int WM_ENABLE = 0x0A;
    private const int WM_SETCURSOR = 0x20;

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        // https://www.pinvoke.net/default.aspx/Structures/RECT.html
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        // https://www.pinvoke.net/default.aspx/Structures/POINT.html
    }

    public LabelTextBox()
    {
        // Disable some TextBox interactions, so it is intact as the Label
        this.ReadOnly = true;
        this.TabStop = false;
    }

    [DllImport("user32")]
    private static extern IntPtr GetWindowDC(IntPtr hwnd);
    
    [DllImport("user32")]
    private static extern bool GetClientRect(IntPtr hwnd, out RECT rectangle);
    
    [DllImport("user32")]
    private static extern bool GetWindowRect(IntPtr hwnd, out RECT rectangle);

    [DllImport("user32.dll")]
    static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);

    protected override void WndProc(ref Message m)
    {
        // Disable some TextBox interactions, so it is intact as the Label
        if (m.Msg == WM_SETFOCUS || m.Msg == WM_ENABLE || m.Msg == WM_SETCURSOR)
            return;

        // When the boreder should be painted
        if (m.Msg == WM_NCPAINT)
        {
            // Get the rect of the client area in the screen coordinates.
            var clientOrigin = new POINT(0, 0);
            ClientToScreen(Handle, ref clientOrigin);
            GetClientRect(Handle, out var clientRect);
            clientRect.X = clientOrigin.X;
            clientRect.Y = clientOrigin.Y;

            // Get the rect of the window area in the screen coordinates.
            GetWindowRect(Handle, out var windowRect);

            // Calculate the rect of the client area in the window coordinates.
            // This will be our clipping area, because we want to paint only the border.
            var clip = new Rectangle(
                clientRect.Left - windowRect.Left,
                clientRect.Top - windowRect.Top,
                clientRect.Width,
                clientRect.Height
                );

            // Get the Graphics, set the clip and fill with the BackColor.
            var dc = GetWindowDC(Handle);
            using (Graphics g = Graphics.FromHdc(dc))
            {
                g.SetClip(clip, CombineMode.Exclude);
                g.FillRectangle(new SolidBrush(BackColor), 0, 0, windowRect.Width, windowRect.Height);
            }
            
            return;
        }

        base.WndProc(ref m);
    }
}

The result:

100 %

100%

200 %

200%

I came to this through those bad solutions in TableLayoutPanel:

  1. Vertically centered – Quite stable alignment, but unable to align to the baseline.
  2. Finetune by a padding – Still not stable enough on higher DPIs.
  3. Use TextBox without border instead of the Label – The TextBox without border is aligned the same way as the Label.

The solution is inspired by:

  1. https://stackoverflow.com/a/27027829/2408668
  2. https://stackoverflow.com/a/74106039/2408668
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
sebetovsky
  • 101
  • 7