Учебник по Visual C++ .Net

         

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


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>.

    Перейдите в окно LookDlg.cpp и найдите в карте сообщений новый элемент

    ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN, OnDeltaposSpin)

    который был вставлен инструментом ClassWizard и который означает, что если окну диалога, управляемому классом CLookDlg, придет сообщение UDN_DELTAPOS (Up-Down Notification) от элемента с идентификатором IDC_SPIN, то управление будет передано функции-обработчику OnDeltaposSpin. Теперь в конце файла найдите эту функцию:

    void CLookDlg::OnDeltaposSpin(NMHDR *pNMHDR, LRESOLT *pResult)

    {

    NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;

    // TODO: Add your control notification handler code here


    *pResult = 0; }

    Вот здесь происходит то, о чем было сказано выше. Указатель PNMHDR приводится к типу указателя на более сложную структуру NM_UPDOWN. Это нужно для того, чтобы достать из нее необходимую информацию. Теперь с помощью указателя pNMUpDown мы можем добыть требуемое приращение показаний счетчика (pNMUpDown->iDelta). Вместо комментария // TODO: вставьте следующий фрагмент кода:

    //====== Вычисляем желаемую позицию

    int nPos = m_Spin.GetPos() + pNMUpDown->iDelta;

    //====== Если она вне допустимых пределов, то уходим

    if (nPos < 0 m_nltems <= nPos) return;

    //====== Корректируем позицию ползунка

    m_Slider.SetPos(nPos);

    //====== Расшифровываем код ошибки

    Getlnfo(nPos);

    //====== Вызываем обмен данными с элементами окна диалога

    UpdateData(FALSE);

    Здесь уместно напомнить, что Studio.Net 7.0, как и Visual Studio 6, позволяет форматировать введенный текст так, как это принято в сообществе разработчиков. Выделите весь код функции и дайте команду Edit > Advanced > Format Selection или Alt+F8.

    В коде мы используем данные (m_Spin, m_nltems, m_Slider) и метод (Getlnfо), которых еще нет в классе, но вы, наверное, имеете некоторый опыт программирования и знаете, что разработка часто идет в обратном порядке. Введем эти элементы в состав класса позже, а сейчас дадим оценку того, что только что сделали. С помощью ClassWizard мы ввели в класс главного окна обработку уведомляющего сообщения UDN_DELTAPOS, работающего по схеме WM_NOTIFY. Теперь введем обработку сообщения EN_CHANGE, поступающего от окна редактирования IDC_FIND каждый раз, когда в нем происходят изменения. Это сообщение работает по старой схеме и не влечет за собой необходимости преобразовывать указатели на структуры данных.

  • Вновь откройте шаблон диалога и установите курсор мыши в окно IDC_FIND.


  • В окне Properties нажмите кнопку с подсказкой ControlEvents.


  • В появившемся списке уведомляющих сообщений, которые генерирует окно редактирования, выберите сообщение EN_CHANGE и его реализацию <Add>.



    Проверьте результаты работы ClassWizard. Они должны быть видны в трех разных местах вашего приложения. В файле LookDlg.h должен появиться прототип функции обработки

    void OnChangeFind (void) ;

    в файле LookDlg.cpp должен появиться новый элемент карты сообщений

    ON_EN_CHANGE(IDC_FIND, OnChangeFind)

    и заготовка тела функции обработки, в которую мы должны внести свою функциональность:

    void

    CLookDlg::OnChangeFind(void)

    {

    // TODO: Если это RICHEDIT control, то он не пошлет

    // уведомления пока вы не дадите своей версии функции

    // CDialog::OnInitDialog() и не сделаете вызов функции

    // CRichEditCtrl().SetEventMask() с флагом ENM_CHANGE,

    // включенным с помощью операции побитового ИЛИ.

    // TODO: Здесь вставьте код обработки уведомления.

    }

    В комментариях CLassWizard предупреждает нас о том, что с элементом типа Rich Edit control надо работать по особым правилам. К нам это не относится, поэтому уберите комментарии и вставьте вместо них такой код:

    CString s;

    //==== Выбираем код ошибки, введенный пользователем

    GetDlgltemText(IDC_FIND, s) ;

    //==== Преобразуем к типу string, с которым мы работаем

    string find = s;

    //==== Ищем код в контейнере

    m_Vector

    for (int n=0;

    n < m_nltems is find != m_Vector[n].Code;n++);

    if (n < m_nltems) // Если нашли,

    {

    Getlnfo(n); // то расшифровываем этот код

    m_Slider.SetPos(n); // и синхронизируем ползунок

    UpdateData(FALSE); // Высвечиваем данные в окнах

    }

    Переменная s типа CString понадобилась для того, чтобы воспользоваться функцией GetDlgltemText, которая вычитывает содержимое окна редактирования. Приходится делать преобразование к типу string, так как мы хотим работать со стандартными строками (string) библиотеки STL.

    Возвращаясь к элементам управления в окне диалога, отметим, что ползунок тоже посылает уведомляющие сообщения по схеме WM_NOTIFY. Их всего три и вы можете их увидеть в окне Properties после нажатия кнопки ControlEvents, если предварительно установите фокус на элементе IDC_SLIDER. Одно из них — NM_RELEASEDCAPTURE — подходит нам, так как посылается в тот момент, когда пользователь отпускает движок после его установки мышью в новое положение. Но мы не будем вводить реакцию на это уведомление, так как есть другое (старое) сообщение Windows — WM_HSCROLL (или WM_VSCROLL при вертикальном расположении ползунка), которое работает более эффективно. Дело в том, что ползунок управляется не только мышью. Если он обладает фокусом, то реагирует на все клавиши перемещения курсора (4 стрелки, Page Up, Page Down, Home, End). Это очень удобно, так как позволяет гибко управлять темпом перемещения по многочисленным кодам ошибок. Введите реакцию оконного класса на сообщение WM_HSCROLL.



  • Вновь откройте шаблон диалога и установите фокус в его окне. Проследите, чтобы он не был ни в одном из элементов управления.

  • В окне Properties нажмите кнопку Messages, найдите в списке сообщение WM_ HSCROLL и укажите действие <Add>.

    Отыщите изменения в классе CLookDlg. Их должно быть три. Отметим, что когда ClassWizard делает вставки в карту сообщений, то он пользуется своим опознавательным знаком — знаком комментария вида //}} AFX_MSG_MAP. Напомним, что в

    Visual Studio 6 эти знаки существовали парами, а вставки между элементами пар отличались цветом. Теперь все упростилось. Введите код в тело функции-обработчика так, чтобы она была:

    void CLookDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

    {

    //====== Расшифровываем новый код

    Getlnfo(m_Slider.GetPos());

    //====== Помещаем данные в поля диалога

    UpdateData(FALSE); }

    Сообщение WM_HSCROLL посылается в те моменты, когда ползунок изменяет свое положение как с помощью мыши, так и с помощью клавиш. В обработчике мы выявляем новую позицию ползунка, ищем и расшифровываем код, соответствующий этой позиции. Обратите внимание на то, что мы не пытаемся синхронизировать счетчик. Когда приложение будет работать, вы увидите, что последний, тем не менее, отслеживает изменения позиции ползунка. Попробуйте самостоятельно найти объяснение этому факту. Ответ можно найти в MSDN по теме CSpinButtonCtrl, если обратить внимание на то, что счетчик может иметь (Buddy) двойника-приятеля, в качестве которого мы уже выбрали окно редактирования IDC_CURRENT.


    Содержание раздела