Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
370
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

ON_NOTIFY_EX_RANGE (wNotifyCode, idFirst, idLast, memberFn)

Команды, определяемые пользователем, включаются в карту сообщений при помощи макроса

ON_MESSAGE (WM_NAMEMSG, OnNameMsg)

В качестве параметров этот макрос использует номер (WM_NAMEMSG) и имя (OnNameMsg) обработчика сообщения. Очевидно, что необходимо определить как номер сообщения, так и его функцию обработки:

#define WM_NAMEiMSG (WM_USER + 100) afx _ msg LRESULT OnNameMsg (

WPARAM

wParam,

LPARAM

1 Pa ram

Ясно, что в этом случае всю ответственность по использованию параметров wParam и IParam берет на себя программист.

Сообщения, определяемые пользователем, включаются в карту при помощи макроса

ON_REGISTERED_MESSAGE(nMessageVariable, memberFn)

В качестве параметров этот макрос использует идентификатор зарегистрированного сообщения nMessageVariable и имя обработчика memberFn. Обработчик должен иметь следующий прототип:

afx_msg LRESULT memberFn( WPARAM wParam, LPARAM 1Param);

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

Теперь, ознакомившись с общей структурой карты сообщений и ее компонентами, можно переходить к вопросу о том, в какой последовательности и кем осуществляется поиск обработчика сообщения или команды. Если внимательно посмотреть на иерархию классов библиотеки MFC, приведенную в приложении 1, то можно заметить, что все классы, которые могут получать сообщения, являются производными от класса CCmdTarget.

Класс CCmdTarget

Подобно тому как класс CObject является базовым для практически всех классов библиотеки MFC, класс CCmdTarget выступает в качестве базового для всех классов, которые могут работать с сообщениями.

Начнем с рассмотрения функций, входящих в состав класса CCmdTarget, и определенных в файле

AFXWIN.H:1

void CCmdTarget: :BeginWaitCursor() —

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

void CCmdTarget::EndWaitCursor() -

восстанавливает предыдущее изображение курсора, которое было изменено на "песочные часы" при вызове функции BeginWaitCursor.

void CCmdTarget: :RestoreWaltCursor() —

восстанавливает соответствующий курсор в виде песочных часов, после того как он был заменен на системный. virtual BOOL CCmdTarget::OnCmdMsg(

UINT nID, int nCode, void* pExtra,

AFX_CMDHANDLERINFO *pHandlerlnfo) -

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

В качестве параметров функция OnCmdMsq принимает: идентификатор команды — nfD; код извещения

nCode; значение параметра pExtra определяется значением параметра nCode; pHandlerlnfo — обычно этот параметр равен NULL, в противном случае функция сама заполняет элементы pTarget и pmf

структурыpHandlerlnfo.

Если функция нашла своего адресата, который обработает сообщение, то она возвращает значение TRUE, в противном случае FALSE.

Таким образом, основным назначением функции OnCmdMsg является распределение команд по другим объектам. В принципе, можно переопределить эту функцию. Но при этом не следует забывать, что на вас перекладывается задача указания следующего получателя команды, чтобы не прервалась цепочка передачи сообщений.

К практическим аспектам обработки сообщений Windows и извещений элементов управления мы приступим, когда ознакомимся с имеющимися в библиотеке MFC классами, отвечающими за работу с окнами, а пока займемся рассмотрением достаточно важного вопроса — каким образом осуществляется поиск обработчика команды.

Стандартный маршрут команды

Когда Run — функция класса CWinThread — получает сообщение, она направляет его в соответствующее окно-получатель сообщения, где оно будет обрабатываться. Несколько иначе все выглядит для команд. Поскольку они формируются в результате взаимодействия пользователя с приложением, то, обычно, команда начинает свой путь к получателю от главного окна приложения. Напомним, что каждый объект, способный получать сообщения, располагает своей картой сообщений. Что же происходит, когда какой-либо объект получает сообщение? А все очень просто. Имея свою карту сообщений получатель ищет в ней поступившую команду (или сообщение) и в случае успеха запускает соответствующий обработчик. Сразу же возникает вопрос: а если не найдет? Ну что ж, в этом случае поиск переносится в карту сообщений базовых классов, а если и там нет ассоциированного обработчика, то главное окно передает эту команду следующему кандидату на обработку. Для его определения в библиотеке MFC определен стандартный маршрут, по которому передается команда. Этот процесс продолжается до тех пор, пока команда не будет выполнена или отправлена оконной процедуре, действующей по умолчанию.

Порядок, в котором адресаты передают команды, "зашит" в библиотеке классов MFC и представляет собой следующую последовательность шагов:

1. Команду получает фрейм MDI (объект класса CMDIFrameWnd или производного от него). Порядок получателей команды:

активное окно класса CMDIChildWnd,

сам объект класса CMDIFrameWnd,

приложение-объект класса CWinApp или производного от него.

2. Команду получает фрейм документа (объект класса CFrameWnd, CMDIChildWnd или производного от одного из них). Порядок получате лей команды:

активное представление (view),

текущий фрейм документа,

приложение-объект класса CWinApp или производного от него.

