Реакция окна на уведомляющие сообщения
Наш анализатор кодов ошибок по сути является браузером (инструментом для просмотра) файла 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 реакции на уведомляющие сообщения:
- Откройте шаблон диалога и установите курсор мыши на счетчике (IDC_SPIN).
- В окне Properties нажмите кнопку с подсказкой ControlEvents.
- В появившемся списке уведомляющих сообщений, которые генерирует счетчик, выберите UDN_DELTAPOS, а в ячейке справа укажите действие – <Add>.