WinAPI C++:重新编程窗口大小调整
WinAPI C++: Reprogramming Window Resize
我有一个窗口,我想像任何其他窗口一样实现边框大小。从评论和答案中采纳建议,我重写了我的代码。对于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;
变量是不言自明的。这段代码给了我一个边框,我可以拖动它来调整窗口大小。当我拖动右下、下和右边框时,它效果很好。对于其他边框,当我尝试拖动它们时,窗口的右下角似乎仍然来回移动。它类似于在Google Chrome或Visual Studio 2012中看到的具有相同边框集的内容,但我在Windows资源管理器中没有看到这一点。
有没有办法使右下角在我调整顶部或左侧边框的大小时不会来回"蠕动",就像在 Windows 资源管理器中一样?
有点晚了,但我想我已经找到了一种在不"蠕动"的情况下调整大小的方法(内部窗口绘制滞后仍然存在(。
与曼努埃尔所说的不同,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,
GetModuleHandle(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
的消息处理程序。 这也允许实现你的边框宽度。 相同的配方,首先调用 DefWindowProc((,在适当的时候覆盖返回值。
唉,这不是你正在等待的答案。在 Windows Seven 上,同时移动和调整大小WS_POPUP具有样式的顶级窗口确实被破坏了。在视觉上,首先移动窗口,然后调整大小。按左侧或顶部调整大小时,移动操作会短暂显示背景像素,从而导致非常糟糕的用户体验。
据我所知,这与WM_GETMINMAXINFO或WM_NCCALCSIZE无关。
看到效果非常简单:创建一个WS_POPUP |WS_VISIBLE窗口过程几乎为空的窗口中,请设置一个计时器,并在WM_TIMER中使用 SetWindowPos,将窗口向左移动一点,同时放大窗口,以便让右边缘在同一位置。你会看到背景像素,这很愚蠢。在Windows XP上没有这样的破损。
我尝试了很多技巧,其中一些非常扭曲,但最终结果总是相同的:在窗口最终呈现为新状态的那一刻,首先是移动操作,然后是大小操作......
您剩下 2 个选项(如果您的目标是 Seven+(:
1(使用标准大小边框并利用新的API(例如:DwmExtendFrameIntoClientArea(来自定义框架以满足您的需求。请参阅在 http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195.aspx 使用 DWM 的自定义窗口框架
2(不要使用WS_POPUP,而是WS_BORDER并使用欺骗Windows的技巧,以永不渲染边框。似乎这就是VS2012正在做的事情。
不要忘记:在窗口内闪烁是另一回事,我只是在这里谈论右/底边缘"稳定性"。
查看正在更改窗口大小和位置的代码会很有帮助。
当您移动底部或右侧时,您只是更改窗口的大小(高度或宽度(。当您移动顶部或左侧时,您不仅要更改大小,还要更改上角/左角位置。
如果有人想将左边框向右移动 10 像素,那么您必须将角位置增加 10 并将宽度减少 10 - 最好同时(例如,同时使用 SetWindowPos 进行两次更改(。
请注意,更改该角位置也会更改鼠标屏幕坐标的解释方式。因此,旧仓位的任何存储也必须更新。
你只需要处理WM_NCCALCSIZE
消息,用边框宽度增加左rgrc矩形,用标题栏高度增加顶部,用边框宽度减少右边,用标题栏高度减少底部。对于边框角,您应该更改WM_SIZE
消息上的窗口区域。
- SDL2 调整窗口大小后如何缩放鼠标坐标?
- 如何设置与其背景图像大小相对应的窗口大小?
- 如何防止opengl绘图拉伸到窗口大小?
- 使用 Win32 将 GDI 绘制大小缩放为窗口大小
- 恢复Qt窗口大小和位置问题
- 与WM_DPICHANGED消息一起发送的建议窗口大小太大
- 快板获取当前窗口大小
- 当窗口大小更改时,如何自动缩放QT中的图表
- 尝试在 win 32 中禁用窗口大小调整时,窗口样式行为不一致
- OpenGL纹理闪烁/有时在窗口大小上移动
- QGraphics手动调整窗口大小后场景宽度/高度没有变化
- Qt - 防止在拖动标题栏时调整窗口大小
- 在 Direct2D 绘图中,调整窗口大小后尺寸会更改
- 在窗口大小上绘制的OpenGL消失
- XDG-Shell:如何更改窗口大小
- WinAPI.检查窗口大小调整是否已完成
- Win32 透明全屏仅在窗口大小溢出桌面时才有效
- 如何设置启用setFixize后再次调整主窗口大小
- C ,QT,防止窗口大小大于实际布局高度
- WinAPI C++:重新编程窗口大小调整