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

совместно с библиотекой классов MFC.

Прежде чем двигаться дальше, давайте так же внимательно рассмотрим класс, который отвечает за функционирование "MDI child" окон.

Класс CMDIChildWnd

Как вы уже поняли, основным назначением этого класса является обеспечение работоспособности дочер-MDI-приложений — окон "MDI child", кото-^ рые, говоря упрощенно, позаимствовали свои свойства как у дочерних, так и у перекрывающихся окон.

Большую часть возможностей этот класс наследует от своего базового — CFrameWnd, но имеет и свойства, присущие только ему. К ним можно отнести автоматическое отображение меню окна "MDI child" вместо основного меню MDI-приложения, а также добавление заголовка дочернего окна к заголовку его родителя.

Для того чтобы произошла автоматическая замена основного меню на меню окна "MDI child", в классе определена единственная переменная

HMENUCMDIChildWnd::m_hMenuShared

Она содержит дескриптор меню, ассоциированного с окном "MDI child".

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

Кроме того, в классе определены несколько функций:

BOOL CMDIChildWnd::Create( LPCTSTR IpszClassName, LPCTSTR IpszWindowName,

DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDW!NDOW, const RECT &rect = rectDefault,

CMDIFrameWnd *pParentWnd = NULL, CCreateContext *pContext = NULL) -

эта функция абсолютно идентична функции CFrameWnd::Create, однако задачи, которые они решают, достаточно сильно различаются, и нельзя подменять вызов одной из них вызовом другой. Как уже упоминалось, активное текущее окно "MDI child" может определять заголовок своего родительского окна, если для него установить бит стиля FWS_ADDTOTITLE. Эта функция вызывается MDI-окном в ответ на команду пользователя создать дочернее окно. При этом параметр pContext позволяет правильно связать дочернее окно с приложением. Если такой необходимости нет, то для него можно использовать значение по умолчанию

(NULL).

void CMDIChildWnd::MDIDestroy() -

удаляет заголовок окна "MDI child" из MDI-окна и разрушает само окно "MDI child".

void CMDIChildWnd::MDIActivate() -

активизирует текущее окно "MDI child". Когда MDI-окно становится активным, то активизируется и окно "MDI child", которое находилось в этом состоянии последним.

void CMDIChildWnd::MDIMaximize() -

разворачивает текущее окно "MDI child". Если у него не сброшен бит стиля FWS_ADDTOTITLE, то его заголовок добавляется в MDI-окно.

void CMDIChildWnd::MDIRestore() -

восстанавливает исходные размеры текущего окна "MDI child", если оно было развернутоилисвернуто.

CMDIFrameWnd* CMDIChildWnd::GetMDIFrame() -

позволяет получить указатель на родительское MDI-окно объекта класса CMDIFrameWnd. Для того чтобы получить указатель на родительское окно типа MDICLIENT, которое управляет обьектом класса

CMDIChildWnd, необходимо вызвать функцию CWnd::GetParent.

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

MDI — пример приложения

Категории сообщений

В библиотеке MFC принята классификация сообщений, отличная от рассмотренной ранее. Все

возможные сообщения разделены на три основные категории: G сообщения Windows,

Q извещения элементов управления,

Q командные сообщения (команды).

В первую категорию входят сообщения, имена которых начинаются с префикса WM_, за исключением WM_COMMAND. Все сообщения этой категории предназначены для обработки окнами и представлениями и часто содержат параметры, которые определяют алгоритм обработки того или иного сообщения. Сюда входят, например, аппаратные сообщения, сообщения обслуживания окна и т. д.

Вторая категория включает извещения (notification messages) от элементов управления и других дочерних окон, направляемые своим родительским окнам. Например, элемент управления LISTVIEW посылает своему родительскому окну сообщение WM_COMMAND, содержащее код извещения LVN SETDISPINFO, когда требуется обновить информацию об элементах списка. Оконная процедура отвечает на полученное извещение заполнением структуры LV_DISPINFO и передачей ее обратно в элемент управления. Механизмы передачи извещений и других ШМ_-сообщений аналогичны, с одним единственным исключением. Извещение BN_CLICKED, посылаемое элементом управления BUTTON, когда пользователь щелкает по нему, трактуется как командное сообщение и передается аналогично другим командам.

