如何設置Winform控件的ClientRectangle
最近學習制作WinForm控件,自己動手寫控件的時候才發現System.Windows.Forms.Control 竟然沒有提供默認的border繪制。記得以前用API做控件的時候,只需要設置空間窗口的WS_BORDER 風格就可以。遍尋無方,只有自己繪制了,這里有出現一個,如果border在客戶區,那么在OnPaint方法里不得不每次都要考慮Border所占用的區域,而且,如果從這個類派生的話,將無法獲得準確的客戶區。
現在要解決的問題就是如何重新設置客戶區的矩形區域的尺寸,查看了一下Control類的ClientRectangle屬性:
public Rectangle ClientRectangle { get; }是個只讀屬性,看來是不能通過這個屬性達到目的了。再查找Control類的文檔,也沒有這方面的說明,沒有辦法,只能用API搞定了。可以通過計算非客戶區尺寸來設置客戶區尺寸,Border在非客戶繪制。下面就是主要的代碼,就是通過重載WndProc方法,捕捉WM_NCCALCSIZE消息,實現自己的邏輯。
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)WinAPI_WM.WM_NCCALCSIZE:
if (m.WParam.ToInt32() == 0)
{
WinAPI_RECT rc = (WinAPI_RECT)m.GetLParam(typeof(WinAPI_RECT));
rc.Left += 1;
rc.Top += 1;
rc.Right -= 1;
rc.Bottom -= 1;
Marshal.StructureToPtr(rc, m.LParam, true);
m.Result = IntPtr.Zero;
}
else
{
WinAPI_NCCALCSIZE_PARAMS csp;
csp = (WinAPI_NCCALCSIZE_PARAMS)m.GetLParam(typeof(WinAPI_NCCALCSIZE_PARAMS));
csp.rgrc0.Top += 1;
csp.rgrc0.Bottom -= 1;
csp.rgrc0.Left += 1;
csp.rgrc0.Right -= 1;
![]()
Marshal.StructureToPtr(csp, m.LParam, true);
//Return zero to preserve client rectangle
m.Result = IntPtr.Zero;
}
break;
case (int)WinAPI_WM.WM_NCPAINT:
{
m.WParam = NCPaint(m.WParam);
break;
}
}
![]()
base.WndProc(ref m);
}
![]()
public IntPtr NCPaint(IntPtr region)
{
IntPtr hDC = GetWindowDC(this.Handle);
if (hDC != IntPtr.Zero)
{
Graphics grTemp = Graphics.FromHdc(hDC);
![]()
int ScrollBarWidth = SystemInformation.VerticalScrollBarWidth;
int ScrollBarHeight = SystemInformation.HorizontalScrollBarHeight;
![]()
WINDOWINFO wi = new WINDOWINFO();
wi.cbSize = (uint)Marshal.SizeOf(wi);
![]()
//得到當前控件的窗口信息
GetWindowInfo(Handle, ref wi);
![]()
wi.rcClient.Right--;
wi.rcClient.Bottom--;
![]()
![]()
//獲得當前控件的區域
Region UpdateRegion = new Region(new Rectangle(wi.rcWindow.Top,wi.rcWindow.Left,wi.rcWindow.Right-wi.rcWindow.Left,wi.rcWindow.Bottom-wi.rcWindow.Top));
![]()
//獲得客戶區以外的區域
UpdateRegion.Exclude(new Rectangle(wi.rcClient.Top, wi.rcClient.Left, wi.rcClient.Right - wi.rcClient.Left, wi.rcClient.Bottom - wi.rcClient.Top));
![]()
if (IsHScrollVisible && IsVScrollVisible)
{
UpdateRegion.Exclude(Rectangle.FromLTRB
(wi.rcClient.Right + 1, wi.rcClient.Bottom + 1,
wi.rcWindow.Right, wi.rcWindow.Bottom));
}
![]()
//得到當前區域的句柄
IntPtr hRgn = UpdateRegion.GetHrgn(grTemp);
![]()
//For Painting we need to zero offset the Rectangles.
Rectangle WindowRect = new Rectangle(wi.rcWindow.Top, wi.rcWindow.Left, wi.rcWindow.Right - wi.rcWindow.Left, wi.rcWindow.Bottom - wi.rcWindow.Top);
![]()
Point offset = Point.Empty - (Size)WindowRect.Location;
![]()
WindowRect.Offset(offset);
![]()
Rectangle ClientRect = WindowRect;
![]()
ClientRect.Inflate(-1, -1);
![]()
//Fill the BorderArea
Region PaintRegion = new Region(WindowRect);
PaintRegion.Exclude(ClientRect);
grTemp.FillRegion(SystemBrushes.Control, PaintRegion);
![]()
//Fill the Area between the scrollbars
if (IsHScrollVisible && IsVScrollVisible)
{
Rectangle ScrollRect = new Rectangle(ClientRect.Right - ScrollBarWidth,
ClientRect.Bottom - ScrollBarHeight, ScrollBarWidth + 2, ScrollBarHeight + 2);
ScrollRect.Offset(-1, -1);
grTemp.FillRectangle(SystemBrushes.Control, ScrollRect);
}
![]()
//Adjust ClientRect for Drawing Border.
ClientRect.Inflate(2, 2);
ClientRect.Width--;
ClientRect.Height--;
![]()
//Draw Outer Raised Border
ControlPaint.DrawBorder3D(grTemp, WindowRect, Border3DStyle.Raised,
Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right | Border3DSide.Top);
![]()
//Draw Inner Sunken Border
ControlPaint.DrawBorder3D(grTemp, ClientRect, Border3DStyle.Sunken,
Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right | Border3DSide.Top);
![]()
ReleaseDC(Handle, hDC);
![]()
grTemp.Dispose();
![]()
return hRgn;
![]()
}
![]()
RefreshScrollBar();
return region;
}
現在要解決的問題就是如何重新設置客戶區的矩形區域的尺寸,查看了一下Control類的ClientRectangle屬性:
public Rectangle ClientRectangle { get; }是個只讀屬性,看來是不能通過這個屬性達到目的了。再查找Control類的文檔,也沒有這方面的說明,沒有辦法,只能用API搞定了。可以通過計算非客戶區尺寸來設置客戶區尺寸,Border在非客戶繪制。下面就是主要的代碼,就是通過重載WndProc方法,捕捉WM_NCCALCSIZE消息,實現自己的邏輯。
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)WinAPI_WM.WM_NCCALCSIZE:
if (m.WParam.ToInt32() == 0)
{
WinAPI_RECT rc = (WinAPI_RECT)m.GetLParam(typeof(WinAPI_RECT));
rc.Left += 1;
rc.Top += 1;
rc.Right -= 1;
rc.Bottom -= 1;
Marshal.StructureToPtr(rc, m.LParam, true);
m.Result = IntPtr.Zero;
}
else
{
WinAPI_NCCALCSIZE_PARAMS csp;
csp = (WinAPI_NCCALCSIZE_PARAMS)m.GetLParam(typeof(WinAPI_NCCALCSIZE_PARAMS));
csp.rgrc0.Top += 1;
csp.rgrc0.Bottom -= 1;
csp.rgrc0.Left += 1;
csp.rgrc0.Right -= 1;
Marshal.StructureToPtr(csp, m.LParam, true);
//Return zero to preserve client rectangle
m.Result = IntPtr.Zero;
}
break;
case (int)WinAPI_WM.WM_NCPAINT:
{
m.WParam = NCPaint(m.WParam);
break;
}
}
base.WndProc(ref m);
}
public IntPtr NCPaint(IntPtr region)
{
IntPtr hDC = GetWindowDC(this.Handle);
if (hDC != IntPtr.Zero)
{
Graphics grTemp = Graphics.FromHdc(hDC);
int ScrollBarWidth = SystemInformation.VerticalScrollBarWidth;
int ScrollBarHeight = SystemInformation.HorizontalScrollBarHeight;
WINDOWINFO wi = new WINDOWINFO();
wi.cbSize = (uint)Marshal.SizeOf(wi);
//得到當前控件的窗口信息
GetWindowInfo(Handle, ref wi);
wi.rcClient.Right--;
wi.rcClient.Bottom--;

//獲得當前控件的區域
Region UpdateRegion = new Region(new Rectangle(wi.rcWindow.Top,wi.rcWindow.Left,wi.rcWindow.Right-wi.rcWindow.Left,wi.rcWindow.Bottom-wi.rcWindow.Top));
//獲得客戶區以外的區域
UpdateRegion.Exclude(new Rectangle(wi.rcClient.Top, wi.rcClient.Left, wi.rcClient.Right - wi.rcClient.Left, wi.rcClient.Bottom - wi.rcClient.Top));
if (IsHScrollVisible && IsVScrollVisible)
{
UpdateRegion.Exclude(Rectangle.FromLTRB
(wi.rcClient.Right + 1, wi.rcClient.Bottom + 1,
wi.rcWindow.Right, wi.rcWindow.Bottom));
}
//得到當前區域的句柄
IntPtr hRgn = UpdateRegion.GetHrgn(grTemp);
//For Painting we need to zero offset the Rectangles.
Rectangle WindowRect = new Rectangle(wi.rcWindow.Top, wi.rcWindow.Left, wi.rcWindow.Right - wi.rcWindow.Left, wi.rcWindow.Bottom - wi.rcWindow.Top);
Point offset = Point.Empty - (Size)WindowRect.Location;
WindowRect.Offset(offset);
Rectangle ClientRect = WindowRect;
ClientRect.Inflate(-1, -1);
//Fill the BorderArea
Region PaintRegion = new Region(WindowRect);
PaintRegion.Exclude(ClientRect);
grTemp.FillRegion(SystemBrushes.Control, PaintRegion);
//Fill the Area between the scrollbars
if (IsHScrollVisible && IsVScrollVisible)
{
Rectangle ScrollRect = new Rectangle(ClientRect.Right - ScrollBarWidth,
ClientRect.Bottom - ScrollBarHeight, ScrollBarWidth + 2, ScrollBarHeight + 2);
ScrollRect.Offset(-1, -1);
grTemp.FillRectangle(SystemBrushes.Control, ScrollRect);
}
//Adjust ClientRect for Drawing Border.
ClientRect.Inflate(2, 2);
ClientRect.Width--;
ClientRect.Height--;
//Draw Outer Raised Border
ControlPaint.DrawBorder3D(grTemp, WindowRect, Border3DStyle.Raised,
Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right | Border3DSide.Top);
//Draw Inner Sunken Border
ControlPaint.DrawBorder3D(grTemp, ClientRect, Border3DStyle.Sunken,
Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right | Border3DSide.Top);
ReleaseDC(Handle, hDC);
grTemp.Dispose();
return hRgn;
}
RefreshScrollBar();
return region;
}




浙公網安備 33010602011771號