Иллюстрированный самоучитель по Visual Studio .NET

Реакция окна на уведомляющие сообщения

Наш анализатор кодов ошибок по сути является браузером (инструментом для просмотра) файла WinError.h с особой структурой. Мы хотим дать пользователю возможность выбрать один из двух вариантов просмотра:

  • последовательный, с помощью счетчика Spin Control или ползунка slider Control,
  • поиск по значению, задаваемому в поле элемента IDC_FIND типа Edit Control.

Отметим, что счетчик (spinner) раньше назывался Up-Down control, а ползунок (slider) – Trackbar Control. Знание этого факта помогает понять, почему сообщения (стили) счетчика содержат аббревиатуру UD, а ползунка – тв. Все три используемых элемента (счетчик, ползунок и поле редактирования IDC_FIND) должны быть синхронизированы так, чтобы изменение позиции счетчика отслеживалось ползунком, и наоборот. Ввод пользователем кола ошибки в поле редактирования IDC_FIND должен вызывать мгновенную реакцию приложения и отражаться в показаниях счетчика и ползунка, но только в случае, если требуемый код найден.

Примечание
Напомним, что элементы управления на форме диалога являются дочерними окнами (child-windows) по отношению к окну диалога. И этот тип отношений parent-child (родство) не является отражением наследования в смысле ООП. Он существовал до того, как была создана MFC. Важно то, что дочерние окна генерируют уведомляющие сообщения об изменениях, происходящих в них а система направляет их в оконную процедуру родительского окна
.

Здесь мы должны использовать способность родительских (parent) окон реагировать на уведомляющие сообщения, исходящие от их "детей". Сообщения такого типа можно условно поделить на старые (Windows 3.x) и новые (Win32). Старых много, так как каждый тип элементов имеет несколько уведомляющих сообщений. Посмотрите Help › Index, задав индекс EN_ (Edit Notifications), и вы увидите, что элемент типа окна редактирования уведомляет своего родителя о таких событиях, как: EN_CHANGE (изменился текст), EN_KILLFOCUS (окно теряет фокус ввода), EN_UPDATE (окно готово к перерисовке) и множестве других.

Наряду со старыми существует одно новое универсальное событие WM_NOTIFY. Теперь при создании новых элементов управления не надо плодить сообщения типа WM_*, которых и так очень много. Все могут обойтись одним – WM_NOTIFY. Его универсальность состоит в том, что все новые типы элементов умеют генерировать это одно сообщение. В дополнение они сопровождают его указателем на структуру NMHDR (Notify Message Header), которая способна "привести" за собой различные другие структуры. Весь трюк состоит в том, что, получив это сообщение вместе с указателем NMHDR* pNMHDR, который на самом деле показывает на другую, более сложную структуру, класс родительского окна знает тип элемента и, следовательно, знает, к какому типу надо привести этот указатель. Например, при изменении показаний счетчика система посылает родительскому окну сообщение WM_NOTIFY, в IParam которого помещен указатель типа NMHDR*:

typedef struct tagNMHDR
{
//=== Описатель окна (счетчика), пославшего сообщение
HWND hwndFrom;
//=== Идентификатор окна (счетчика)
UINT idFrora;
//=== Код сообщения
OINT code;
}
NMHDR;

Но на самом деле указатель pNMHDR содержит адрес другой структуры:

typedef struct _NM_UPDOWN
{
//====== Вложенная структура
NMHDR hdr;
//====== Текущая позиция счетчика
int iPos;
//====== Предлагаемое увеличение показаний
int iDelta;
}
NMUPDOWN, FAR *LPNMUPDOWN;

Так как структура hdr типа NMHDR стоит первой в списке полей NMUPDOWN, то все законно – присланный в iParam указатель действительно показывает на NMHDR, но в составе NMUPDOWN. Эту ситуацию легче запомнить, а может быть, и понять, если использовать аналогию. Способ запоминания замысловатых выкладок с помощью глупых аналогий известен давно. Мне приходит в голову такая: звонят в дверь (WM_NOTIFY), вы подходите к ней и видите, что пришел знакомый мальчик (NMHDR) с сообщением, но, открыв дверь, вы обнаруживаете, что за ним стоит широкоплечий мужчина (NMUPDOWN). Теперь пора ввести в класс CLookDlg реакции на уведомляющие сообщения:

  1. Откройте шаблон диалога и установите курсор мыши на счетчике (IDC_SPIN).
  2. В окне Properties нажмите кнопку с подсказкой ControlEvents.
  3. В появившемся списке уведомляющих сообщений, которые генерирует счетчик, выберите UDN_DELTAPOS, а в ячейке справа укажите действие – <Add>.
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.