I want to make the same thing "Balazs Tihanyi" do here: https://stackoverflow.com/a/9772020/8458887 But with a TableLayoutPanel. I tested his code an worked. But when i change the TextBox for a TableLayoutPanel don't work.
Picture: First is "Balazs Tihanyi" TextBox and second my TableLayoutPanel
Picture 2: This is how the TableLayoutPanel looks when i add a TextBox
The changed code:
public class BorderedPanel : UserControl
{
TableLayoutPanel tableLayoutPanel;
public BorderedPanel()
{
tableLayoutPanel = new TableLayoutPanel()
{
BackColor = SystemColors.Window,
//AutoSize = true,
//CellBorderStyle = TableLayoutPanelCellBorderStyle.None, //Single
BorderStyle = BorderStyle.None, //FixedSingle
ColumnCount = 1,
RowCount = 1,
Location = new Point(-1, -1),
//Dock = DockStyle.Fill,
Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right
};
Control container = new ContainerControl()
{
Dock = DockStyle.Fill,
Padding = new Padding(-1)
};
container.Controls.Add(tableLayoutPanel);
//Controls.Add(tableLayoutPanel);
DefaultBorderColor = SystemColors.ControlDark;
FocusedBorderColor = Color.Red;
BackColor = DefaultBorderColor;
Padding = new Padding(1);
Size = tableLayoutPanel.Size;
}
public Color DefaultBorderColor { get; set; }
public Color FocusedBorderColor { get; set; }
//public override string Text
//{
// get { return textBox.Text; }
// set { textBox.Text = value; }
//}
protected override void OnEnter(EventArgs e)
{
BackColor = FocusedBorderColor;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
BackColor = DefaultBorderColor;
base.OnLeave(e);
}
//protected override void SetBoundsCore(int x, int y,
// int width, int height, BoundsSpecified specified)
//{
// base.SetBoundsCore(x, y, width, height, specified);
//}
}
How i use the code:
public Form1()
{
InitializeComponent();
BorderedPanel borderedPanel = new BorderedPanel();
borderedPanel.Location = new Point(73, 150); //73, 150 //12, 10
borderedPanel.Size = new Size(319, 25);
Controls.Add(borderedPanel);
//borderedPanel.Controls.Add(txtPath);
As an option to add a custom border the non-client area of a panel, you can handle the following native messages:
WM_NCCALCSIZE
: To change the size of the nonclient areaWM_NCPAINT
: To paint on the nonclient area.In the following screenshot you see a panel with BorderColor
and BorderWidth
custom properties, where the implementation works in right to left mode and also in auto-scroll mode:
You can clone or download the code:
I've explained more details about handling the messages in the post Panel border size and border color – Customize nonclient area, but here you can see the code as well:
PanelEx
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static PanelBorderExample.Win32Helpers;
namespace PanelBorderExample
{
public class PanelEx : Panel
{
public PanelEx()
{
BorderStyle = BorderStyle.FixedSingle;
}
private Color borderColor = Color.Blue;
[DefaultValue(typeof(Color), "Blue")]
public Color BorderColor
{
get { return borderColor; }
set
{
if (borderColor != value)
{
borderColor = value;
Redraw();
}
}
}
private int borderWidth = 16;
[DefaultValue(16)]
public int BorderWidth
{
get { return borderWidth; }
set
{
if (value == 0)
throw new ArgumentException("The value should be greater than 0");
if (borderWidth != value)
{
borderWidth = value;
RecalculateClientSize();
}
}
}
protected override void WndProc(ref Message m)
{
if (BorderStyle != BorderStyle.FixedSingle)
{
base.WndProc(ref m);
return;
}
if (m.Msg == WM_NCPAINT)
{
base.WndProc(ref m);
WmNCPaint(ref m);
}
else if (m.Msg == WM_NCCALCSIZE)
{
base.WndProc(ref m);
WmNCCalcSize(ref m);
}
else if (m.Msg == WM_NCHITTEST)
{
base.WndProc(ref m);
WmNCHitTest(ref m);
}
else
base.WndProc(ref m);
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
Redraw();
}
private void Redraw()
{
RedrawWindow(Handle, IntPtr.Zero, IntPtr.Zero,
RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW);
}
private void RecalculateClientSize()
{
SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOZOORDER);
}
private void WmNCCalcSize(ref Message m)
{
if (BorderStyle != BorderStyle.FixedSingle)
return;
if (m.WParam != IntPtr.Zero)
{
var nccsp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
nccsp.rgrc[0].top += borderWidth - 1;
nccsp.rgrc[0].bottom -= borderWidth - 1;
nccsp.rgrc[0].left += borderWidth - 1;
nccsp.rgrc[0].right -= borderWidth - 1;
Marshal.StructureToPtr(nccsp, m.LParam, true);
InvalidateRect(this.Handle, nccsp.rgrc[0], true);
m.Result = IntPtr.Zero;
}
else
{
var clnRect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
clnRect.top += borderWidth - 1;
clnRect.bottom -= borderWidth - 1;
clnRect.left += borderWidth - 1;
clnRect.right -= borderWidth - 1;
Marshal.StructureToPtr(clnRect, m.LParam, true);
m.Result = IntPtr.Zero;
}
}
private void WmNCPaint(ref Message m)
{
var dc = GetWindowDC(Handle);
using (var g = Graphics.FromHdc(dc))
{
using (var p = new Pen(BorderColor, borderWidth) { Alignment = PenAlignment.Inset })
{
if (VScroll && HScroll)
{
Rectangle bottomCornerRectangle = new Rectangle(
Width - SystemInformation.VerticalScrollBarWidth - borderWidth,
Height - SystemInformation.HorizontalScrollBarHeight - borderWidth,
SystemInformation.VerticalScrollBarWidth,
SystemInformation.HorizontalScrollBarHeight);
if (RightToLeft == RightToLeft.Yes)
{
bottomCornerRectangle.X = Width - bottomCornerRectangle.Right;
}
g.FillRectangle(SystemBrushes.Control, bottomCornerRectangle);
}
var adjustment = borderWidth == 1 ? 1 : 0;
g.DrawRectangle(p, 0, 0, Width - adjustment, Height - adjustment);
}
}
ReleaseDC(Handle, dc);
m.Result = IntPtr.Zero;
}
private void WmNCHitTest(ref Message m)
{
var pt = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
var rect = Parent.RectangleToScreen(Bounds);
if (((pt.X >= rect.Left && pt.X <= rect.Left + borderWidth) ||
(pt.X >= rect.Right - borderWidth && pt.X <= rect.Right)) ||
((pt.Y >= rect.Top && pt.Y <= rect.Top + borderWidth) ||
(pt.Y >= rect.Bottom - borderWidth && pt.Y <= rect.Bottom)))
m.Result = (IntPtr)HTBORDER;
}
}
}
Win32Helpers
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PanelBorderExample
{
public static class Win32Helpers
{
public const int WM_NCPAINT = 0x85;
public const int RDW_INVALIDATE = 0x0001,
RDW_ERASE = 0x0004,
RDW_ALLCHILDREN = 0x0080,
RDW_ERASENOW = 0x0200,
RDW_UPDATENOW = 0x0100,
RDW_FRAME = 0x0400;
public const int WM_NCCALCSIZE = 0x0083;
public const int SWP_FRAMECHANGED = 0x0020,
SWP_NOMOVE = 0x0002,
SWP_NOSIZE = 0x0001,
SWP_NOZOORDER = 0x0004;
public const int WM_NCHITTEST = 0x0084;
public const int HTBORDER = 18;
public const int HTHSCROLL = 6;
public const int HTVSCROLL = 7;
public const int HTCLIENT = 1;
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct NCCALCSIZE_PARAMS
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public RECT[] rgrc;
public WINDOWPOS lppos;
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
public const int NM_FIRST = 0;
public const int NM_CLICK = NM_FIRST - 2;
public const int WM_REFLECT = 0x2000;
public const int WM_NOFITY = 0x004e;
public const int WM_CTLCOLORSCROLLBAR = 0x0137;
[StructLayout(LayoutKind.Sequential)]
public struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
}
[DllImport("user32.dll")]
public static extern bool InvalidateRect(IntPtr hWnd, RECT lpRect, bool bErase);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32")]
public static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll")]
public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprc, IntPtr hrgn, int flags);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
}
}