[英]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;
這些變量非常明顯。 這段代碼給了我一個邊框,我可以拖動它來調整窗口大小。 拖動右下角,右下角和右邊框時效果很好。 對於其他邊框,當我嘗試拖動它們時,窗口的右下角似乎仍然來回移動。 它類似於谷歌Chrome或Visual Studio 2012中使用相同的邊框集,但我在Windows資源管理器中看不到這一點。
當我調整頂部或左邊框時,有沒有辦法讓右下角不來回“蠕動”,就像在Windows資源管理器中一樣?
我知道這有點晚了,但我想我找到了一種不用“蠕動”調整大小的方法(內部窗口繪制滯后仍然存在)。
與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,
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
實現消息處理程序。 這也允許實現你的BORDERWIDTH。 相同的配方,首先調用DefWindowProc(),在適當時覆蓋返回值。
唉,這不是你要等的答案。 在Windows 7上,同時移動和調整大小與WS_POPUP樣式的頂級窗口確實被破壞了。 在視覺上,首先移動窗口,然后調整大小。 通過左側或頂部調整大小時,移動操作會短暫顯示背景像素,從而導致非常糟糕的用戶體驗。
據我了解發生了什么,它與WM_GETMINMAXINFO或WM_NCCALCSIZE無關。
看到效果非常簡單:創建一個WS_POPUP | WS_VISIBLE窗口帶有一個幾乎為空的窗口程序,設置一個計時器並在WM_TIMER中使用SetWindowPos,將窗口稍微向左移動,同時調整它的大小,以便讓右邊緣在同一個地方。 你會看到背景像素,這是愚蠢的。 在Windows XP上沒有這樣的破壞。
我嘗試了很多技巧,其中一些非常扭曲,但最終結果總是一樣的:當窗口最終呈現在新狀態時,首先是一個移動操作,然后是一個尺寸...
你有兩個選擇(如果你的目標是七+):
1)使用標准大小邊框並利用新API(例如:DwmExtendFrameIntoClientArea)自定義框架以滿足您的需求。 請參閱使用DWM自定義窗口框架, 網址為http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195.aspx
2)不要使用WS_POPUP,而是使用WS_BORDER並使用欺騙Windows的技巧來永久渲染邊框。 這似乎是VS2012正在做的事情。
不要忘記:閃爍在窗口內是另一個故事,我只是在談論右邊/底邊“穩定”這里。
看到正在改變窗口大小和位置的代碼會很有幫助。
當您移動底部或右側時,您只需更改窗口的大小(高度或寬度)。 移動頂部或左側時,您不僅要更改尺寸,還要更改上/左角位置。
如果有人想要將左邊框向右移動10個像素,則必須將角位置增加10並將寬度減小10 - 最好相同(例如,同時使用SetWindowPos進行兩次更改)。
請注意,更改該角位置也會更改鼠標的屏幕坐標的解釋方式。 因此,任何舊倉位的存儲也必須更新。
您只需處理WM_NCCALCSIZE
消息,使用邊框寬度增加左邊的rgrc矩形,並使用CaptionBar高度增加頂部,使用邊框寬度減少右邊,使用CaptionBar高度減小底部。 對於邊界角,您應該在WM_SIZE
消息上更改窗口區域。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.