More questions about client areas

G

Guest

Similar situation to other posts, but I hope I can get a better answer to
mine. I have a control derived from System.Windows.Forms.ContainerControl.
I do all my own painting. Consequently, the area that I wish to allow users
to place controls is smaller and has a different location than the
ClientRectangle. Setting the ClientSize doesn't work, as this only changes
the size, not the origin. ClientRectangle is read only, and cannot be
overridden. DisplayRectangle can be overridden, but it only affects child
controls that are docked. If they are just anchored, they can still be
placed outside of the DisplayRectangle. Essentially, all I want to do is
what could easily be done by changing the size and origin of the
ClientRectangle. Right now, the only way to do it is to override the
OnLayout event and make sure the control is within the correct area. is
there a simpler way? And if not, why can't ClientRectangle be overridden?
 
J

Jelle van der Beek

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top