Пользовательский элемент управления MFC GUI: как отображать обновления курсора в ответ на движения мыши? - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть пользовательский элемент управления Windows, подкласс из CButton (понятия не имею, почему он был выбран - это код 17-летней давности; нет подобия функциональности кнопок).

Его DrawItem( LPDRAWITEMSTRUCT pdis ) метод вызывается CButton::OnChildNotify в ответ на WM_DRAWITEM. Он визуализирует свою сцену с DC CDC::FromHandle( pdis->hDC ).

Метод события мыши OnMouseMove() вычисляет новую позицию курсора и вызывает RedrawWindow( NULL, NULL, RDW_INVALIDATE ). Курсор, который следует за мышью, должным образом появляется в новой позиции мыши. Работает нормально, но медленно. На самом деле, нужно перерисовывать только предыдущие и новые ячейки курсора (если таковые имеются), но графические обновления начинают отставать, поскольку вся сцена визуализируется много раз.

Я думал, что в моем методе OnMouseMove() вместо перерисовки всей сцены можно просто нарисовать соответствующие клетки. У него уже есть точные координаты X и Y ячеек и указатели на их данные. Я думал, что CPaintDC(this) даст DC, который позволил бы это, но это не рисует. (Также не падает, что является редкой радостью.)

Мое смутное воспоминание состоит в том, что «оптимальный» способ сделать это - сделать недействительными только области двух ячеек, а метод DrawItem() в конечном итоге скажет, что эти области были признаны недействительными, и вместо того, чтобы полностью перекрасить его, можно просто определите по координатам, какими ячейками они были (не простая операция, между прочим), и перекрасьте их, и это упростит не только эту проблему с курсором, но также обеспечит частичное раскрытие частично скрытого элемента управления, когда будут нарисованы только несколько ячеек. Но нехватка времени не позволяет, и варианты использования, кажется, не требуют, чтобы это было оптимизировано.

Итак, вопрос в следующем: Есть ли какой-нибудь хороший способ для OnMouseMove() немедленно перерисовать один элемент управления, и если да, то с помощью какого DC? (Например, я могу кэшировать DC, который я получил в DrawItem() через FromHandle()?

Прямо сейчас единственная идея, которая у меня есть, - это иметь объектный объект, указывающий на одну ячейку для перерисовки, вызывать RedrawWindow () с этим флагом RDW_UPDATENOW и иметь DrawItem (), если этот флаг установлен, делать именно это один предмет. Это приведет к тому, что DrawItem () получит DC, который предположительно будет работать так, как всегда. Похоже на настоящий хак, есть ли лучший способ?

1 Ответ

1 голос
/ 01 мая 2019

В приложении Windows обычно выполняется весь рендеринг в ответ на сообщение WM_PAINT (или WM_NCPAINT). Код, который должен вызвать перерисовку, помечает часть или всю клиентскую область окна как грязную, вызывая InvalidateRect (и друзей). Система оптимизирована для этого подхода, объединяя несколько запросов в одну область обновления, и впоследствии выдает сообщение WM_PAINT, когда нет более важной работы (например, обработка ввода).

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

Весь рендеринг проходит через абстракцию, называемую контекстом устройства (DC). При обработке сообщения WM_PAINT в приложении MFC подходящий DC может быть получен путем создания экземпляра CPaintDC. При рендеринге в другом месте вы не можете использовать CPaintDC, но вместо этого вам нужно использовать CClientDC (или CWindowDC, чтобы визуализировать также и не-клиентскую область) , Как правило, коду рендеринга не нужно знать, к какому типу DC он относится, и обычно его можно использовать без изменений.

...