3.Команду получает представление. Порядок получателей команды:

• текущее представление,

• документ, присоединенный к представлению.

4.Команду получает документ. Порядок получателей команды:

текущий документ,

шаблон документа, присоединенный к нему.

5.Команду получает блок диалога. Порядок получателей команды:

«текущий блок диалога,

окно, которое владеет этим блоком диалога,

приложение-объект класса CWinApp или производного от него.

Очевидно, что представленные здесь переходы взаимозависимы. Например, если команду получает документ, присоединенный к текущему представлению, то следующим объектом, которому будет передана команда, является текущий документ, а затем его шаблон.

Чтобы сказанное стало более понятным, рассмотрим небольшой пример. Пользователь работает с MDIприложением и хочет выделить весь текущий документ (для реализации этого действия в объекте класса документа приложения предусмотрен специальный обработчик). Он выбирает пункт меню

Edit\Select All (Правка\Выделить все), с которым ассоциирована команда ID EDIT_SELECT ALL. Первым эту команду получает главное окно приложения, которое дает возможность обработать его текущему активному дочернему окну MDI. До того как дочернее окно просмотрит свою карту сообщений, оно дает возможность обработать команду своему

представлению. Представление проверяет свою карту сообщений и, поскольку не находит там обработчика, направляет команду в ассоциированный с ним документ. Теперь уже документ ищет обработчик в своей карте сообщений, находит его, и начинает обрабатывать команду. Если бы мы не реализовали обработчик в нашем объекте-документе, то команда была бы направлена в шаблон, и в случае неудачи поиск продолжается в объекте класса CMDIFrameWnd, а затем в объекте класса приложения. Наглядно описанная последовательность действий представлена на рис. 17. Для команды, совершающей этот или любой другой маршрут, вызывается метод OnCmdMsg следующего объекта.

Выборобъектаинтерфейса

Элементменю

Edit\Select All

пользователя

 

Формирование

ID EDIT SELECT LL

команды

 

Поискполучателя

Объект-документ

командыON_COMMAND

 

в карте сообщений

 

Вызовобработчика

OnEditSelectAIIO

команды

 

Выделениеданныхвсегодокумента

Рис. 17. Пример обработки действия пользователя

Стандартные сообщения Windows, в отличие от команд, не используют какой-либо маршрут для поиска обработчика, т. к. они посылаются непосредственно в оконную процедуру того объекта, который должен соответствующим образом отреагировать на них. Но и в этом случае каждый оконный класс, образованный непосредственно или косвенно от класса CWnd, имеет собственную карту сообщений, которая устанавливает связи между сообщениями и соответствующими обработчиками. Если соответствующий обработчик не найден в карте сообщений некоторого класса, то он ищется в карте сообщений базового для него класса. При этом более эффективным является использование механизма виртуальных функций, которые определены в классе CWnd для стандартных сообщений Windows.

Команды обновления и класс CCmdlll

Когда пользователь, например, раскрывает меню, каждый его элемент "должен знать" каким он будет отображаться — доступным, отмеченным или нет. Получатель команды предоставляет ему эту информацию, решшзуя обработчик сообщения ON_UPDATE_COMMAND_UI, внутри которого используется специальный класс CCmdUI, определяющий или элемент меню, или кнопку панели инструментов, или любой другой объект интерфейса пользователя, способный формировать команды. Этот обработчик вызывается до вывода элементов интерфейса пользователя на экран (или их

перерисовки), что

позволяет задать их внешний вид. Маршрут, по которому ищется обработчик,

совпадает с тем, который действует для "обычных" командных сообщений (рис. 18)

Проверкасостоянияэлемента

Циклфоновойобработки

менюEdiftSeiect All

 

 

 

интерфейса пользователя

 

Т

 

 

Формирование

ID EDIT SELECT ALL

команды

 

 

Т

Поискполучателякоманды

 

ON_UPDATЈ_COMMAND

Объект-документ

_UI в карте

 

сообщений

 

Т

Вызовобработчика

OnUpdateEditSelectAIIO

команды

 

Блокирование/разблокированиепунктаменю

Рис. 18. Пример обновления элемента меню

Рассмотрим те возможности, которые заложены в классе CCmdUI для обновления элементов интерфейса пользователя. Начнем с членов класса:

UINT CCmdUI::m_nlD — идентификатор объекта интерфейса пользователя. DINT

CCmdlll::m_nlndex — индекс объекта интерфейса пользователя.

CMenu CCmdUI::m_pMenu —

указатель на меню, представляемое объектом CCmdUI. Если представляется не меню, то этот параметр равен NULL.

CMenu CCmdUI::m_pSubMenu -

указатель на имеющееся подменю, представляемое объектом CCmdUI. Если представляется не меню, то этот параметр равен NULL. Если подменю является раскрывающимся, то параметр m_nlD содержит идентификатор его первого элемента.

CWnd* CCmdUI::m_pOther -

указательнаобъектокна, которыйпослализвещение, например, панельэлементовуправленияилистрокасостояния. Если извещение пришло от меню или не от оконного объекта, то этот параметр равен NULL.