Двойная буферизация winAPI

Итак, в моем приложении есть несколько WinAPI и несколько настраиваемых элементов управления. Ура...

Теперь, как правило, они просто незаметно перерисовывают себя для анимации, изменения состояния и т. Д., И все это работает нормально.

Но у меня есть метод класса Window под названием fix (). Это вызывается всякий раз, когда необходимо обновить все окно. Он изменяет размеры элементов управления и делает окно недействительным.

Когда это происходит, рисуется фон, затем вкладка, а затем все остальные сверху. Это вызывает очень раздражающее мигание, особенно при изменении размера окна (из-за постоянных вызовов fix ()).

Что я пробовал:

  • WS_EX_COMPOSITED. Это только двойная буферизация отдельных элементов управления. Это улучшение, но мерцание неизбежно остается.
  • Отключение отрисовки фона. Вряд ли решает проблему, а на самом деле только усугубляет ситуацию.

Итак: мне нужна техника / метод / что угодно, чтобы позволить мне полностью выполнить двойную буферизацию окна. Я подумал, что обработка сообщения WM_PAINT сама по себе может быть решением, но я не знал, с чего начать. У меня ужасное предчувствие, что это даже невозможно ...

Пожалуйста, помогите, это критическая проблема. Я буду очень рад, когда эта дурацкая проблема будет исправлена.


person Alexander Rafferty    schedule 11.10.2010    source источник


Ответы (5)


Именно в это время можно было понять всю глубину пренебрежения Microsoft к собственным разработчикам. Фактически, можно начать питать параноидальные заблуждения, что Microsoft намеренно нарушила естественную отрисовку, чтобы заставить разработчиков перейти на WPF.

Сначала рассмотрим WS_EX_COMPOSITED. WS_EX_COMPOSITED кажется горчичным: - в нем говорится, что он заставляет боттон выполнять верхний порядок рисования для дочерних элементов управления, и в основном сообщения WM_PAINT обрабатываются в пакете. В нем говорится, что он был добавлен в Windows 2000 (5.0) и несколькими строками ниже, что он не работает с включенной композицией рабочего стола. т.е. он перестает работать с Windows Vista (6.0), если аэростекло не выключено, и кто это будет делать?

Затем есть два возможных "улова", чтобы попытаться заставить работать рисование без мерцания:

  • Во-первых, вам нужно имитировать количество перерисовки. WS_EX_CLIPCHILDREN | WS_EX_CLIPSIBLINGS необходимы для того, чтобы любая конкретная область окна была закрашена только один раз. BeginDeferWindowPos также необходимо для пакетирования операций изменения размера, чтобы гарантировать, что переходные состояния - когда одно окно перекрывает другое - не происходят (то есть, когда размер окна A был изменен, а окна B - нет).

Конечно, когда вы пытаетесь нарисовать диалоговое окно со скином, или использовать групповые блоки, или элементы управления вкладками, или любое количество других ограничений, WS_EX_CLIPSIBLINGS просто не подходит.

  • WM_SETREDRAW - волшебное послание. API для доступа к этой функции отсутствует: WM_SETREDRAW напрямую обрабатывается DefWindowProc, чтобы пометить окно как скрытое на время. После отправки WM_SETREDRAW, FALSE вызовы GetDC / GetDCEx / GetWindowDC и т. Д. С использованием дескриптора родительского окна (и всех его дочерних элементов) вернут DC, который не будет отображаться на экране. Это дает вам возможность делать всевозможные вещи с дочерними окнами, когда вы закончите, отправьте WM_SETREDRAW,TRUE (и затем перекрасите окно вручную). Все дочерние окна, конечно, будут рисовать в свое время и после того, как родительское окно выполнит стирание фона, поэтому WM_SETREDRAW не является панацеей.

После взлома WS_EX_COMPOSITED, WinForms и WPF .NET переписали элементы управления с нуля, чтобы не использовать собственные элементы управления, чтобы они могли запекать там буферизованную отрисовку. И альфа-поддержка тоже.

person Chris Becke    schedule 11.10.2010
comment
In .NET's WinForms and WPF re-wrote the controls from the ground up to not use the native controls, so they could bake in buffered painting there. Ой, такая возможность даже не приходила мне в голову! Полагаю, это было примерно во времена .NET 2.0. Я всегда задавался вопросом, почему именно этот флаг был так сломан, теперь кучу вещей внезапно обретают смысл в моей голове. - person dialer; 11.01.2021

