Расширение (изменение размера) окна влево без мерцания
Скажем, у вас есть форма, которую вы можете развернуть влево, чтобы показать дополнительные элементы управления:
рухнул:
Expanded:
Самый простой способ добиться этого в Delphi - использовать alRight
в качестве основного якоря для всех элементов управления (вместо alLeft
), а затем просто отрегулируйте ширину и координату X формы. Либо вы можете установить Width
а также Left
свойства по отдельности, или вы можете использовать функцию, которая устанавливает их одновременно, как
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0)
Проблема заключается в том, что при развертывании или свертывании в всегда видимой части формы (в этом примере, кнопках) наблюдается довольно заметное мерцание. Попробуй сам!
Операционная система может изменить размер формы влево без какого-либо мерцания - просто возьмите левую грань формы с помощью мыши и перетащите мышь влево или вправо - но я не могу найти какой-либо функция в Windows API, которая предоставляет такой вид изменения размера.
Я пытался использовать несколько различных функций Windows API для изменения размера и расположения формы, пробовал их различные параметры (например, SWP_*
флаги), попробовал LockWindowUpdate
, WM_SETREDRAW
, TForm.DoubleBuffered
и т.д. безрезультатно. Я также рассмотрел возможность использования WM_SYSCOMMAND
SC_SIZE
подход.
Я еще не уверен, лежит ли проблема на уровне ОС или на уровне VCL.
Какие-либо предложения?
Изменить: я очень удивлен, увидев, что этот Q получил близкие голоса. Позвольте мне попытаться уточнить:
Создайте новое приложение VCL формы.
Добавьте несколько кнопок справа от основной формы и заметку слева. Задавать
Anchors
в[alTop, alRight]
на всех элементах управления. НаOnClick
Обработчик кнопок, добавьте следующий код:if FCollapsed then SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0) else SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0); FCollapsed := not FCollapsed;
где
FCollapsed
является частным логическим полем формы (инициализируетсяfalse
).Теперь нажимайте кнопки несколько раз. (Или сфокусируйтесь на клавиатуре и удерживайте клавишу Enter в течение нескольких секунд.) Вы, вероятно, заметите, что область с кнопками на мониторе не будет отображать идеальное неподвижное изображение, но будет мерцать. Кроме того, вы можете увидеть "призраки" кнопок слева от фактического столбца кнопок.
Я не могу запечатлеть это мерцание миллисекунды с помощью захвата экрана, поэтому вместо этого я использовал цифровую камеру для записи своего экрана:
https://privat.rejbrand.se/VCLFormExpandFlicker.mp4
В этом видеоролике видно, что столбец кнопок не является статичным изображением на экране; вместо этого, в течение нескольких миллисекунд каждый раз, когда форма изменяется, эта область является чем-то другим, чем должно быть. В равной степени очевидно, что слева находится столбец "призрачных" кнопок.
У меня вопрос, есть ли какой-нибудь достаточно простой способ избавиться от этих визуальных артефактов (которые, по крайней мере, для меня очень заметны, даже если вы разложите / свернете форму один раз).
На моем компьютере с операционной системой Windows 10/Delphi 10.1 форма идеально изменяет размеры, когда я перетаскиваю ее крайний левый край с помощью мыши: незатронутая клиентская область формы совершенно неподвижна на мониторе. Однако на домашнем ПК с Windows 7/Delphi 2009 я вижу, что когда я это делаю, происходит много перестановок.
1 ответ
Я могу дать некоторое представление о том, почему вы видите призрачные изображения другой половины вашего пользовательского интерфейса и, возможно, способ остановить это. Призрачное изображение указывает, что кто-то копирует пиксели вашей клиентской области (и копирует их в неправильное место, всегда влево в вашем окне), прежде чем вы сможете перерисовать их с правильными пикселями.
Вероятно, есть два разных перекрывающихся источника этих призрачных пикселей.
Первый уровень применяется ко всем ОС Windows и происходит от BitBlt
внутри SetWindowPos
, Вы можете избавиться от этого BitBlt
несколькими способами. Вы можете создать свою собственную реализацию WM_NCCALCSIZE
чтобы сказать Windows, чтобы ничего не стряхивать (или отбрасывать один пиксель поверх себя), или вы можете перехватить WM_WINDOWPOSCHANGING
(сначала передавая его на DefWindowProc
) и установить WINDOWPOS.flags |= SWP_NOCOPYBITS
, который отключает BitBlt
внутри внутреннего звонка SetWindowPos()
что Windows делает во время изменения размера окна. Это имеет тот же возможный эффект пропуска BitBlt
,
Тем не менее, Windows 8/10 Aero добавляет еще один, более хлопотный слой. Приложения теперь рисуют в закадровом буфере, который затем создается новым, злым оконным менеджером DWM.exe. И оказывается, что DWM.exe иногда делает свое дело BitBlt
введите операцию поверх той, что уже была сделана в устаревшем коде XP/Vista/7. А помешать DWM делать это намного сложнее; до сих пор я не видел каких-либо полных решений.
Для примера кода, который пробьет уровень XP/Vista/7 и по крайней мере улучшит производительность уровня 8/10, пожалуйста, смотрите:
Поскольку у вас есть несколько дочерних окон, ситуация даже немного сложнее. BitBlt
Операции типа, о которых я упоминал выше, выполняются для всего окна верхнего уровня в целом (они обрабатывают окно как один набор пикселей независимо от того, сколько окон находится под ним, и независимо от того, CLIPCHILDREN
). Но вам нужно, чтобы окна двигались атомарно, чтобы при следующем перерисовке все они были расположены правильно. Вы можете найти BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
полезно для этого (но заходите туда, только если вышеперечисленные трюки не работают).