The right way to do this is by overriding the WndProc and responding to the
WM_NCCALCSIZE message.
There are a bunch of NC (non-client area) messages with which you can
control the non-client area of a control. Here you can draw your own border
and control it by responding to mouse messages etc.
Note that a client area always starts at (0, 0), so you cannot move it. You
just need to distinguish between the client and non-client area correctly.
Here's the source code that does the trick... I ripped most of it from a guy
named Lukasz. There are two things to note in this sample: you must draw the
border by yourself and you have to invalidate the non-client area by
yourself
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace PlaylogicControlLibrary
{
public class ControlWithSmallerClientArea : System.Windows.Forms.Control
{
private System.ComponentModel.Container components = null;
private int m_BorderWidth = 5;
#region Win32
[DllImport("User32.dll")]
public extern static IntPtr GetWindowDC( IntPtr hWnd );
[DllImport("User32.dll")]
public extern static int ReleaseDC( IntPtr hWnd, IntPtr hDC );
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr
hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
public enum WinAPI_WM
{
WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084,
WM_NCLBUTTONDOWN = 0x00A1,
WM_NCLBUTTONUP = 0x00A2,
WM_NCMOUSEMOVE = 0x00A0,
WM_NCPAINT = 0x0085,
WM_LBUTTONDOWN = 0x0201,
WM_MOUSEMOVE = 0x0200
}
[StructLayout(LayoutKind.Sequential)]
public struct WinAPI_NCCALCSIZE_PARAMS
{
public WinAPI_RECT rgrc0, rgrc1, rgrc2;
public IntPtr lppos;
}
[StructLayout(LayoutKind.Sequential)]
public struct WinAPI_RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public struct WinAPI_SWP
{
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_NOREDRAW = 0x0008;
public const int SWP_NOACTIVATE = 0x0010;
public const int SWP_FRAMECHANGED = 0x0020; // The frame
changed: send WM_NCCALCSIZE
public const int SWP_DRAWFRAME = SWP_FRAMECHANGED;
public const int SWP_SHOWWINDOW = 0x0040;
public const int SWP_HIDEWINDOW = 0x0080;
public const int SWP_NOCOPYBITS = 0x0100;
public const int SWP_NOOWNERZORDER = 0x0200; // Don't do owner Z
ordering
public const int SWP_NOREPOSITION = SWP_NOOWNERZORDER;
public const int SWP_NOSENDCHANGING = 0x0400; // Don't send
WM_WINDOWPOSCHANGING
}
#endregion
public ControlWithSmallerClientArea()
{
InitializeComponent();
SetStyle( ControlStyles.ResizeRedraw, true );
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
protected override void WndProc(ref Message m)
{
switch( m.Msg )
{
case (int)WinAPI_WM.WM_NCCALCSIZE :
if ( m.WParam == IntPtr.Zero )
{
WinAPI_NCCALCSIZE_PARAMS csp;
csp =
(WinAPI_NCCALCSIZE_PARAMS)Marshal.PtrToStructure( m.LParam,
typeof(WinAPI_NCCALCSIZE_PARAMS));
csp.rgrc0.Top += m_BorderWidth;
csp.rgrc0.Bottom -= m_BorderWidth;
csp.rgrc0.Left += m_BorderWidth;
csp.rgrc0.Right -= m_BorderWidth;
Marshal.StructureToPtr( csp, m.LParam, false );
}
else if (m.WParam == new IntPtr(1))
{
WinAPI_NCCALCSIZE_PARAMS csp;
csp =
(WinAPI_NCCALCSIZE_PARAMS)Marshal.PtrToStructure( m.LParam,
typeof(WinAPI_NCCALCSIZE_PARAMS));
csp.rgrc0.Top += m_BorderWidth;
csp.rgrc0.Bottom -= m_BorderWidth;
csp.rgrc0.Left += m_BorderWidth;
csp.rgrc0.Right -= m_BorderWidth;
Marshal.StructureToPtr( csp, m.LParam, false );
}
break;
case (int)WinAPI_WM.WM_NCPAINT:
IntPtr hDC = GetWindowDC( m.HWnd );
Graphics g = Graphics.FromHdc( hDC );
PaintNonClientArea( g );
ReleaseDC( m.HWnd, hDC );
break;
}
base.WndProc (ref m);
}
private void PaintNonClientArea( Graphics graphics )
{
Rectangle rect = new Rectangle(0, 0, Width, Height);
using (Pen p = new Pen( Color.Blue, m_BorderWidth ))
{
p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
graphics.DrawRectangle(p, rect);
}
}
private void invalidateNC()
{
SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0,
WinAPI_SWP.SWP_NOMOVE |
WinAPI_SWP.SWP_NOSIZE |
WinAPI_SWP.SWP_NOZORDER |
WinAPI_SWP.SWP_NOACTIVATE |
WinAPI_SWP.SWP_DRAWFRAME
);
}
protected override void OnResize(EventArgs e)
{
invalidateNC();
base.OnResize (e);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.FillRectangle( Brushes.YellowGreen,
this.ClientRectangle.Left, this.ClientRectangle.Top,
this.ClientRectangle.Width, this.ClientRectangle.Height );
base.OnPaint(pe);
}
}
}
Hope this helps...
Jelle van de Beek