В WTL есть класс CCustomDraw<>(описан в файле atlctls.h ), который помогает вам перехватывать уведомление NM_CUSTOMDRAWи распаковывать его параметры. Он очень похож на класс COwnerDraw<>, который мы рассмотрели выше. Его реализация выглядит так.
template class CCustomDraw {
public:
// Message map and handlers
BEGIN_MSG_MAP(CCustomDraw)
NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
ALT_MSG_MAP(1)
REFLECTED_NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
END_MSG_MAP()
// message handler
LRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) {
T* pT = static_cast(this);
pT->SetMsgHandled(TRUE);
LPNMCUSTOMDRAW lpNMCustomDraw = (LPNMCUSTOMDRAW)pnmh;
DWORD dwRet = 0;
switch(lpNMCustomDraw->dwDrawStage) {
case CDDS_PREPAINT:
dwRet = pT->OnPrePaint(idCtrl, lpNMCustomDraw);
break;
case CDDS_POSTPAINT:
dwRet = pT->OnPostPaint(idCtrl, lpNMCustomDraw);
break;
// Остальные фазы отрисовки
// ...
default:
pT->SetMsgHandled(FALSE);
break;
}
bHandled = pT->IsMsgHandled();
return dwRet;
}
// Overrideables
DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) {
return CDRF_DODEFAULT;
}
DWORD OnPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/) {
return CDRF_DODEFAULT;
}
// Остальные функции.
// ...
Как видим, в классе CCustomDraw<>также предусмотрено две карты сообщений – для родительского окна и для самого контрола, если он получает отражённые уведомления. Обработчик OnCustomDrawраспаковывает параметры уведомления NM_CUSTOMDRAWи определяет фазу рисования. Каждой фазе соответствует своя функция, которая и вызывается из OnCustomDraw. Вы можете переопределить любую из этих функций в производном классе и включить в неё нужный вам код (реализации из класса CCustomDraw<>не выполняют никой полезной работы). Список фаз рисования и соответствующих им функций приведён в таблице 10.
Фаза |
Прототип функции |
CDDS_PREPAINTD |
WORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_POSTPAINTD |
WORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_PREERASAED |
WORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_POSTERASED |
WORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_ITEMPREPAINTD |
WORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_ITEMPOSTPAINTD |
WORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_ITEMPREERASED |
WORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
CDDS_ITEMPOSTERASE |
DWORD OnItemPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) |
Вот небольшой пример использования класса CCustomDraw<>. Для разнообразия я поручил обработку сообщения NM_CUSTOMDRAWсамому контролу. Подразумевается, что родительское окно переправляет ему уведомления, используя механизм отражения.
class CCustomDrawListView : public CWindowImpl, public CCustomDraw {
public:
BEGIN_MSG_MAP(CCustomDrawListView)
// Направляем сообщения в карту №1 класса CCustomDraw!
CHAIN_MSG_MAP_ALT(CCustomDraw, 1)
END_MSG_MAP()
DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) {
// Запрашиваем уведомления NM_CUSTOMDRAW для каждого элемента списка.
return CDRF_NOTIFYITEMDRAW;
}
DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw) {
// Нам нужны поля, специфичные для ListView.
LPNMLVCUSTOMDRAW pLVCD = (LPNMLVCUSTOMDRAW)lpNMCustomDraw;
if ((lpNMCustomDraw->dwItemSpec & 0x01) != 0) {
// Для нечётных элементов: рисуем белым по чёрному.
pLVCD->clrText = RGB(255,255,255);
pLVCD->clrTextBk = RGB(0,0,0);
} else {
// Для чётных элементов: рисуем красным по серому.
pLVCD->clrText = RGB(255,0,0);
pLVCD->clrTextBk = RGB(200,200,200);
}
return CDRF_NEWFONT;
}
};
От теории к практике
Мы изучили уже целую кучу новых классов, и теперь самое время посмотреть, как они применяются на практике. В этом разделе мы изучим целый ряд демонстрационных программ, иллюстрирующих различные аспекты программирования диалогов и контролов с использованием библиотеки WTL.
WTLErrLook: приложение на базе модального диалога
Демонстрационный проект WTLErrLook
WTLErrLook
Приложение WTLErrLook – это упрощённый вариант программы Error Lookup, которая входит в Visual Studio 6. Главное окно программы выполнено в виде модельного диалога. Обмен данными с полями ввода осуществляется с помощью DDX_TEXT.
WTLSndVol: приложение на базе немодального диалога
Демонстрационный проект WTLSndVol
WTLSndVol
WTLSndVol – это упрощённая версия регулятора громкости ( sndvol32.exe ), который входит в комплект Windows. При запуске программы она не показывает главное окно (которое выполнено в виде немодального дмалога), а размещает иконку в системном трее ( Shell_NotifyIcon). Чтобы она отличалась от иконки стандартного регулятора, я сделал её зелёной. Щелчок по иконке приводит к появлению окна регулятора. Для изменения громкости используется класс CSimpleMixer. Рассматривать его устройство мы не будем, так как это тема для отдельной статьи. Чтобы закрыть WTLSndVol, щёлкните правой кнопкой на иконке в трее и выберите из меню команду Exit.
Читать дальше