Расширение (изменение размера) окна влево без мерцания

Скажем, у вас есть форма, которую вы можете развернуть влево, чтобы показать дополнительные элементы управления:

рухнул:

Свернутая форма

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_SYSCOMMANDSC_SIZE подход.

Я еще не уверен, лежит ли проблема на уровне ОС или на уровне VCL.

Какие-либо предложения?

Изменить: я очень удивлен, увидев, что этот Q получил близкие голоса. Позвольте мне попытаться уточнить:

  1. Создайте новое приложение VCL формы.

  2. Добавьте несколько кнопок справа от основной формы и заметку слева. Задавать 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).

  3. Теперь нажимайте кнопки несколько раз. (Или сфокусируйтесь на клавиатуре и удерживайте клавишу 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, пожалуйста, смотрите:

Как сгладить уродливый джиттер / мерцание / прыжок при изменении размеров окон, особенно перетаскивая левую / верхнюю границу (Win 7-10; bg, bitblt и DWM)?

Поскольку у вас есть несколько дочерних окон, ситуация даже немного сложнее. BitBlt Операции типа, о которых я упоминал выше, выполняются для всего окна верхнего уровня в целом (они обрабатывают окно как один набор пикселей независимо от того, сколько окон находится под ним, и независимо от того, CLIPCHILDREN). Но вам нужно, чтобы окна двигались атомарно, чтобы при следующем перерисовке все они были расположены правильно. Вы можете найти BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos полезно для этого (но заходите туда, только если вышеперечисленные трюки не работают).

Другие вопросы по тегам