Сообщения этих двух категорий предназначены для объектов классов, образованных от базового класса CWnd, т. е. имеющих дескриптор HWND окна Windows. Их обработка осуществляется в соответствующей оконной процедуре.

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

Но независимо от способа разделения сообщений по тем или иным категориям, они существуют для того, чтобы их обрабатывает, т. е. при получении сообщения некоторая часть программы должна выполнить определенную последовательность действий. При рассмотрении оконной процедуры говорилось, что поступающие в нее сообщения обрабатываются индивидуально. Там же мы упомянули о существовании специального типа функций, каждая из которых отвечает за обработку одного и только одного сообщения. Аналогичный подход реализован и в библиотеке классов MFC, где для обработки каждого отдельного сообщения используется специальная функция-обработчик. Все функцииобработчики являются функциями какого-либо класса, и для них используются названия: функция-член обработчик сообщения, функция обработчик сообщения или просто обработчик сообщения.1 Принципы работы обработчиков сообщений не изменяются от того, к какой категории относится сообщение, однако механизмы их вызова различны. Но прежде чем рассматривать механизмы посылки и получения сообщений, необходимо понять, каким образом сопоставляются сообщение и соответствующий ему обработчик.

Карта сообщений

Библиотека классов MFC предоставляет альтернативу многочисленным операторам switch, используемым в традиционных Windows-программах, для обработки сообщений, посылаемых окну.2 Взаимосвязь сообщений и их обработчиков может быть определена таким образом, что когда сообщение поступает в оконную процедуру, автоматически вызывается соответствующий обработчик. Реализация такой взаимосвязи основана на понятии карты сообщений(message map).

Карта сообщений представляет собой механизм пересылки сообщений и команд Windows в окна, документы, представления и другие объекты приложения, реализованного на базе MFC. Такие карты преобразуют сообщения Windows, извещения элементов управления, а также команды меню, кнопок панелей инструментов, акселераторов клавиатуры в функции соответствующих классов, которые их обрабатывают. Эта отличительная черта карты сообщений реализована по аналогии с виртуальными функциями C++, но имеет дополнительные преимущества, не доступные для них. Каждый класс, который может получить сообщение, должен иметь свою карту сообщений, для того, чтобы иметь возможность соответствующим образом обрабатывать сообщения. При этом следует иметь в виду, что карта сообщений должна определяться вне какой-либо функции или объявления класса. Она также не

может размещаться внутри С-блока.

Для определения карты сообщений необходимо использовать три макроса: BEGIN_MESSAGE_MAP, END_MESSAGE_MAP иDECLARE_MESSAGE_MAP.

Макрос DECLARE_MESSAGE_MAP располагается в конце ' объявления класса, использующего карту сообщений:

class CTheClass : public CBaseClass

