Проблема, которая у меня кажется тривиальной, но я не могу найти способ ее решить. Вот. У меня есть окно с графикой.Рисование в окне при изменении размера листьев Неокрашенная граница
Для простоты можно сказать, что это сплошной зеленый прямоугольник, который заполняет всю клиентскую область окна. Я хочу, чтобы этот прямоугольник перерисовывался и заполнял все окно каждый раз, когда окно меняет свой размер. Первоначально я это делал. Я отправил WM_PAINT сообщение от WM_SIZE обработчик.
Это работает, но если я быстро перемещаю мышь, я вижу немного неокрашенной (белой) области вокруг зеленого прямоугольника (на самом деле только одна или две стороны, рядом с той, где находится мышь). Мое понимание проблемы заключается в том, что системный поток, который обрабатывает ввод пользователя (мышь), работает быстрее, чем мой обработчик сообщения WM_PAINT. Это означает, что к моменту начала рисования обновленного прямоугольника (его размер берется из WM_SIZE) мышь на самом деле немного движется, и система рисует новый оконный кадр, который отличается от того, что я пытаюсь заполнить зеленым. Это создает незаполненные области рядом с границами, которые перемещаются во время изменения размера.
Когда я останавливаю изменение размера, зеленый цвет заполняет все окно, но при изменении размера происходит немного мерцание, происходящее близко к границам, что раздражает. Чтобы решить проблему, я попробовал следующее.
bool finishedPainting;
RECT windowRect;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
// .... some actions
// posting WM_PAINT
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
case WM_SIZING :
// this supposedly should prevent the system from passing
// new window size to WM_SIZE
if (!finishedPainting) memcpy((void*)lParam, &windowRect, sizeof(windowRect));
else {
// remember current window size for later use
memcpy(&windowRect, (void*)lParam, sizeof(windowRect));
finishedPainting = FALSE;
}
return TRUE;
Это не работает. Как небольшое изменение, я также пробовал это.
bool finishedPainting;
POINT cursorPos;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
if (!finishedPainting) SetCursorPos(cursorPos.x, cursorPos.y);
else {
finishedPainting = FALSE;
GetCursorPos(&cursorPos);
// .... some actions
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
}
break;
Это также не работает. Насколько я понимаю, решение проблемы заключается в том, что она каким-то образом замедляет мышь, так что она перемещается в следующую позицию на экране (перетаскивая угол или сторону окна вместе с ним) только после завершения рисования.
Любые идеи, как достичь этого? Или, может быть, есть что-то принципиально неправильное в том, как я вижу проблему и решение лежит где-то в другом месте?
// ===================================================================================================================== ========
Update
Я сделал несколько экспериментов, и вот что я нашел
1) При изменении размера, последовательность сообщений является WM_SIZING - WM_NCPAINT - WM_SIZE - WM_PAINT. Мне это немного странно. Я ожидал бы, что WM_SIZE будет следовать за WM_SIZING без прерывания с помощью WM_NCPAINT
2) В каждом обработчике сообщений я проверял ширину окна во время изменения размера (для простоты я менял только ширину). Удивительно, что ширина, измеренная в WM_SIZE, оказалась отличной от той, что была в WM_SIZING, но такая же, как в WM_NCPAINT и WM_PAINT. Это не проблема как таковая, просто странный факт.
3) Я пришел к выводу, что существуют две основные причины мерцания вблизи границ окна. Первый заключается в том, что WM_NCPAINT поставляется до WM_PAINT. Представьте, что вы растягиваете свое окно. Сначала появится первый кадр (сначала появится WM_NCPAINT), затем WM_PAINT заполняет клиентскую область. Человеческий глаз ловит этот короткий промежуток времени, когда новый кадр уже находится на экране, но он пуст. Даже если вы укажете, что вы не хотите, чтобы фон фона удалялся перед перекраской, все еще добавленная область пуста, и вы можете увидеть ее в течение секунды. Эта причина для мерцания лучше всего продемонстрировать, когда вы берете правый край окна и быстро перемещаете его вправо.Другая причина мерцания эффекта менее очевидна и лучше всего видна, когда вы берете левый край окна и перемещаете его влево. Во время этого перемещения вы увидите незаполненные области вдоль правого края. Насколько я понимаю, эффект вызван этим. Когда пользователь выполняет изменение размера Windows делает следующее: A) он отправляет WM_NCPAINT для рисования нового кадра, B) он копирует содержимое старой клиентской области в новый верхний угол окна (в нашем случае он перемещается влево), C) он отправляет WM_PAINT для заполнения новой клиентской области. Однако на этапе B по какой-то причине Windows создает те незаполненные области вдоль правого края, хотя кажется, что это не должно быть, потому что старый контент должен оставаться там, где он есть, пока он не будет перерисовываться во время WM_PAINT.
Хорошо, вопрос остается - как избавиться от этих артефактов во время изменения размера. Насколько я могу видеть сейчас, невозможно использовать стандартные методы и функции, потому что они вызваны последовательностью шагов, выполняемых Windows во время изменения размера. Обмен WM_NCPAINT и WM_PAINT, вероятно, поможет, но это, похоже, не под нашим контролем (если только нет простого способа сделать то, о чем я просто не знаю).
Вы копаете себе довольно глубокое отверстие здесь. Запретить мерцание путем двойной буферизации или путем написания обработчика сообщений для WM_ERASEBKGND, который ничего не делает. –
Ханс, я делаю двойную буферизацию, обрабатываю WM_ERASEBKGND и делаю другие трюки, чтобы уменьшить мерцание. Дело в том, что в этом случае мерцание НЕ вызвано неправильной техникой окраски. На самом деле у меня вообще нет мерцания во время обычных операций. Картина при изменении размера - это единственное, что я не могу победить. И снова, даже в этом случае мои вещи не мерцают. Это новая добавленная область окна, которая мерцает, потому что я не могу контролировать ее добавление. Кажется, что проблема на одном или двух уровнях ниже - это то, что делает Windows при изменении размеров окон. – wladp
Вам не нужно будет обрабатывать WM_SIZE/SIZING - если вы хотите, чтобы все окно было перерисовано при изменении размера, просто укажите [CS_HREDRAW и CS_VREDRAW биты стиля класса в WNDCLASS] (http://msdn.microsoft.com). /en-us/library/windows/desktop/ff729176(v=vs.85).aspx), и Windows позаботится об этом для вас. Кроме того, некоторый уровень мерцания рядом с краем границы кажется неизбежным (в ответе Марка) ниже - вы можете сравнить с такими приложениями, как Explorer, Chrome, IE и т. Д., Чтобы узнать, что такое «базовый уровень», и проверить, что вы находитесь на по крайней мере, не хуже. – BrendanMcK