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 %

200 %

I came to this through those bad solutions in TableLayoutPanel:
- Vertically centered – Quite stable alignment, but unable to align to the baseline.
- Finetune by a padding – Still not stable enough on higher DPIs.
- 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:
- https://stackoverflow.com/a/27027829/2408668
- https://stackoverflow.com/a/74106039/2408668