Пользовательская кнопка рисования с использованием uxtheme.dll
Я реализовал свою пользовательскую кнопку, унаследованную от CButton и нарисовав ее, используя uxtheme.dll (DrawThemeBackground с BP_PUSHBUTTON).
Все работает нормально, но у меня есть два статуса (Нормальный и Отжатый), статус Горячего одинаковый. Это означает, что когда пользователь наводит курсор на кнопку, она рисуется одинаково, независимо от состояния кнопки (нажата или нет).
Это немного сбивает с толку пользователя, и я хотел бы изменить способ отображения кнопки в состоянии "Нажатие и горячее". Кто-нибудь знает способ?
Я также думал о настройке всего чертежа, но кнопки используют градиенты, границы, тени и т. Д. Поэтому нелегко добиться того же внешнего вида и ощущения, рисуя все самостоятельно. Есть ли способ найти исходный код DLL или знать, как это сделать?
Заранее спасибо.
Хавьер
Примечание: я думаю, что смог бы достичь того, что хочу, используя CMFCButton и переопределив метод OnDraw. Позвольте элементу управления нарисовать кнопку на OnDrawBorder и затем нарисовать внутреннюю кнопку самостоятельно. Но мне нужно знать, как элемент управления рисует внутреннюю кнопку при нажатии. Это градиент, и я не могу догадаться, как это делается. У кого-нибудь есть подсказка?
3 ответа
Я наконец понял, как добиться того, что я хочу сделать. Это действительно легко.
Я использую два вызова DrawThemeBackground. Первый с PBS_PRESSED, а второй с состоянием PBS_HOT. Затем я делаю ExcludeClipRect, чтобы избежать рисования по центру кнопки.
Что-то вроде этого:
DrawThemeBackground( hTheme,
pCustomDraw->hdc,
BP_PUSHBUTTON,
PBS_PRESSED,
&pCustomDraw->rc,
NULL);
CDC *pDC = CDC::FromHandle(pCustomDraw->hdc);
CRect rectClient;
GetClientRect(rectClient);
CRect rectInternal = rectClient;
rectInternal.DeflateRect(4,4);
pDC->SelectClipRgn(NULL);
pDC->ExcludeClipRect(&rectInternal);
DrawThemeBackground( hTheme,
pCustomDraw->hdc,
BP_PUSHBUTTON,
PBS_HOT,
&pCustomDraw->rc,
NULL);
pDC->SelectClipRgn(NULL);
Конечно, это не весь код, но я думаю, что этого достаточно, чтобы высказать свою точку зрения.
Благодарю.
В ответ на ваш второй вопрос, если вы производите от CMFCButton
вместо CButton
Вы можете переопределить OnDraw()
или же OnDrawText()
вместо обычного DrawItem()
, Таким образом, фон кнопки по умолчанию будет нарисован, а затем ваш код рисования будет выполнен.
Единственный способ, которым я знаю, чтобы по-настоящему справиться с этим, - это использовать "пользовательскую ничью", а не "ничью владельца". Настраиваемое рисование появилось в Windows 2000, но используется только кнопочными элементами управления с comctrl32 6.0 (начиная с Windows XP и далее), не очень четко задокументировано и не является чем-то, что MFC старается поддерживать.
В любом случае, хорошая особенность пользовательского рисования заключается в том, что он позволяет вам подключаться к различным точкам процесса рисования, в отличие от рисования владельцем, что заставляет вас иметь дело со всем этим. Посмотрите в MSDN сообщение-уведомление NM_CUSTOMDRAW.
Для другой части вашей проблемы, обнаружение "горячего" состояния, самый простой способ сделать это - использовать сообщения WM_MOUSEMOVE и функцию TrackMouseEvent(), чтобы отслеживать, находится ли мышь над вашей кнопкой.
К сожалению, это немного расплывчатый ответ: объем кода, который вам необходим для демонстрации кнопки, использующей пользовательское рисование, слишком велик для ввода в эти поля для ответов! У меня есть проект, который демонстрирует такие приемы, используя настраиваемую кнопку рисования (возврат к рисованию владельца в старых версиях Windows), которая добавляет небольшую стрелку к кнопке. Вы можете взглянуть на исходный код, получив
Откройте его и посмотрите на класс "DropArrowButton". Важным битом является обработчик OnCustomDraw() и его вспомогательная функция DrawControl(): они вызываются на различных этапах рисования кнопок и используют UxTheme для соответствующего рисования элемента управления.