{

// объявления членов класса protected:

//{ {AFX_MSG_MAP (TheClass) afx_msg void OnPaintO;

//} }AFX_MSG_MAP, DECLARE_MESSAGE_MAP ( )

Он используется для объявления трех специальных членов класса:

О частного массива компонент AFX_MSGMAP_ENTRY — ^message Entries,

G защищенной структуры AFX_MSGMAP — messageMap, которая указывает на массив _message Entries, Q защищенной виртуальной функции — Get Message Map, которая возвращает адрес messageMap.

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

BEGIN_MESSAGE_MAP (CTheClass, CBaseClass) // { {AFX_MSG_MAP (CTheClass)

ON_WM_CREATE ( )

ON_WM_DESTROY () ON_COMMAND(ID_CHAR_BOLD, OnCharBold)

ON_UPDATE_COMMAND_UI ( ID_CHAR_BOLD, OnUpdateCharBold) //{ {AFX_MSG_MAP ON_NOTIFY(FN_GETFORMAT, ID_VIEW_FORMATBAR, OnGetCharFormat) ON_NOTIFY(FN_SETFORMAT, ID_VIEW_FORMATBAR, CnSetCharFormat) END_MESSAGE_MAP ( )

Как видите, начинается она с выполнения макроса:

BEGIN_MESSAGE_MAP (CTheClass, CBaseClass)

который имеет следующие параметры: CTheClass — задает имя класса, владельца карты сообщений; CBaseClass — определяет имя базового класса.

Если вы захотите объявить какие-либо члены класса после этого макроса, то для них нужно определить новый тип доступа (public, private или protected).

При использовании специальных средств автоматической генерации программного кода AppWiiard и ClassWizard, достаточно подробно описанных в приложении, все три описанных макроса, а также специальные скобки для ClassWizard (//{{AFX_MSG_MAP(CTheClass) и //{{AFX_ MSG_MAP) формируются автоматически при создании оконного класса.

Заканчивается карта сообщений вызовом макроса END MESSAGE_MAP, имеющего еще более простой формат:

END_MESSAGE_MAP()

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

Компоненты карты сообщений

Для каждого стандартного сообщения Windows определен свой макрос в форме:

ON_WM_XXX // XXX - имя сообщения, например, ON_WM_PAINT.

Имена обработчиков определяются при распаковке параметров каждого сообщения Windows на основе простого соглашения; имя всегда начинается с префикса On, за которым следует имя соответствующего сообщения Windows (без префикса WM_), записанное строчными буквами.

Например, для сообщения WM_PAINT в классе CWnd определен обработчик

afx_msg void OnPaintO;

а для сообщения WMJLBUTTONUP — обработчик

afx_msg void OnLButtonUp( UINT nFlaqs, CPoint point);

За формирование параметров, таких как nFlags и point, необходимых для каждого конкретного сообщения, отвечает соответствующий макрос. Описание всех обработчиков стандартных сообщений Windows можно найти в файле AFXWIN.H в объявлении класса CWnd, а также в приложении, приведенном в конце этой книги.

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

ON__COMMAND(id, memberFn)

Этот макрос использует в качестве параметров идентификатор команды id и произвольное имя обработчика команды memberFn. Прототип обработчика должен быть описан в соответствующем классе и иметь вид:

Макрос ON COMMAND EX, определяющий расширенную форму обработчика, является надмножеством над функциональными возможностями макроса ON_COMMAND. Такие обработчики команд принимают единственный параметр типа UINT, содержащий идентификатор команды, и возвращают значение типа BOOL: если возвращается FALSE, то команда передается для обработки другому объекту "получателю" команды; в противном случае обработка завершается.

Команды обновления выполняются посредством того же самого механизма, что и для "обычных" команд, только при этом используется макрос

ON_UPDATE_COMMAND_UI(id, memberFn)

Обработчики таких команд имеют вид:

afx_msg void memberFn(CCmdUI *pCmdUI);

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

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

Для обработки извещений от элементов управления применяется макрос

ON_CONTROL(wNotifyCode, id, memberFn)

Этот макрос использует в качестве параметров код извещения wNotifyCode от элемента управления, идентификатор элемента управления id и произвольное имя обработчика этой команды memberFn, прототип которого должен быть описан в соответствующем классе и иметь вид:

afx_msg void memberFn О;

Так же, как и для обработчиков команд, для каждого извещения необходимо использовать один, и только один макрос ON_CONTROL. Обработчик memberFn вызывается только в том случае, если код извещения от элемента управления wNotify, такой как BN_CLICKED, совпадает с кодом извещения, определенным в компоненте карты сообщений, и значение параметра id совпадает с идентификатором элемента управления.

Помимо рассмотренного, общего для всех элементов управления и дочерних окон, макроса, для кнопок (BUTTON), элементов редактирования (EDIT), списков (LISTBOX) и комбинированных списков (СОМВОВОХ) используются специальные макросы, например

ON_BN_CLICKED(id, memberFn) ON_EN_SETFOCUS(id, memberFn) ON_LBN_DBLCLK(id, memberFn)

и многие другие. Полный список макросов этой группы можно найти в документации или в файле AFXMSG.H. Их принцип действия полностью совпадает с принципом действия макроса

ON_CONTROL,

С появлением в Windows 95 и Windows NT более сложных общих элементов управления (common controls), имеющихся макросов оказалось недостаточно, т. к. для них вместе с сообщением зачастую

должны быть посланы некоторые данные. В связи с этим общие элементы управления Windows используют более мощное сообщение WM_NOTIFY, а в версиях Visual C++, начиная с 4.0, имеют прямую поддержку для этого сообщения на базе макроса

ON_NOTIFY(wNotifyCode, id, memberFn)

Параметры этого макроса полностью аналогичны параметрам уже рассмотренного макроса ON_CONTROL. Единственное, но существенное отличие заключается в параметрах обработчика memberFn:

afx_msg void memberFn( NMHDR *pNotifyStruct, LRESULT *result);

В качестве параметров обработчика используются: pNotifyStruct — указатель на структуру типа NMHDR, или большую, включающую ее в себя; result — указатель на переменную, в которую должно быть записано возвращаемое значение.

Подробно работа с сообщением WM_NOTIFY и поля структуры NMHDR описываются в главе 13, при обсуждении панелей элементов управления. Здесь мы приведем только список извещений, общих для всех дополнительных элементов управления:'

NM_CLICK пользователь нажал левую кнопку мыши в элементе управления

NM_DBLCLK пользователь дважды щелкнул левой кнопкой мыши в элементе управления

NM_RCLICK пользователь нажал правую кнопку мыши в элементе управления

NM_DBLCLK пользователь дважды щелкнул правой кнопкой мыши в элементе управления

NM_RETURN пользователь нажал клавишу <Enter>, когда элемент управленияимелфокусввода

NM_SETFOCUS элемент управления получил фокус ввода

NM_KILLFOCUS элементуправленияпотерялфокусввода

NM_OUTOFMEMORY элемент управления не может завершить какую-либо операциюиз-занехватки памяти

Если есть необходимость использования одного обработчика для нескольких команд или извещений от элементов управления, то можно воспользоваться соответствующими макросами Извещения, определенные

для каждого элемента управления, будут описаны при обсуждении соответствующего элемента управления.

ON_COMMAND_RANGE (idFirst, idLast, memberFn) ON_CONTROL_RANGE (wNotif yCode, idFirst, idLast, memberFn) ON_NOTIFY_RANGE (wNotif yCode, idFirst, idLast, memberFn) Эти макросы используют в качестве параметров два идентификатора idFirst и idLast, которые составляют непрерывный ряд. Обработчик memberFn будет вызываться всякий раз, когда в оконную процедуру поступит сообщение, которое в параметре wParam содержит идентификатор команды, лежащий в границах, определяемых idFirst и idLast. Прототип обработчика должен иметь вид

afx_msg void memberFn (UINT nID) ;

для первых двух и

af x_msg void memberFn ( UINT nID,

NMHDR *pNotifyStruct, LRESULT *result) ;

для последнего макроса.

Аналогичный макрос существует и для команд обновления:

ON_UPDATE_COMMAND_UI_RANGE (idFirst, idLast, memberFn)

Обработчик, как и для макроса ON UPDATE_COMMAND_UI, имеет единственный параметр — указатель на объект CCmdUI:

afx_msg void memberFn (CCmdUI *pCmdUI);

Для извещений, по аналогии с макросом ON_COMMAND_EX, определены и макросы:

ON_NOTIFY_EX(r.Code, id, memberFn)