ПРОБЛЕМА: Когда я беру границу изменения размера моего приложения Windows, особенно верхнюю или левую границы, и изменяю размер окна, содержимое окна изменяет размер "вживую" при перетаскивании, но они изменяют размер отвратительным образом, который выглядит как вопиющая ошибка даже самого начинающего пользователя: содержимое на противоположном краю окна от края, который я дико перетаскиваю джиттер/мерцание/скачок назад и вперед. В зависимости от ситуации явление может выглядеть следующим образом:
- содержимое, которое, кажется, сходит с края окна и возвращается назад, когда мы замедляемся или перестаем перетаскивать
- содержимое, которое, кажется, тянет в окно, периодически смещается границей различных цветов, часто черного или белого
- серьезно уродливое "двойное изображение" с двумя перекрывающимися копиями контента, смещенными на расстояние, пропорциональное тому, насколько/насколько быстро мы перетаскиваем
Ужасное явление прекращается, как только я прекращаю перетаскивать, но во время перетаскивания оно заставляет приложение выглядеть любительским и непрофессиональным.
Не стоит преуменьшать, что эта проблема Windows свела с ума тысячи разработчиков приложений.
Вот два примера картины этого явления, любезно подготовленные к смежному вопросу Романа Старкова:
Другой пример, показывающий злое явление "двойного изображения" (обратите внимание на быстрые вспышки) от Кенни Лю:
Еще один пример видео о явлении с помощью диспетчера задач здесь.
ЦЕЛЬ НАСТОЯЩЕГО ВОПРОСА. Любой разработчик, столкнувшийся с этой проблемой, быстро обнаруживает, что существует по меньшей мере 30 вопросов StackOverflow, некоторые из которых были недавно, а некоторые возникли в 2008 году, полные многообещающих ответов, которые редко работают. Реальность такова, что эта проблема имеет много причин, и существующие вопросы/ответы StackOverflow никогда не проясняют более широкий контекст. Этот вопрос стремится ответить:
- Каковы наиболее вероятные причины этого вида уродливого дрожания/мерцания/прыжков?
- Как я могу сказать, что, потому что я вижу?
- это причина специфическая для конкретных графических драйверов или общая для Windows?
- как исправить каждую причину? может ли приложение это исправить?
СФЕРА ЭТОГО ВОПРОСА: Для объема этого вопроса StackOverflow, явление происходит с:
- как родные Win32, так и управляемые приложения .NET/WPF/Windows Forms
- и обычные окна Win32 и окна диалога Win32
- Версии Windows, включая XP, Vista, 7, 8 и 10 (но см. Ниже темную правду о множественных причинах)
НЕ ФЛАГИРУЙТЕ ШИРОКИЙ ИЛИ ДУПАЙ: Пожалуйста, прочитайте полный ответ ниже, прежде чем отмечать как дупли или отмечать широко. Мы уже прошли этот цикл, и пользователи SO впоследствии вновь открыли вопрос, поняв следующее: этот вопрос приносит пользу сообществу, поскольку он является первым Q & A, объясняющим все различные причины дрожания изменения размера окна, чтобы пользователи могли определить, какой из причины вызывает их проблему и решить ее. Приведенные ниже ответы являются длинными, просто потому, что сложно объяснить, почему джиттер возникает в целом, а не потому, что этот вопрос охватывает множество отдельных ошибок/проблем, которые будут лучше обслуживаться отдельными вопросами и ответами. Как вы увидите, все приведенные выше перестановки (нативная/управляемая, окно/диалоговое окно, XP-10) сводятся только к двум основным причинам, но выявление того, что у вас есть, является сложной задачей. Вот о чем этот вопрос. Разделение вопроса полностью сведет на нет эту выгоду. Мы дополнительно ограничим сферу вопроса в следующем разделе...
НЕ В СФЕРЕ ЭТОГО ВОПРОСА:
-
Если в вашем приложении есть одно или несколько дочерних окон (дочерних HWND), информация в этом вопросе полезна для вас (поскольку
BitBlts
вызывающиеBitBlts
мы опишем, применяются к вашим дочерним окнам вместе с родительским окном), но при изменении размера окна у вас есть дополнительная проблема для решения, которая выходит за рамки этого вопроса: вам нужно заставить все ваши дочерние окна двигаться атомарно и синхронно с родительским окном. Для этой задачи вам, вероятно, понадобитсяBeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
и вы можете узнать о них здесь и здесь. -
Этот вопрос предполагает, что если ваше приложение обращается к окну, используя GDI, DirectX или OpenGL, то вы уже
WM_ERASEBKGND
обработчикWM_ERASEBKGND
в свойwndproc
который просто возвращает 1.WM_ERASEBKGND
- это тайный остаток Windows от Windows 3.1, который предшествуетWM_PAINT
чтобы дать у вашего приложения есть шанс "стереть фон" вашего окна, прежде чем вы начнете рисовать окно... ага. Если вы позволите сообщениюWM_ERASEBKGND
войти вDefWindowProc()
, это приведет к тому, что все ваше окно будет окрашено в сплошной цвет, обычно белый, при каждой перерисовке, включая перерисовки, которые происходят при изменении размера живого окна. В результате получается мерзкое мерцание в полноэкранном режиме, но не тот тип джиттера/мерцания/прыжков, о котором мы говорим в этом вопросе. ПерехватWM_ERASEBKGND
эту проблему немедленно. -
Этот вопрос в первую очередь касается изменения размера в реальном времени путем перетаскивания границ окна с помощью мыши. Тем не менее, многое из того, что написано здесь, также относится к уродливым артефактам, которые вы можете увидеть, когда приложение вручную изменяет размер окна с помощью
SetWindowPos()
. Они менее заметны, потому что они мерцают на экране только на одно мгновение, а не в течение длительного периода перетаскивания. -
Этот вопрос не о том, как сделать так, чтобы ваш код рисования для конкретного приложения работал быстрее, хотя во многих случаях это может быть решением проблемы с изменяемым размером. Если вашему приложению действительно требуется огромное количество времени для повторного отображения его содержимого при изменении размера живого окна, рассмотрите возможность оптимизации кода рисования в целом или, по крайней мере, переключитесь на более быстрый и менее качественный режим рисования во время изменения размера, перехватывая сообщения
WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE
для обнаружения размер. -
Если вашему приложению вообще не удается изменить размер во время изменения размера приложения (например, оно "зависает" во время изменения размера, особенно если это OpenGL, использующий GLFW или другую библиотеку), посмотрите эти другие вопросы, в которых объясняется отвратительный вложенный/модальный цикл событий Microsoft внутри
WM_SYSCOMMAND
во время перетаскивания.: вот особенно этот хороший ответ, здесь, здесь, здесь и здесь.
НАЧАЛЬНЫЕ ОТВЕТЫ
Для начала мы предоставили два начальных ответа, которые вы должны прочитать в следующем порядке:
-
ЧАСТЬ 2: выявление и устранение проблем с изменением размера Windows
- 2a: изменение размера проблем из
SetWindowPos()
BitBlt
и фоновой заливки - 2b: Проблемы с изменением размера при заполнении композиции DWM
- 2с: как диагностировать вашу проблему
- 2a: изменение размера проблем из
а также список исходного материала, который может помочь другим понять:
Мы надеемся, что люди дадут больше ответов с творческими способами избежать проблем, описанных в 2a и особенно 2b.