我有一个窗口,我想要实现边界的调整边界,就像任何其他窗口。 从意见和答复中收集build议,我重写了我的代码。 对于WM_GETMINMAXINFO我有:
MINMAXINFO *min_max = reinterpret_cast<MINMAXINFO *>(lparam); min_max->ptMinTrackSize.x = MINX; min_max->ptMinTrackSize.y = MINY;
MINX和MINY是我希望窗口的最小尺寸。 对于WM_NCHITTEST我有:
RECT wnd_rect; int x, y; GetWindowRect (window, &wnd_rect); x = GET_X_LPARAM (lparam) - wnd_rect.left; y = GET_Y_LPARAM (lparam) - wnd_rect.top; if (x >= BORDERWIDTH && x <= wnd_rect.right - wnd_rect.left - >BORDERWIDTH && y >= BORDERWIDTH && y <= TITLEBARWIDTH) return HTCAPTION; else if (x < BORDERWIDTH && y < BORDERWIDTH) return HTTOPLEFT; else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH && y < BORDERWIDTH) return HTTOPRIGHT; else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH && y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH) return HTBOTTOMRIGHT; else if (x < BORDERWIDTH && y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH) return HTBOTTOMLEFT; else if (x < BORDERWIDTH) return HTLEFT; else if (y < BORDERWIDTH) return HTTOP; else if (x > wnd_rect.right - wnd_rect.left - BORDERWIDTH) return HTRIGHT; else if (y > wnd_rect.bottom - wnd_rect.top - BORDERWIDTH) return HTBOTTOM; return HTCLIENT;
variables是非常明显的。 这个代码给了我一个边框,我可以拖动来调整窗口的大小。 当我拖动右下angular,底部和右侧边框时,效果很好。 与其他边界,当我试图拖动它们时,窗口的右下angular似乎仍然来回移动。 它与谷歌浏览器或Visual Studio 2012具有相同的边框类似,但在Windows资源pipe理器中没有看到。
有没有办法让右下angular不会像我在Windows资源pipe理器中调整顶部或左边框那样来回“蠕动”?
我知道这有点晚了,但是我想我已经找到了一个没有“蠕动”的方法来调整大小(窗口内部的绘制滞后依然存在)。
与manuell所说的不同, WM_NCCALCSIZE
是万恶之源。 此外,此方法应该能够在保留其功能的同时使用任何窗口样式(使用WS_POPUP
和WS_OVERLAPPEDWINDOW
进行测试),所以现在是时候闭嘴并发布代码:
//some sizing border definitions #define MINX 200 #define MINY 200 #define BORDERWIDTH 5 #define TITLEBARWIDTH 30 //................ HWND TempHwnd = Create(NULL, TEXT("CUSTOM BORDER"), TEXT("CUSTOM BORDER"), WS_POPUP | WS_VISIBLE, 100, 100, 400, 400, NULL, NULL, GetmoduleeHandle(NULL), NULL); //............... LRESULT CALLBACK WinMsgHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_SIZING: // I use this message to redraw window on sizing (o rly?) RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_INTERNALPAINT); return DefWindowProc(hWnd, uMsg, wParam, lParam); case WM_PAINT: // Used to draw borders and stuff to test WM_NCHITTEST { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); RECT ClientRect; GetClientRect(hWnd, &ClientRect); RECT BorderRect = { BORDERWIDTH, BORDERWIDTH, ClientRect.right - BORDERWIDTH - BORDERWIDTH, ClientRect.bottom - BORDERWIDTH - BORDERWIDTH }, TitleRect = { BORDERWIDTH, BORDERWIDTH, ClientRect.right - BORDERWIDTH - BORDERWIDTH, TITLEBARWIDTH }; HBRUSH BorderBrush = CreateSolidBrush(0x0000ff); FillRect(ps.hdc, &ClientRect, BorderBrush); FillRect(ps.hdc, &BorderRect, GetSysColorBrush(2)); FillRect(ps.hdc, &TitleRect, GetSysColorBrush(1)); DeleteObject(BorderBrush); EndPaint(hWnd, &ps); } break; case WM_GETMINMAXINFO: // It is used to restrict WS_POPUP window size { // I don't know if this works on others MINMAXINFO *min_max = reinterpret_cast<MINMAXINFO *>(lParam); min_max->ptMinTrackSize.x = MINX; min_max->ptMinTrackSize.y = MINY; } break; case WM_CREATE: // In this message we use MoveWindow to invoke { //WM_NCCALCSIZE msg to remove border CREATESTRUCT *WindowInfo = reinterpret_cast<CREATESTRUCT *>(lParam); MoveWindow(hWnd, WindowInfo->x, WindowInfo->y, WindowInfo->cx - BORDERWIDTH, WindowInfo->cy - BORDERWIDTH, TRUE); //Notice that "- BORDERWIDTH" is recommended on every manually called resize function, //Because we will add BORDERWIDTH value in WM_NCCALCSIZE message } break; case WM_NCCALCSIZE: { // Microsoft mentioned that if wParam is true, returning 0 should be enough, but after MoveWindow or similar functions it would begin to "wriggle" if (wParam) { NCCALCSIZE_PARAMS *Params = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam); Params->rgrc[0].bottom += BORDERWIDTH; // rgrc[0] is what makes this work, don't know what others (rgrc[1], rgrc[2]) do, but why not change them all? Params->rgrc[0].right += BORDERWIDTH; Params->rgrc[1].bottom += BORDERWIDTH; Params->rgrc[1].right += BORDERWIDTH; Params->rgrc[2].bottom += BORDERWIDTH; Params->rgrc[2].right += BORDERWIDTH; return 0; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } break; case WM_NCHITTEST: { RECT WindowRect; int x, y; GetWindowRect(hWnd, &WindowRect); x = GET_X_LPARAM(lParam) - WindowRect.left; y = GET_Y_LPARAM(lParam) - WindowRect.top; if (x >= BORDERWIDTH && x <= WindowRect.right - WindowRect.left - BORDERWIDTH && y >= BORDERWIDTH && y <= TITLEBARWIDTH) return HTCAPTION; else if (x < BORDERWIDTH && y < BORDERWIDTH) return HTTOPLEFT; else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y < BORDERWIDTH) return HTTOPRIGHT; else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) return HTBOTTOMRIGHT; else if (x < BORDERWIDTH && y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) return HTBOTTOMLEFT; else if (x < BORDERWIDTH) return HTLEFT; else if (y < BORDERWIDTH) return HTTOP; else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH) return HTRIGHT; else if (y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) return HTBOTTOM; else return HTCLIENT; } break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; }
这种代码很匆忙,你通过改变客户区的位置来改变鼠标的相对位置。 这就要求你在窗口太小时忽略鼠标移动来更新* track_start *变量。 不这样做会产生一个有趣的效果,窗口来回跳动。 是的,“蠕动”。
只要不这样做,你正在寻找的功能已经实现。 编写WM_GETMINMAXINFO
的消息处理程序。 先调用DefWindowProc(),然后重写MINMAXINFO.ptMinTrackSize值。 如果意图是在无边框窗口上实现边角或边缘拖动,那么为WM_NCHITTEST
实现消息处理程序。 这也允许实现你的BORDERWIDTH。 同样的方法,首先调用DefWindowProc(),在适当的时候覆盖返回值。
唉,这不是你正在等待的答案。 在Windows 7上,同时移动和调整具有WS_POPUP样式的顶层窗口确实被打破。 在视觉上,首先移动窗口,然后调整大小。 当按左或上尺寸确定尺寸时,移动操作会短暂地显示背景像素,从而导致非常不好的用户体验。
据我了解发生了什么事情,这与WM_GETMINMAXINFO或WM_NCCALCSIZE没有任何关系。
看到效果非常简单:创建一个WS_POPUP | WS_VISIBLE窗口用一个几乎为空的窗口过程,设置一个定时器,并在WM_TIMER中使用SetWindowPos,将窗口稍微向左移动一边调整它的大小,以便让右边缘在同一个地方。 你会看到背景像素,这是愚蠢的。 在Windows XP上没有这样的损坏。
我尝试了很多技巧,其中一些非常扭曲,但最终的结果总是一样的:此时窗口最终呈现为新的状态,首先是一个移动操作,然后是一个尺寸…
你剩下2个选项(如果你瞄准七+):
1)使用标准的尺寸边框,并利用新的API(例如:DwmExtendFrameIntoClientArea)来定制框架以适应您的需求。 请参阅使用DWM的自定义窗口框架, 网址为http://msdn.microsoft.com/zh-cn/library/windows/desktop/bb688195.aspx
2)不要使用WS_POPUP,而要使用WS_BORDER,并使用欺骗Windows的技巧来永不渲染边界。 这似乎是VS2012正在做的事情。
不要忘记:闪烁窗内是另外一个故事,我只是在这里谈论右边/底边的“稳定”。
看到你的代码正在改变窗口的大小和位置是有帮助的。
当您移动底部或右侧时,只会改变窗口的大小(高度或宽度)。 移动顶部或左侧时,不仅要改变大小,还要改变顶部/左侧角位置。
如果有人想将左边框向右移动10个像素,则必须将角位置增加10,并将宽度减少10,最好是相同(例如同时使用SetWindowPos)。
请注意,改变角落位置也会改变鼠标屏幕坐标的解释方式。 所以任何存储旧位置也必须更新。
您只需要处理WM_NCCALCSIZE
消息,使用边框宽度增加左边的rgrc矩形,然后使用CaptionBar高度增加顶部,然后使用CaptionBar高度减小右边的边框宽度和底部。 对于边框角落,你应该改变WM_SIZE
消息的窗口区域。