Хм. Прежде чем люди поспешат закрыть эту тему как дубликат, я лучше упомяну, что ваша проблема не в двойной буферизации как таковой, а в мерцании при изменении положения элементов управления, для которого «ручная» двойная буферизация является лишь одним из нескольких решений.

Может быть, например, BeginDeferWindowPos и друзья могут исправить мерцание.

Отказ от ответственности: когда-то я знал почти все детали Win16 API, но прошло несколько лет с тех пор, как я занимался программированием на уровне API.

Ура & hth.,

- Альф

person Cheers and hth. - Alf    schedule 11.10.2010
comment
@Alexander, win32 - это просто win16s менее злой близнец. - person Prof. Falken; 11.10.2010

Без кода я с трудом представляю, какова реальная ситуация ... Но вы можете попробовать обработать WM_ERASEBKGND, вернуть TRUE всякий раз, когда вы их получили, чтобы пропустить стирание.

person YeenFei    schedule 11.10.2010
comment
Я пробовал это. Это просто создало большой беспорядок. Знаете, что происходит, если не стирать фон? - person Alexander Rafferty; 11.10.2010
comment
фон явно не стирается: ›. Что ж, если ваша картина покрывает всю площадь, неважно, стерта она или нет. - person YeenFei; 12.10.2010

Обработайте WM_ERASEBKGND для вашего окна и в реализации исключите прямоугольники каждого из ваших дочерних элементов управления, затем заполните оставшийся фон соответствующим образом и сообщите платформе, что вы нарисовали фоновый URL, вернув true. Сделайте то же самое для всех других дочерних элементов управления, которые, в свою очередь, содержат другие элементы управления, такие как элемент управления вкладками в вашем случае.

Пример использования MFC см. здесь.

person Hemant    schedule 11.10.2010

В .net есть ControlStyle.AllPaintingInWmPaint, который должен быть установлен в каждом окне контейнера (то есть в главном окне и на вкладках). Мне не удалось найти аналог для winapi. Но что это делает, так это перемещение рисования фона с WM_ERASEBKGND на WM_PAINT. он также вычитает область дочернего элемента управления из родительской, чтобы избежать мерцания. Возможно, вам нужно сделать это вручную.

Я удивлен, что WS_EX_COMPOSITED не помогает. Это должно сработать, если вы установите это в окне верхнего уровня и не вызываете RedrawWindow для субэлементов управления.

Также обязательно протестируйте с DWM / Aero и без него. Вы можете получить разные результаты.

person Community    schedule 11.10.2010
comment
P.S. Я поместил образец кода C # в свой предыдущий ответ. Вы можете изменить его, чтобы продемонстрировать наблюдаемый эффект. stackoverflow.com/questions/3735121/ - person ; 11.10.2010
comment
@ Александр: Это то, что вам нужно сделать. Вам также необходимо вернуть истину на WM_ERASEBKGND. Также может потребоваться исключение прямоугольников дочернего окна из рисования (возможно, добавление WS_CLIPCHILDREN поможет). - person ; 11.10.2010
comment
Вы мне говорите, что двойная буферизация окна невозможна? - person Alexander Rafferty; 11.10.2010
comment
@Alexander: Обычно каждое окно раскрашивает само себя. Каждый элемент управления (включая кнопки) имеет собственный дескриптор окна. Если вы хотите удвоить буфер, вы можете удвоить буфер для каждого элемента управления по отдельности (а не для всей формы). Однако при использовании WS_EX_COMPOSITED окно с этим стилем и все дочерние элементы управления визуализируются за пределами экрана за один проход, а затем копируются как один прямоугольник. Обратной стороной является медленная работа, требует XP или более поздней версии, а также иногда появляются артефакты (в частности, черные области при изменении размера с включенным Aero / DWM). - person ; 11.10.2010
comment
Это то, что мне нужно, окно с полностью двойной буферизацией, но WS_EX_COMPOSITED не работает! - person Alexander Rafferty; 11.10.2010
comment
@ Александр: Пожалуйста, не жалуйтесь мне. Вы должны знать, что когда вы программируете win32 на C ++, это требует ручного труда. - person ; 11.10.2010