У меня есть окно, и я хочу реализовать границы как изменение размеров границ, как и любое другое окно. Принимая предложения от комментариев и ответов, я переписал свой код. Для 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 Explorer?
Я знаю, что уже немного поздно, но я думаю, что нашел способ изменить размер без «шевеления» (задержка внутри рисования окна все равно останется).
В отличие от того, что сказал Мануэлл, 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) для настройки фрейма в соответствии с вашими потребностями. См. Пользовательскую оконную раму с использованием 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
сообщение