我正在创建一个自定义表单(C#/ Windows Forms / Vista / Windows7),并覆盖WndProc以捕获WM_NCPAINT,WM_NCCALCSIZE和WM_NCHITTEST以绘制自定义框架。我差不多完成了它,但有一个问题,我无法解决自己。
问题是NC_CALCSIZE使我的表单在最大化后恢复时缩小。我用Google搜索并找到了鲍勃鲍威尔的答案,他说当WPARAM为TRUE时我不需要处理NC_CALCSIZE。在我完成之后,WM_NCPAINT不再有效(它确实处理WM_NCPAINT,但它不再绘制非客户区域,只有在我使其无效之后)。
所以,重新开始,当我处理WM_NCCALCSIZE(WPARAM == TRUE)时,它缩小了我的形状,当我不这样做时,它不再绘画了。
以前有人有这个问题吗?如果需要更多代码,我可以提供。 TKS。
这是我的WN_CALCSIZE代码:
private void WndProcNonClientCalcSize(ref Message m)
{
if (m.WParam == WinAPI.FALSE)
{
this.Log(MethodInfo.GetCurrentMethod(), "FALSE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
csp.rectProposed.Top += this._nonClientHeight;
csp.rectProposed.Bottom -= this._nonClientBorderThickness;
csp.rectProposed.Left += this._nonClientBorderThickness;
csp.rectProposed.Right -= this._nonClientBorderThickness;
Marshal.StructureToPtr(csp, m.LParam, false);
}
else if (m.WParam == WinAPI.TRUE)
{
this.Log(MethodInfo.GetCurrentMethod(), "TRUE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
csp.rectProposed.Top += this._nonClientHeight;
csp.rectProposed.Bottom -= this._nonClientBorderThickness;
csp.rectProposed.Left += this._nonClientBorderThickness;
csp.rectProposed.Right -= this._nonClientBorderThickness;
Marshal.StructureToPtr(csp, m.LParam, false);
}
m.Result = WinAPI.TRUE;
}
这是我的WM_NCPAINT代码:
private bool WndProcNonClientPaint(ref Message m)
{
this.Log(MethodInfo.GetCurrentMethod(), string.Empty);
this.PaintNonClient(m.HWnd, (IntPtr)m.WParam);
m.Result = WinAPI.TRUE;
return true;
}
private void PaintNonClient(IntPtr hWnd, IntPtr hRgn)
{
WinAPI.RECT windowRect = new WinAPI.RECT();
WinAPI.GetWindowRect(hWnd, ref windowRect);
Rectangle bounds = new Rectangle(0, 0,
windowRect.Right - windowRect.Left,
windowRect.Bottom - windowRect.Top);
if (bounds.Width == 0 || bounds.Height == 0)
return;
Region clipRegion = new Region(bounds);
if (hRgn != (IntPtr)1)
clipRegion = Region.FromHrgn(hRgn);
WinAPI.DCV dcv =
WinAPI.DCV.WINDOW |
WinAPI.DCV.INTERSECTRGN |
WinAPI.DCV.CACHE |
WinAPI.DCV.CLIPSIBLINGS;
IntPtr hDC =
WinAPI.GetDCEx(
hWnd,
hRgn,
dcv);
if (hDC == IntPtr.Zero)
hDC = WinAPI.GetWindowDC(hWnd);
IntPtr compatiblehDC = WinAPI.CreateCompatibleDC(hDC);
IntPtr compatibleBitmap = WinAPI.CreateCompatibleBitmap(hDC, bounds.Width, bounds.Height);
try
{
WinAPI.SelectObject(compatiblehDC, compatibleBitmap);
WinAPI.BitBlt(compatiblehDC, 0, 0, bounds.Width, bounds.Height, hDC, 0, 0, WinAPI.SRCCOPY);
using (Graphics g = Graphics.FromHdc(compatiblehDC))
{
Rectangle outterEdge = new Rectangle(0, 0, this.Width, this.Height);
int x = this._nonClientBorderThickness;
int y = this._nonClientHeight;
int width = this.Width - (this._nonClientBorderThickness * 2);
int height = this.Height - this._nonClientBorderThickness - this._nonClientHeight;
Rectangle innerEdge = new Rectangle(x, y, width, height);
GraphicsPath path = new GraphicsPath();
path.AddRectangle(outterEdge);
path.AddRectangle(innerEdge);
using (SolidBrush brush = new SolidBrush(Color.FromArgb(45, 45, 48)))
g.FillPath(brush, path);
path.Dispose();
}
WinAPI.BitBlt(hDC, 0, 0, bounds.Width, bounds.Height, compatiblehDC, 0, 0, WinAPI.SRCCOPY);
}
finally
{
WinAPI.DeleteObject(compatibleBitmap);
WinAPI.DeleteDC(compatiblehDC);
}
}
答案 0 :(得分:2)
嗯,我无法让它工作,似乎返回WM_NCCALCSIZE比我预期的更复杂。我根本无法理解它,因为它没有按照我的预期去做。我尝试做了这篇文章所说的内容但是再次没用:
http://blogs.msdn.com/b/oldnewthing/archive/2003/09/15/54925.aspx
所以我再次搜索,我在CodeProject上发现了这篇文章,描述了我遇到的同样问题:
http://www.codeproject.com/Articles/55180/Extending-the-Non-Client-Area-in-Aero
解决方法是收听WM_SYSCOMMAND并捕获SC_RESTORE,将我的表单宽度和高度设置为I Maximized / Restored。
我的WM_NCCALCSIZE成了这个:
private void WndProcNonClientCalcSize(ref Message m)
{
if (m.WParam == WinAPI.FALSE)
{
this.Log(MethodInfo.GetCurrentMethod(), "FALSE");
WinAPI.RECT rect = (WinAPI.RECT)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.RECT));
rect.Left += this._nonClientBorderThickness;
rect.Top += this._nonClientHeight;
rect.Right -= this._nonClientBorderThickness;
rect.Bottom -= this._nonClientBorderThickness;
Marshal.StructureToPtr(rect, m.LParam, false);
m.Result = WinAPI.FALSE;
}
else if (m.WParam == WinAPI.TRUE)
{
this.Log(MethodInfo.GetCurrentMethod(), "TRUE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
WinAPI.RECT rectNewClient = csp.rectProposed;
rectNewClient.Left += this._nonClientBorderThickness;
rectNewClient.Top += this._nonClientHeight;
rectNewClient.Right -= this._nonClientBorderThickness;
rectNewClient.Bottom -= this._nonClientBorderThickness;
csp.rectProposed = rectNewClient;
csp.rectBeforeMove = csp.rectProposed;
Marshal.StructureToPtr(csp, m.LParam, false);
m.Result = (IntPtr)(WinAPI.NCCALCSIZE_RESULTS.ValidRects);
}
}
和我的WM_SYSCOMMAND:
private void WndProcSysCommand(ref Message m)
{
UInt32 param;
if (IntPtr.Size == 4)
param = (UInt32)(m.WParam.ToInt32());
else
param = (UInt32)(m.WParam.ToInt64());
if ((param & 0xFFF0) == (int)WinAPI.SystemCommands.SC_RESTORE)
{
this.Height = this._storedHeight;
this.Width = this._storedWidth;
}
else if (this.WindowState == FormWindowState.Normal)
{
this._storedHeight = this.Height;
this._storedWidth = this.Width;
}
base.WndProc(ref m);
}
这可能不是最佳解决方案,但已完成工作。如果有人能提供更好的解决方案,我真的会感到满意。
Tks,Hans Passant和Tergiver引起你的注意。
答案 1 :(得分:1)
WM_NCCALCSIZE
没问题。处理WM_NCCALCSISE
,.NET调用RestoreWindowBoundsIfNecessary()
方法时,窗口的大小发生了变化(请参阅Changing a window's restore position using SetWindowPlacement doesn't work on every window)。
您可以在表单最小化时手动缓存表单的大小,并在还原时重置,或者不调用基本WndProc()
方法,该方法调用RestoreWindowBoundsIfNecessary()
答案 2 :(得分:0)
我有不同的结果,我真的希望得到公众对此解决方案的意见:
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
width += BORDER_LEFT_WIDTH + BORDER_RIGHT_WIDTH;
height += BORDER_TOP_HEIGHT + TITLEBAR_HEIGHT + BORDER_BOTTOM_HEIGHT;
base.SetBoundsCore(x, y, width, height, specified);
}
这实际上抵消了我在处理类似问题时遇到的萎缩。
答案 3 :(得分:-1)
看起来问题有更优雅的解决方案,但仍然是hackish。见Custom window frame with DWM: how to handle WM_NCCALCSIZE correctly。
不同之处在于,这里的解决方案要么改变窗口大小,导致重绘/闪烁,要么人为地收缩边界,而上面的帖子解决方案只是说你的窗口没有标题/边框,因此窗口和客户区域的大小相等。
顺便说一下,如果你想让客户区占用所有窗口大小,可以将WndProcNonClientCalcSize简化为
private void WndProcNonClientCalcSize(ref Message m)
{
m.Result = IntPtr.Zero;
}