Категории
Самые читаемые
RUSBOOK.SU » Компьютеры и Интернет » Программирование » Программирование на Visual C++. Архив рассылки - Алекс Jenter

Программирование на Visual C++. Архив рассылки - Алекс Jenter

Читать онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 99 100 101 102 103 104 105 106 107 ... 156
Перейти на страницу:

В основе механизма owner draw лежат сообщения WM_DRAWITEM, WM_MEASUREITEM, WM_COMPAREITEM и WM_DELETEITEM. Так, в обработчике WM_DRAWITEM выполняется собственно отрисовка контрола, а в обработчике WM_MEASUREITEM – задание размеров отдельных элементов, содержащихся в контроле (пунктов меню, элементов списка и т.п.). WTL содержит небольшой класс COwnerDraw<>, который помогает вам обрабатывать все эти сообщения (описан в файле atlframe.h). Чтобы им воспользоваться, включите его в список базовых классов окна, которое будет заниматься отрисовкой контролов.

Посмотрим, какие элементы входят в класс COwnerDraw<>. В первую очередь это карта сообщений. Точнее, две карты (вы ещё не забыли, что в WTL окно может иметь несколько карт сообщений?).

BEGIN_MSG_MAP(COwnerDraw<T>)

 MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)

 MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)

 MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)

 MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)

ALT_MSG_MAP(1)

 MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)

 MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)

 MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)

 MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)

END_MSG_MAP()

По умолчанию используется карта с номером 0. Она обрабатывает сообщения в родительском окне. Карту с номером 1 можно использовать для перехвата отражённых сообщений, связанных с механизмом owner draw, в самом контроле.

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

void DrawItem(LPDRAWITEMSTRUCT);

void MeasureItem(LPMEASUREITEMSTRUCT);

int CompareItem(LPCOMPAREITEMSTRUCT);

void DeleteItem(LPDELETEITEMSTRUCT);

Именно эти функции вы можете переопределить в производном классе, чтобы реализовать отрисовку контрола. Это удобнее, чем вручную перехватывать сообщения и вспоминать, каким образом в их параметрах запакована информация. Обратите внимание, что класс COwnerDraw<> содержит стандартную реализацию этих функций. Функции DrawItem, CompareItem и DeleteItem ничего полезного не делают, зато функция MeasureItem возвращает размер пункта меню в зависимости от настроек системы и размер элемента в списке в зависимости от размера стандартного системного фонта, который используется в диалогах и меню. Если такое поведение вас не устраивает, измените его на любое другое.

Рассмотрим пример использования класса COwnerDraw<> для рисования нестандартной кнопки.

class CButtonDemoDlg : public CSimpleDialog<IDD_BUTTON_DIALOG>, public COwnerDraw<CButtonDemoDlg>, ... {

private:

 HICON m_hIcon1, m_hIcon2;

 ...

public:

 BEGIN_MSG_MAP(CButtonDemoDlg)

  ...

  CHAIN_MSG_MAP(COwnerDraw<CButtonDemoDlg>)

 END_MSG_MAP()

 void DrawItem(LPDRAWITEMSTRUCT pDIS) {

  if ((pDIS->itemState & ODS_SELECTED) != 0) {

   // Кнопка нажата

   DrawIcon(pDIS->hDC, 0, 0, m_hIcon2);

  } else {

   // Кнопка отпущена

   DrawIcon(pDIS->hDC, 0, 0, m_hIcon1);

  }

 }

};

Класс CCustomDraw<>: пользовательское рисование в стиле WTL

Механизм пользовательского рисования (custom draw) иногда путают с owner draw. Он предназначен для той же цели – изменить внешний вид контролов. Однако он появился несколько позже (вместе с набором общих контролов из библиотеки comctl32.dll) и используется для более новых контролов (таких, как ListView и TreeView).

Пользовательское рисование работает следующим образом. Когда контрол перерисовывается, он посылает родительскому окну одно или несколько уведомлений NM_CUSTOMDRAW, упакованных в сообщение WM_NOTIFY. Каждое уведомление соответствует некоторой фазе перерисовки (до/после рисования контрола целиком или отдельного элемента и т. д.). Фазу можно определить по полю dwDrawStage структуры NMCUSTOMDRAW, указатель на которую передаётся вместе с уведомлением. В зависимости от фазы родительское окно может выполнить некоторые действия (например, изменить цвет или фонт отдельного элемента списка). Подробности можно найти в MSDN (см. статью "Customizing a Control's Appearance Using Custom Draw").

В WTL есть класс CCustomDraw<> (описан в файле atlctls.h), который помогает вам перехватывать уведомление NM_CUSTOMDRAW и распаковывать его параметры. Он очень похож на класс COwnerDraw<>, который мы рассмотрели выше. Его реализация выглядит так.

template <class T> class CCustomDraw {

public:

 // Message map and handlers

 BEGIN_MSG_MAP(CCustomDraw<T>)

  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<T*>(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<CCustomDrawListView, CListViewCtrl>, public CCustomDraw<CCustomDrawListView> {

public:

 BEGIN_MSG_MAP(CCustomDrawListView)

  // Направляем сообщения в карту №1 класса CCustomDraw!

  CHAIN_MSG_MAP_ALT(CCustomDraw<CCustomDrawListView>, 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

1 ... 99 100 101 102 103 104 105 106 107 ... 156
Перейти на страницу:
На этой странице вы можете бесплатно скачать Программирование на Visual C++. Архив рассылки - Алекс Jenter торрент бесплатно.
Комментарии
Открыть боковую панель
Комментарии
Сергій
Сергій 25.01.2024 - 17:17
"Убийство миссис Спэнлоу" от Агаты Кристи – это великолепный детектив, который завораживает с первой страницы и держит в напряжении до последнего момента. Кристи, как всегда, мастерски строит