Студия web-дизайна Хостмэйк
Наши работыКонтактыО компанииОтзывыГлоссарийСтатьи

Глава 12. В глубине меню

Статьи Программирование на С/С++

В глубине меню



Что такое меню, как оно создается и как с ним работать? Начальные сведения
уже были приведены в главе 4, здесь же мы постараемся осветить эти вопросы более
подробно. Однако, прежде чем переходить к изучению соответствующих средств
Windows и библиотеки MFC, необходимо рассмотреть стандартные типы меню, которые
можно создавать в приложениях Windows, и их основные характеристики.


Основные типы меню


Меню обычно располагается под полосой заголовка в верхней части
перекрывающегося или всплывающего окна и представляет собой словесные или
символические опции, дающие возможность манипулировать особенностями программы.
Такое меню называется Главным меню приложения (верхний уровень иерархии).
Отдельные элементы меню располагаются в полосе меню ( size=2>menu bar) — рис. 12.1 и 12.2.


Элементы меню могут иметь различные типы и содержать ряд модификаторов (рис.
12.3):



  •  командная клавиша — вместо выбора пункта меню команду можно вызвать
    с помощью этой клавиши;
  •  стрелка — показывает, что при выборе данного элемента появится
    подменю;
  •  разделительная черта — отделяет одну группу элементов меню от
    другой;
  •   маркер — означает, что данный параметр меню выбран;
  •  многоточие — сообщает пользователю о том, что при выборе данного
    элемента на экран будет выведен блок диалога;
  •  полутоновая надпись — говорит, что в текущем состоянии данный
    элемент меню недоступен;
  •  знак подчеркивания — означает, что данный элемент меню можно вызвать
    с помощью командной клавиши (комбинации клавиш), куда входит эта буква. Такие
    буквы называются мнемоническими.
\

................................................1

Рис. 12.1. Заголовок и Главное меню приложения WordPad



........................................................2

Рис. 12.2. Раскрытое меню пункта "Вид"



.........................................................3

Рис. 12.3. Команда, имеющая подменю


Каждое окно, имеющее заголовок, может иметь системное меню, которое
вызывается щелчком левой кнопки мыши на пиктограмме, расположенной в левой части
заголовка окна (рис. 12.4), и представляет собой набор стандартных команд. В
Windows 95 системное меню появляется, если щелкнуть правой кнопкой мыши в полосе
заголовка или на пиктограмме свернутого окна.



..............................................................4

Рис. 12.4. Системное меню


Система Windows предоставляет специальный тип меню, которое можно создавать в
любом месте экрана. Такое меню называется контекстным или плавающим ( face="Courier New" size=2>floating popup menu) и не привязано к полосе
меню (рис. 12.5)'. Оно ассоциируется с некоторой областью окна или объектом,
например, пиктограммой. Контекстное меню в некоторых случаях удобнее других, т.
к. его содержимое зависит от того, для какого объекта оно было создано. Обычно
оно выводится на экран щелчком правой кнопки мыши.


]
................................................................5

Рис. 12.5. Контекстное меню рабочей области окна приложения face="Courier New" size=2>WordPad


Теперь, перечислив основные типы используемых в Windows меню, рассмотрим те
возможности, которые предоставляет библиотека MFC для работы с ними.


Точно так же, как в Win32 API есть функции, специально предназначенные для
работы с меню, в библиотеке MFC есть для них адекватные альтернативы,
реализованные в виде класса СМenu (рис.
12.6), полностью вобравшего в себя функции API.



...................................................................6

Рис. 12.6. Место класса СМenu в
иерархии библиотеки MFC


Класс СМenu   инкапсулирует
объект Windows, определяемый дескриптором size=2>HMENU, и предоставляет функции для работы с меню. С помощью этого
класса приложение может создавать меню, даже не имея его шаблона, добавлять или
удалять элементы или подменю, управлять их состоянием — активизировать или
блокировать, отмечать команды и т. д.


 Рассмотрим основные члены этого класса.



CMenu::m_hMenu


Определяет дескриптор HMENU меню Windows,
присоединенного к объекту класса, для создания которого, как обычно,
используется конструктор.


 СМеnu::СМеnu
()


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


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



BOOL CMenu::CreateMenu
()


Создает пустое меню Windows и присоединяет
его к объекту класса. Добавлять в него элементы можно, используя функции
AppendMenu и InsertMenu. При успешном создании меню функция возвращает TRUE и
FALSE — в случае неудачи.


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



BOOL
CMenu::CreatePopupMenu ()


Создает пустое подменю. Добавить в него
элементы можно с помощью тех же функций — AppendMenu и InsertMenu. При
успешном завершении функция возвращает TRUE и FALSE — в случае неудачи.


Если создание меню производится на основе шаблона, то можно воспользоваться
функциями



BOOL CMenu::LoadManu
(LPCTSTR IpszResourceName)


 и



BOOL CMenu::LoadMenu
(UINT nIDResource)


Загружают ресурс меню из исполняемого файла
приложения и присоединяют его к объекту. В качестве параметра используется
либо указатель на текстовую строку IpszResourceName, содержащую имя ресурса
загружаемого меню, либо его числовой идентификатор nID.


Если по каким-либо причинам понадобилось меню, которое не присоединяется ни к
одному окну, то при завершении работы с ним следует воспользоваться функцией



BOOL
CMenu::DestroyMenu ()


Удаляет меню перед завершением работы
приложения и возвращает системе занимаемые им ресурсы.


Для изменения состава меню, созданного функцией size=2>CreateMenu, в  классе предусмотрена специальная группа
функций.


Добавление элементов в меню производится функциями size=2>AppendMenu и InsertMenu.



BOOL CMenu::AppendMenu


UINT nFlags,


 UINT nIDNewItem = 0,
LPCTSTR IpszNewItem = NULL) 


BOOL CMenu::AppendMenu


UINT nFlags, 


UINT nIDNewItem = 0,


 const CBitmap
*pBmp)


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


MF_CHECKED, MF_UNCHECKED



При изображении элемент меню отмечается
(MF_CHECKED) или не отмечается (MF_UNCHECKED) галочкой


MF_DISABLED, 
MF_ENABLED



Элемент меню доступен (MF_ENABLED) или
блокирован (MF_DISABLED), но изображается обычным (черным) цветом


MF_GRAYED 



Элемент меню блокирован и изображается
серым цветом


MF_MENUBREAK



 Элемент меню верхнего уровня
выводится с новой строки, а элемент подменю— в новом столбце без
разделительной вертикальной линии


MF_MENUBARBREAK 



Аналогично MF_MENUBREAK, но дополнительно
новый столбец отделяется вертикальной линие
й


MF_OWNERDRAW 



Меню само отвечает за изображение своего
элемента 


MF_POPUP



 Элемент меню имеет ассоциированное с
ним подменю, дескриптор которого задается в параметре nIDNewItem


MF_SEPARATOR 



Элемент представляет собой горизонтальную
разделительную линию; этот флаг не может комбинироваться с MF_POPUP и
MF_DISABLED; при установленном данном флаге все другие игнорируются


MF_STRING 



Элемент представляет собой текстовую
строку


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



  • MF_DISABLED, MF_ENABLED и MF_GRAYED

  • MF_STRING, MFJDWNERDRAW, MF_SEPARATOR и
    битовый массив

  • MF_MENUBREAK и MF_MENUBARBREAK
  • MF_CHECKED и MF_UNCHEGKED


Параметр nIDNewltem определяет
идентификатор команды нового элемента, а если nFlags установлен в MF_POPUP, то
дескриптор (HMENU) подменю. Через этот параметр передается дополнительная
информация о содержимом нового элемента, если nFlags принимает одно из
следующих значений:


color=#800000>nFlags                         
size=2>IpszNewltem


size=2>MF_OWNERDRAW            





Содержит 32-битное значение, которое
приложение может использовать в качестве дополнительных данных,
ассоциированных с элементом меню, при обработке сообщений WM_MEASUREITEM и
WM_DRAWITEM


MF_SEPARATOR





 Значение игнорируется


MF_STRING 





Содержит текстовую строку


Параметр рВтр определяет указатель на объект
класса СВ'Лтар, который может использоваться в качестве содержимого элемента
меню вместо текстовой строки. При этом параметр nFlags не может принимать
значения MF_OWNERDRAW и MF_STRING.




BOOL
CMenu.::InsertMenu (


UINT nPosition,


UINT nFlags,


UINT nIDNewltem =0,


LPCTSTR IpszNewltem =
NULL)


 и 



BOOL CMenu::InsertMenu
(


UINT nPosition,


UINT nFlags,


UINT nIDNewltem =0,


const CBitmap *pBmp)


Функции вставляют новый элемент в позицию,
определяемую параметром nPosition, и сдвигают вниз существующие элементы.
Значения всех параметров, кроме nFlags, такие же, как и для функции
AppendMenu. Параметр nFlags дополнительно используется для интерпретации
значения nPosition:


size=2>nFlags                               face="Courier New" color=#800000 size=2>   
nPosition


MF_BYCOMMAND 





Определяет идентификатор элемента меню,
перед которым вставляется новый элемент


MF_BYPOSITION 





Определяет порядковый номер элемента
меню, перед которым вставляется новый элемент; если установить nPosition =
—1, то элемент будет добавлен в конец меню


Если требуется не вставлять новый, а заменить существующий элемент меню, то
можно воспользоваться функциями:



BOOL CMenu:
:ModifyMenu (


UINT nPosition, 


UINT nFlags, 


UINT nIDNewItem, 


LPCTSTR IpszNewItem)


 и



BOOL CMenu:
:ModifyMenu (


UINT nPosition, 


UINT nFlags, 


UINT nIDNewItem, 


const CBitmap *pBmp)


Параметры этих функций идентичны параметрам
функций AppendMenu и InsertMenu за исключением того, что новый элемент не
вставляется в меню, а заменяет тот, на который указывает параметр
nPosition.


Для удаления элемента из меню в классе предусмотрены две функции:


BOOL CMenu:rDeleteMenu
(


UINT nPosition,


UINT nFlags) 


и



BOOL CMenu::RemoveMsnu
(


UINT nPosition,


UINT nFlags)


Функции удаляют элемент меню, задаваемый
параметром nPosition, значение которого интерпретируется так же, как в функции
InsertMenu. Отличие между функциями заключается в том, что при удалении
подменю функцией DeleteMenu все связанные с ним ресурсы освобождаются, а в
случае RemoveMenu— нет, и можно снова воспользоваться удаленным элементом (по
его идентификатору)
.


Для изменения состояния элемента меню используется функция:



UINT
CMenu::EnableMenuItem (


UINT nIDEnableltem,


UINT nEnable)


Позволяет установить элемент меню
nlDEnableltem в одно из трех состояний (задается параметром nEnable):
доступное (MF_ENABLED), запрещенное (MF_DISABLED) или заблокированное
(MF_GRAYED). Эти значения должны комбинироваться с одним из флагов
MF_BYCOMMAND или MF_BYPOSITION, которые определяют, каким образом задается
элемент — идентификатором или порядковым номером. Функция возвращает
предыдущее состояние элемента или —1, если элемент не существует.


У элемента можно проставить отметку с помощью следующих функций.



UINT
CMenu::CheckMenuItem (


UINT nIDCheckltem,


UINT nCheck)


Устанавливает или сбрасывает галочку у
элемента меню. Параметр nCheck может принимать значение MF_CHECKED или
MFJJNCHECKED, как обычно, в комбинации с флагами MF_BYCOMMAND или
MF_BYPOSITION, которые определяют интерпретацию параметра nIDChecked—
идентификатор или порядковый номер. Функция возвращает предыдущее состояние
элемента или —1, если элемент не существует.


BOOL
CMenu::CheckMenuRadioItem (


UINT nIDFirst, 


UINT nIDLast,


UINT nIDItem, 


UINT nFlags)


Помечает определенный элемент меню в группе
переключателей, одновременно сбрасывая метку у остальных элементов группы. Для
отметки элемента вместо галочки используется кружок. Параметры nIDFirst и
nIDLast определяют, соответственно, первый и последний элемент в группе
переключателей. Сам отмечаемый элемент задается параметром nIDItem. Параметр
nFlags определяет интерпретацию этих параметров и может принимать одно из
значений: MF_BYCOMMAND или MF_BYPOSITION.


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



BOOL
CMenu::SetManuItemBitmaps (


UINT nPosition,


UINT nFlags,


const CBitmap
*pBmpUnchecked,


const CBitmap
*pBmpChecked)


Функция ассоциирует с элементом меню
битовые массивы, которые будут выводиться для помеченного (pBmpChecked) и
непомеченного (pBmpUnchecked) состояний. Параметры nPosffion и nFlags
определяют элемент меню по идентификатору или порядковому номеру. Если либо
pBmpChecked, либо pBmpUnchecked равен NULL, то для соответствующего атрибута
рядом с элементом ничего не отображается, а если оба параметра равны NULL, то
используются стандартные галочка и кружок. При использовании функции следует
иметь в виду, что при разрушении меню не происходит автоматического удаления
битовых массивов — эта задача возлагается на приложение.


После внесения в меню всех изменений необходимо вызвать функцию



void CWnd::DrawMenuBar
()


Производит перерисовку полосы меню.


Для поддержки работы с контекстными меню в классе реализована функция



BOOL
CMenu::TrackPopupManu (


UINT nFlags,


int x,


int y,


CWnd *pWnd,


LPCRECT IpRect = 0)


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


Значения параметра nFlags для задания
расположения меню на экране


TMP_CENTERALIGN 



Центрирование относительно координаты,
заданной параметром х


TMP_LEFTALIGN



 Выравнивание по левой границе
относительно координаты, заданной параметром х


TMP_RIGHTALIGN



 Выравнивание по правой границе
относительно координаты, заданной параметром х 


Значения параметра nFlags для кнопки
мыши


TMP_LEFTBUTTON 



Использование левой кнопки
мыши 


TMP_RIGHTBUTTON 



Использование правой кнопки мыши


Параметр pWnd задает окно, которое получит
сообщение WM_COMMAND после того, как пользователь сделает выбор в контекстном
меню, a IpRect является указателем на структуру типа ПЕСТ (или класса CRect),
определяющую координаты прямоугольной области, в которой пользователь может
выполнять выбор из меню. Если щелчок мышью будет сделан вне этой области,
контекстное меню исчезнет с экрана, что эквивалентно отказу от выбора. Если
для IpRect задать значение NULL, то размеры и расположение этой прямоугольной
области будут совпадать с размерами контекстного меню.


В классе представлены две виртуальные функции, которые произвольным образом
позволяют изменять внешний вид меню: size=2>Measureltem и Drawltem. Но
они заслуживают отдельного разговора и мы к ним вернемся в разделе
"Самоотображение элементов меню" данной главы.


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


В системе Windows реализованы три способа создания меню:


1. На основе шаблона, задаваемого в файле ресурса.


2. Динамическое создание при помощи специальных функций.


3. На основе шаблона в оперативной памяти. Рассмотрим каждый из этих способов
более подробно.



Создание меню на основе шаблона


Этот подход мы уже рассматривали и поэтому не будем останавливаться на нем
более подробно, а приведем только описание атрибутов, определяющих внешний вид и
поведение строки меню (рис. 12.7, табл. 12.1).



........................................................7

Рис. 12.7. Атрибуты меню в окне свойств, открытом в режиме настройки
параметров меню


Таблица 12.1. Описание атрибутов, определяющих внешний вид меню

frame=box>


































Атрибут



Описание



Separator



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



Checked



При выводе на экран элемент меню отмечается
слева галочкой



Pop-up



Элемент определяет
подменю



Grayed



Элемент отображается серым цветом и находится в
заблокированном состоянии; несовместим с атрибутом
Inactive



Help



Элемент выравнивается по правому краю полосы
меню



Break



Этот атрибут может принимать одно из трех
значений: None — обычный элемент меню; Column — для меню
верхнего уровня элемент выводится с новой строки, а для подменю — в новом
столбце; Ваг— дополнительный столбец подменю отделяется
вертикальной линией.



ID



Идентификатор элемента
меню


frame=box>














Атрибут



Описание



Caption



Текст, определяющий имя элемента меню;
текстовая строка может содержать символы: & — буква, перед которой
стоит этот знак, будет подчеркнута; t — включает в строку символ
табуляции; а — выравнивает текст по правой границе
меню



Prompt



Текст, который будет отображаться в строке
состояния и (после п) в окне всплывающей
подсказки



Inactive



Элемент находится в заблокированном состоянии,
но отображается обычным цветом


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



  •  При регистрации класса окна — когда меню определяется для нескольких
    окон одного класса:


wc.lpszMenuName =
"IDR_MAINFRAME";



  •  При создании окна — если для него необходимо меню, отличное от
    указанного в классе окна:


if
(!pMainFrame->LoadFrame(IDR_MAINFFLAME)) 


return FALSE;



  •  При работе в рамках архитектуры "документ/представление" —
    достаточно, чтобы идентификатор меню совпадал с идентификаторами фрейма,
    документа и представления. Тогда вопросами подключения меню полностью
    занимается библиотека классов MFC при создании шаблона документа:


pDocTemplate = new
CMultiDocTemplate(


IDR_NOTETYPE,  //
идентификатор меню


size=2> RUNTIME_CLASS(CNoteDoc), 


size=2>RUNTIME_CLASS(CNoteFrame),


size=2> RUNTIME_CLASS(CNoteView));



 Примечание 


Мы обязательно рассмотрим архитектуру
"документ/представление" более подробно.


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


Меню, созданное в системе Windows, где взаимодействие между объектами
осуществляется при помощи сообщений, также работает по этому принципу. Наиболее
важным из сообщений, которое передает меню, является size=2>WM_COMMAND, означающее, что выбран тот или иной элемент меню. В
табл. 12.2 приведены этапы работы с меню и возникающие при этом сообщения.


Таблица 12.2. Этапы работы с меню и возникающие при этом сообщения


frame=box>





































Этап работы



Сообщение



Обработчик
MFC



Инициализация меню



WM INITMENU



OnlnitMenu



Вывод всплывающего меню на
экран



WM INITMENUPOPUP



OnlnitMenuPopup



Инициализация и вывод на экран всплывающего
меню



WM INITMENU и WM
INITMENUPOPUP



OnlnitMenu и
OnlnitMenuPopup



Нахождение нужного элемента
меню



WM_MENUSELECT



OnMenuSelect



Выбор элемента меню



WM_COMMAND



OnCommand



Выбор элемента системного
меню



WM_SYSCOMMAND



size=2>OnSysCommand


Рассмотрим кратко роль перечисленных сообщений.


 Примечание 


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


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



void
CWnd::OnSysCommand(


UINT nID, LPARAM
IParam)


Первый параметр nID может содержать
идентификатор строки системного меню, а также некоторые другие
значения:


SC_CLOSE 



Удаление окна


SC_HOTKEY 



Активизация определенным приложением окна,
связанного с командной клавишей


SC_HSCROLL 



Прокрутка по горизонтали


SC_KEYMENU 



Выбор элемента меню при помощи командной
клавиши


SC_MAXIMIZE или face="Courier New" color=#800000 size=2>SС_ICОМ



разворачивание окна 


SC_MINIMIZE или 
SC_ICON



Сворачивание окна 


SC_MOUSEMOVE



Выбор элемента меню при помощи мыши


SC_MOVE 



Перемещение окна


SC_NEXTWINDOW



Переключение на следующее окно


SC_PREVWINDOW 



Переключение на предыдущее окно


SC_RESTORE



 Восстановление нормального положения
и размеров окна


SC_SCREENSAVE



Запуск приложения-заставки (screen-saver
application), определенного в разделе [boot] файла <system.ini>


SC_SIZE 



Изменение размеров окна


SC_TASKLIST 



Запуск или активизация приложения Task
Manager


SC_VSCROLL 



Прокрутка по вертикали


 Примечание 


Кроме перечисленных, можно использовать и свои
идентификаторы, как будет показано в рассматриваемом ниже примере.


Второй параметр IParam используется только при следующих
значениях первого параметра nID:


size=2>nID                                 
IParam


size=2>SC_HOTKEY              






Содержит идентификатор
активизируемого окна


size=2>SC_MOUSEMOVE      






Младшее слово содержит х-координату,
а старшее — у-координату


Перед отображением всплывающего меню (один раз, при активизации) система
Windows посылает сообщение WM_INITMENU с
тем, чтобы приложение могло изменить меню перед тем, как его увидит
пользователь. При этом библиотека MFC вызывает обработчик:



void CWnd:lOnlnitMenu
(CMenu *pMenu)


Параметр рМепи задает указатель на объект
СМеnu, определяющий активизируемое меню. Обработка может заключаться в
активизации или деактивизации строк меню, изменении их состояния и т.
п.


Когда пользователь выбирает элемент, имеющий подменю, система Windows, прежде
чем отобразить всплывающее меню, посылает владельцу сообщение face="Courier New" size=2>WM_INITMENUPOPUP. Библиотека MFC вызывает
обработчик, который дает приложению возможность изменить всплывающее меню перед
его отображением:



void
CWnd::OnlnitMenuPopup (


CMenu pPopupMenu,


DINT nlndex, 


BOOL bSubMenu)


Параметр рРорuрМеnu— указатель на объект
"всплывающее меню"; nID— индекс всплывающего меню в меню верхнего уровня;
bSubMenu— определяет, является ли всплывающее меню системным (TRUE) или
нет.


Каждый раз, когда пользователь перемещается от одного элемента меню к
другому, Windows посылает сообщение size=2>WM_MENUSELECT, которое идентифицирует текущий элемент, на что
библиотека MFC реагирует вызовом обработчика



void
CWnd::OnMenuSelect (


UINT nltemlD,


UINT nFlags,


HMENU hSubMenu)


Параметр nltemlD— идентификатор выделенного
элемента или индекс всплывающего меню. Параметр nFlags содержит комбинацию
следующих флагов, определяющих атрибуты элемента меню:


MF_BITMAP



 Элемент является битовым
массивом
 


MF_CHECKED 



Элемент отмечается галочкой


MF_DISABLED 



Элемент является заблокированным, но
отображается в нормальном виде


MF_GRAYED 



Элемент отображается серым цветом и не
может быть выбран


MF_MOUSESELECT 



Элемент выделен мышью


MF_OWNERDRAW 



Элемент рисуется окном, создавшим
меню


MF_POPUP 



С данным элементом связывается всплывающее
меню


MF_SEPARATOR 



Элемент является разделителем


MF_SYSMENU



 Элемент принадлежит системному
меню


Последний параметр hSubMenu используется в
двух случаях, зависящих от состояния параметра nFlags
:


size=2>nFlags                        face="Courier New" color=#800000 size=2> hSubMenu


MF_SYSMENU 





Идентифицирует меню, ассоциированное с
сообщением 


MF_POPUP 





Идентифицирует дескриптор меню верхнего
уровня


Многие приложения используют сообщение size=2>WM_MENUSELECT для отображения дополнительной информации о
выбранном элементе меню в строке состояния.


Случай, когда пользователь выбирает элемент меню, a Windows посылает
сообщение WM_COMMAND, уже не раз
описывался в предыдущих примерах — необходимо либо воспользоваться одним из
предопределенных обработчиков стандартных команд меню, либо создать свой,
например, так, как было продемонстрировано в главе 11.


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


1. Добавить в уже имеющуюся полосу меню элемент Tools, а в качестве его
подпунктов — "2 column" и "3 column" (рис. 12.8).



.........................................................................8

Рис. 12.8. Добавляем новые элементы меню


2. Внести приведенный ниже код в карту сообщений класса CMainFmme:


BEGIN_MESSAGE_MAP(CMainFrame,
CFrameWnd)


 
//{(AFX_MSG_MAP(CMainFrame)


//}}AFX_MSG_MAP


ON_COMMAND_RANGE(ID_TOOLS_2
COLUMN, ID_TOOLS_3 COLUMN,


OnToolsColumn)


 
ON_UPDATE_COMMAND_UI_RANGE(ID_TOOLS_2COLUMN, IDJTOOLS_3COLUMN,


size=2>OnUpdateToolsCo.lumn) 


END_MESSAGE_MAP()


3. В описание класса CMainFrame
добавить определение обработчиков:



size=2>//{{AFX_MSG(CMainFrame)


...


//}}AFX_MSG


afx_msg void
OnToolsColuinnfUINT nID) ;


afx_msg void
OnUpdateTooisColumn(CCmdUI* pCmdUI);


4. И, наконец, написать код самих обработчиков:



void
CMainFrame::OnToolsColumn(UINT nID) 


{


// Запоминаем выбранное число
столбцов


m_nPaneCol = (nID =
ID_TOOLS_2COLUMN) ? 2 : 3;


// Устанавливаем столбцы на
панели инструментов рисования


size=2>SetColumns(mJnPaneCol); 


}


void
CMainFrame::OnUpdateToolsColumn(CCmdUI* pCmdUI) 


{


// Определяем выбранный пункт
меню


UINT nCol = (pCmdUI->m_nID
== ID_TOOLS_2COLUMN) ? 2 : 3;


// Устанавливаем маркер
состояния — галочку у выбранного


// пункта меню и сбрасываем у
остальных


if(m_nPaneCol ==
nCol) 


size=2>pCmdUI->SetCheck(TRUE);


else


size=2>pCmdUI->SetCheck(FALSE); 


}


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


Поэтому мы переходим к практическим примерам. Как вы помните, мы еще не
закончили наше приложение Graph. Здесь мы добавим в него возможность выбора
цвета как для рисования линий, так и для заливки. Но сделаем это, как и
договаривались, не общепринятым способом, а таким, который позволит
проиллюстрировать максимум возможностей работы с меню.



Добавление элемента в системное меню


Итак, мы создаем контекстное меню, появляющееся при нажатии правой кнопки
мыши в клиентской области окна и позволяющее выбрать цвет заливки (да и текущий
цвет линий). Одновременно в системное меню добавим пункт, разрешающий работу с
этим контекстным меню (рис. 12.9).



int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct) 


{


// Получаем доступ к
системному меню 


CMenu* pSysMenu =
GetSystemMenu(FALSE); 


// Определяем текст, которым
будет представлен


// созданный элемент
системного меню


CString
strColorMenu("Разрешить смену текущего цвета");


 // Добавляем в него
разделитель и новый элемент 


// "Разблокировать меню
Цвет" 


size=2>pSysMenu->AppendMenu(MF_SEPARATOR); 


size=2>pSysMenu->AppendMenu(MF_BYCOMMAND I MF_STRING,


 IDM_COLOR,
StrColorMenu);


...


}



...........................................................9

Рис. 12.9. Добавляем новый пункт в системное меню


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



CMenu*
CWnd::GetSystemMenu(BOOL bRevert)


Позволяет получить идентификатор копии
системного меню, так что с ним можно проводить любые допустимые манипуляции.
При добавлении своего элемента в системное меню необходимо учитывать, что его
идентификаторы имеют номера, начинающиеся с OxFOOO. Поэтому собственный
идентификатор должен иметь номер, меньший этого значения. Кроме того, в
качестве последней цифры шестнадцатеричного представления номера команды
рекомендуется использовать 0. Параметр bRevert задает режим работы функции:
если он равен FALSE, можно выполнять модификацию системного меню. Значение
TRUE "возвращает" системное меню в состояние, приписанное ему по
умолчанию.


Следующая строка кода



CString
strColorMenu("Разрешить смену текущего цвета");


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


Нам осталось только добавить те элементы меню, которые нам необходимы. Это,
безусловно, разделитель и собственно текст нашего меню. Выполняется это очень
просто с помощью функции size=2>CMenu::AppendMenu.


Здесь необходимы некоторые пояснения. Как вы помните, при создании меню с
помощью редактора ресурсов мы обязательно должны были присвоить каждому его
элементу идентификатор, который является ничем иным, как некоторым целым числом.
Так вот, редактор ресурсов сам назначает числовой эквивалент введенному
идентификатору. Совсем по-другому обстоит дело в данном случае. При динамическом
добавлении элемента меню мы самостоятельно должны сопоставить идентификатор с
некоторым цифровым эквивалентом. Сделать это можно, непосредственно редактируя
файл <resource.h>, а можно с
помощью IDE. Именно такой подход мы
сейчас и рассмотрим:


1. Выберите пункт меню View | Resource
symbols
и на экране появится одноименный диалог (рис. 12.10), в котором
вы найдете все имеющиеся идентификаторы.



..............................................................10

Рис. 12.10. Здесь можно получить информацию по имеющимся
идентификаторам


2. Нажмите кнопку New, чтобы создать
новый идентификатор. На экране появится диалог size=2>New Symbol.


3. В поле Name введите обозначение
идентификатора, а в поле Value — его
цифровое значение (рис. 12.11).


4. Нажмите последовательно кнопки ОК и
Close в соответствующих диалогах — новый
идентификатор создан.



...............................................................11

Рис. 12.11. Создаем новый идентификатор


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


 Примечание 


Некоторые из представленных в блоке диалога Resource
Symbols идентификаторы не снабжены галочкой в столбце In Use. И в поле Used by
для них выводится загадочная надпись "not used". Естественно, это не означает,
что данный идентификатор не нужен. Просто ClassWizard о нем ничего не
знает.


Обработка команд системного меню несколько отличается от стандартной, и об
этом необходимо сказать несколько слов. Я уже отмечал, что когда пользователь
выбирает элемент системного меню, вызывается обработчик сообщения face="Courier New" size=2>WM_SYSCOMMAND. Следовательно, наша задача —
реализовать этот обработчик. Очевидно, что делать это мы будем с помощью мастера
ClassWizard, который создаст необходимые
компоненты карты сообщений и заготовку обработчика:



class CMainFrame : public
CFrameWnd 


{


...


public:


BOOL m__bEnable; // индикатор
включения контекстного меню 


protected:


size=2>//{{AFX_MSG(CMainFrame)


afx_msg void
OnSysCommand(UINT nID, LPARAM IParam); 


//}}AFX_MSG


...


};


...


BEGIN_MESSAGE_MAP(CMainFrame,
CFrameWnd)


/ / {{AFX_MSG_MAP
(CMainFrame)


size=2>ON_WM_SYSCOMMAND() 


//}}AFX_MSG_MAP


END_MESSAGE_MAP()


void
CMainFrame::OnSysCommand(UINT nID, LPARAM IParam) 


{


// Прежде всего отсекаем
команды несистемного меню


if((nID & OxFFFO) ==
IDM_COLOR)


{


// Получаем доступ к
системному меню


 CMenu *pSysMenu =
GetSystemMenu(FALSE);


 // Определяем текущее
состояние элемента


UINT nState =
pSysMenu->GetMenuState(IDM_COLOR, MF_BYCOMMAND);



 // Если элемент
отмечен, ... 


if(nState ==
MF_CHECKED)


 {


// сбрасываем маркер,
... 


size=2>pSysMenu->CheckMenuItem(IDM_COLOR,


MF_BYCOMMAND |
MF_UNCHECKED);


 // разрешаем работу с
контекстным меню mjDEnable = FALSE;


 }


else  //и
наоборот 


{


size=2>pSysMenu->CheckMenu!tem(IDM_COLOR, MF_BYCOMMAND | MF_CHECKED);



 // запрещаем работу с
контекстным меню m_bEnable = TRUE; 



}


else 


{


// Если команда "не наша",
необходимо передать ее 


// библиотеке для корректной
обработки 


CWnd::OnSysCommand(nID,
IParam); 



}


В приведенном фрагменте обратите внимание на строчку



BOOL m_bEnable; // индикатор
включения контекстного меню


которая включена для того, чтобы как-то оправдать задачу создания новой
команды системного меню. Значение TRUE
этой переменной разрешит нам создать и работать с контекстным меню, a face="Courier New" size=2>FALSE, соответственно, запретит.



Создание контекстного меню


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



Создайте обработчик сообщения size=2>WM_RBUTTONDOWN. He забудьте, что помимо самой функции необходимо
определить компоненты карты сообщений.


void
CChildWnd::OnRButtonDown(UINT nFlags, CPoint point)


{


// Создавать и отображать
контекстное меню будем только


//в том случае, если "получили
разрешение" от системного меню



if(((CMainFrame
*)GetParentFrame{))->m_bEnable)


{


// Поскольку мы создаем меню,
нам нужен соответствующий объект



CColorMenu menu;


// Создаем контекстное
меню


size=2>menu.CreatePopupMenu();


for(int i = 0; i < 16;
i++)


{


// Добавляем в него
элементы


menu.AppendMenu(MF_OWNERDRAW,
ID^CLRO + i, ""); 


}


// Для расположения
контекстного меню относительно экрана


 // необходимо
преобразовать положение курсора мыши


 // в экранные
координаты 


size=2>ClientToScreen(Spoint); 


// Выводим контекстное меню
на экран


size=2> menu.TrackPopupMenu(TPM__LEFTALIGN I TPM_RIGHTBUTTON,


point.x, point.у,
this); 


}


// Как всегда, не мешаем
нормальной работе


 
CWnd::OnRButtonDown(nFlags, point); 


}


Первое, что бросается в глаза — это появление нового класса face="Courier New" size=2>CColorMenu, о котором мы ничего не говорили. Да
и не могли ничего сказать, потому что только приступаем к его созданию. До сих
пор мы пользовались объектами класса size=2>СМеnu, и этого было вполне достаточно. Зачем же нам понадобился
еще один класс? Все очень просто — мы хотим взять на себя задачу отрисовки
элементов меню. Этим мы займемся несколько позднее, а пока будем считать, что
работаем с объектом класса СМеnu.


Итак, после того как объект соответствующего класса создан, можно создавать
собственно контекстное меню:



size=2>menu.CreatePopupMenu();


Однако пока в нем нет ни одного элемента. Чтобы исправить такое положение,
добавим их :



for(int i = 0; i < 16;
i++)


menu.AppendMenu(MF_OWNERDRAW,
ID_CLRQ + i, "");


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



size=2>ClientToScreen(Spoint);


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


Примечание 


Эта точка может соответствовать и левому нижнему углу
контекстного меню, но такое перестроение автоматически выполняет сама система
Windows.


После того как мы все подготовили, осталось только вывести созданное меню на
экран. Что и делает, как вы помните, функция



size=2>menu.TrackPopupMenu(TPM_LEFTALIGN I TPM_RIGHTBUTTON, 


point.x, point.у,
this);


Теперь выбор любого элемента нашего меню приведет к тому, что будет вызван
соответствующий обработчик, который, правда, мы еще не создали. Проделайте это
самостоятельно либо найдите его в полном тексте приложения face="Courier New" size=2>Graph, который имеется на сопроводительной
дискете. А мы будем двигаться дальше.



Самоотображение элементов меню


Для того чтобы создать меню, внешний вид которого отличается от стандартного
(например, кроме текста мы хотим вывести там еще и пиктограмму), нам необходимо
перехватить и обработать два сообщения: size=2>WM_MEASUREITEM и size=2>WM_DRAWITEM. Библиотека MFC уже подготовила все для того, чтобы
этот процесс не занял много времени, и реализовала в классе face="Courier New" size=2>СМеnu(и не только в нем) следующие две функции:


virtual void
CMenu::MaasureItem
(LPMEASUREITEMSTRUCT            
IpMeasureltemStruct)



Вызывается библиотекой MFC при создании
меню, имеющего стиль самоотображения (owner-draw). В качестве параметра в
функцию передается указатель на структуру MEASUREITEMSTRUCT, с помощью которой
можно настроить размеры меню.


virtual void
CMenu::DrawItem (LPDRAWITEMSTRUCT IpDrawItemStruct)



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


При использовании стиля самоотображения обе функции должны быть обязательно
переопределены, т. к. их реализация в классе size=2>СМеnu не содержит кода.


Таким образом, прежде всего нам необходимо создать свой класс, производный от
СМеnu. Рассмотрим один из возможных вариантов:


1. Раскроем окно мастера ClassWizard и
выполним команду New раскрывающейся
кнопки Add Class.


2. В появившемся диалоге введем имя создаваемого класса в поле face="Courier New" size=2>Name.


3. К сожалению, ClassWizard не
позволяет создавать класс на основе size=2>СМеnu. Поэтому поступаем следующим образом: в раскрывающемся
списке Base class выбираем какой-нибудь
класс, например, CButton (рис. 12.12).
Нажимаем кнопку ОК, затем еще раз face="Courier New" size=2>ОК — новый класс создан. Теперь нам надо внести
некоторые изменения.



............................................................12

Рис. 12.12. Воспользоваться size=2>ClassWizard можно даже в том случае, если в списке базовых классов
нет того, который нужен


4. В окне Workspace на вкладке face="Courier New" size=2>ClassView дважды щелкните на имени face="Courier New" size=2>CColorMenu, что приведет к открытию файла, где
этот класс определен. Замените имя size=2>CButton на СМеnu — сделать
это надо в одном единственном месте.


5. Раскройте вкладку FileView в окне
Workspace и дважды щелкните по имени
файла <colormenu.cpp>. В строке



BEGIN_MESSAGE_MAP(CColorMenu,
CButton)


замените CButton на face="Courier New" size=2>СМеnu. До этой строки можно "добраться" и
другим способом, я вам показываю только один из возможных вариантов.


6. Сохраните сделанные изменения. В результате мы получили собственный класс
CColorMenu, производный от face="Courier New" size=2>СМеnu. Кстати, в данном конкретном случае
можете удалить из определения и реализации класса (обязательно в обоих местах)
все компоненты, кроме конструктора, — они нам не понадобятся.


После того как класс, базирующийся на size=2>СМеnu, создан, нам необходимо переопределить две функции, которые
отвечают за обработку сообщений size=2>WM_MEASUREITEM и size=2>WM_DRAWITEM. Воспользуйтесь для этого мастером face="Courier New" size=2>ClassWizard. В результате вы должны получить
следующий код:


#if !defined(


size=2>AFX_COLORMENU_H_OC5A82D5_B46D_11D3_BAB9_00600864785A_INCLUDED_)


#define
FX_COLORMENU_H_OC5A82D5_B46DJL1D3_BAB9_00600864785A_INCLUDED_


#if _MSC_VER >=
1000 


#pragma once


#endif // _MSC_VER >=
1000 


// ColorMenuJh : header
file 


//


size=2>///////////////////////////////////////////////////////////////////


CColorMenu window


class CColorMenu : public
CMenu


{


// Construction


public:


CColorMenu(); 


// Implementation 


public:


virtual void
MeasureItem(LPMEASUREITEMSTRUCT IpMeasureltemStruct);


virtual void
Drawltern(LPDRAWITEMSTRUCT IpDrawItemStruct); };


#endif // !defined(


size=2>AFX_COLORMENU_H_OC5A82D5_B46D_11D3_BAB9_00600864785A__INCLUDED_)


  size=2>//////////////////////////////////////////////////////////////



ColorMenu.cpp :
implementation file


//


#include
"stdafx.h" 


#include "Graph.h"


#include
"ColorMenu.h" 


#ifdef _DEBUG 


#define new DEBUG_NEW


#undef.THIS_FILE


static char THIS_FILE[] =
_FILE_;


#endif


size=2>///////////////////////////////////////////////////////////////



CColorMenu


size=2>CColorMenu::CColorMenu()


{


}


size=2>/////////////////////////////////////////////////////////////////



CColorMenu message
handlers


void
CColorMenu::MeasureItem(


LPMEASUREITEMSTRUCT 
IpMeasureltemStruct)



}


void
CColorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct) 



}


size=2>//////////////////////////////////////////////////////////


 CColorMenu message
handlers


Нам осталось только наполнить содержанием созданные для нас оболочки функций
Drawltem и size=2>Measurement. Однако сначала рассмотрим использованные в них
.структуры DRAWITEMSTRUCT и face="Courier New" size=2>MEASUREITEMSTRUCT.


Структура DRAWITEMSTRUCT имеет
следующее определение:



typedef struct
tagDRAWITEMSTRUCT {


UINT CtlType;


UINT CtllD;


UINT iteralD;


UINT itemAction;


UINT itemState;


HWND hwndltem;


HOC hDC;


RECT rcltem;


DWORD itemData; 


} DRAWITEMSTRUCT;


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


ODT_BUTTON 



 Кнопка


ODT_COMBOBOX



 Комбинированный список


ODT_LISTBOX



  Список


ODT_MENU 



 Меню


CtllD— идентификатор кнопки, списка или
комбинированного списка, для меню этот параметр не используется; itemID —
идентификатор пункта меню или индекс записи в списке или комбинированном
списке. Для списка или комбинированного списка, не содержащих записей, этот
параметр должен иметь отрицательное значение, в результате чего будет выведен
прямоугольник с координатами, заданными в rcltem, и не содержащий никаких
элементов. itemAction — определяет, как должна осуществляться перерисовка. Он
может быть комбинацией следующих битов:


ODA_DRAWENTIRE



 Весь элемент управления требует
перерисовки


ODA_FOCUS 



Устанавливается, когда элемент управления
получает или теряет фокус ввода; поле itemState должно быть проверено для
определения, имеет ли элемент управления фокус ввода


ODA_SELECT 



Устанавливается, если сделан выбор; поле
itemState должно быть использовано для определения нового выбора itemState —
задает состояние записи после перерисовки:


ODS_DISABLED 



Запись недоступна


ODS_FOCUS 



Запись имеет фокус ввода


ODS_SELECTED



 Запись выбрана


hwndltem— дескриптор окна элемента
управления; hDC— контекст устройства вывода; rcltem — прямоугольник, задающий
границы перерисовки; itemData — для списка или комбинированного списка этот
параметр содержит значение, установленное функциями CComboBox::AddString,
CComboBox:;lnsertString, CListBox::AddString или CListBox::lnsertString, а для
меню— функциями СМепи:: AppendMenu, CMenu::lnsertMenu или
СМепи::ModifyMenu.


Структура MEASUREITEMSTRUCT имеет
следующий вид:



typedef struct
tagMEASUREITEMSTRUCT {


UINT CtlType;


UINT' CtllD;


UINT itemID;


UINT itemWidth;


UINT itemHeight;


DWORD itemData


  }
MEASUREITEMSTRUCT;


CtlType — содержит информацию о типе
элемента управления (см. описание структуры DRAWITEMSTRUCT); CtllD—
идентификатор элемента управления. itemID— идентификатор элемента в списке
(для списка и комбинированного списка) — не используется для списков и
комбинированных списков с фиксированной высотой элементов. itemWidth —
используется для задания ширины элемента в меню (для списков и комбинированных
списков не используется). itemHeight— задает высоту записи в списке
(максимальное значение равно 255). itemData— назначение этого поля аналогично
одноименному полю структуры DRAWITEMSTRUCT.


Теперь задаем размеры элементов меню. Для этой задачи предназначена функция
MeasureItern:



void
CCoiorMenu::MeasureItem(


LPMEASUREITEMSTRUCT
IpMeasureltemStruct)


 { 


size=2>lpMeasureItemStruct->iteraWidth = 130;


size=2>lpMeasure!temStruct->itemHeight = 20; 


}


Как вы понимаете, размеры вы можете задавать произвольные. Главное — сделать
это именно здесь.


Нам осталось только нарисовать в элементах меню то, ради чего, собственно,
все это и затевалось. Используем функцию:



void
CCoiorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct) 


{


// Определяем
прямоугольник,


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


CRect
rect(lpDrawItemStruct->rcItem.left, 


size=2>lpDraw!temStruct->rcItem.top,


size=2> lpDrawItemStruct->rcItem.right — 110, 


lpDrawItemStruct->rdtem.
bottom) ;


// При обработке сообщения
WM_DRAWITEM для нас уже создан


 // контекст устройства,
и нам остается им воспользоваться 


CDC *pDC =
CDC::FromHandle(lpDrawItemStruct->hDC);


 // Дальнейшие действия
выполняем только в случае, 


// если перерисовки требует
весь элемент меню


size=2> if(lpDraw!temStruct->itemAction & ODA_DRAWENTIRE)


 {


// По, идентификатору
элемента меню находим его цвет


 COLORREF cr =
colors[lpDraw!temStruct->itemID — ID_CLRO]; 


// Заполняем прямоугольник
выбранным цветом 


pDC->FillSolidRect(Srect,
cr) ;


//и отделяем его от
последующего текста


pDC->MoveTo(rect.right +
I, 0);


pDC->LineTo(rect.right +
1, rect.bottom);


// Выводимый текст не должен
создавать "пятна"


size=2>pDC->SetBkMode(TRANSPARENT);


// Выводим текстовое
обозначение цвета


pDC-XTextOut(rect.right + 10,
rect.top + 4,


size=2>clrString[lpDrawItemStruct->itemID - ID_CLRO]); 


}


}


Помимо этого не забудьте определить необходимые идентификаторы и тексты:



// MainFrm.cpp :
implementation of the CMainFrame class 


//


// Определяем массив
цветов


COLORREF colors!] =


{


RGB{ О, О, О),


RGB(255, 255, 255),


RGB(128, 128, 128),


RGB(192, 192, 192),


RGB(128, 0, 0),


RGB(255, О, О),


RGB(128, 128, 0),


RGB(255, 255, 0),


RGB( 0, 128, 0),


RGB( 0, 255, 0),


RGB( 0, 128, 128),


RGB( 0, 255, 255),


RGB( 0, 0, 128),


RGB! 0, 0, 255),


RGB(128, 0, 128),


RGB (255, 0, 255) );


// ColorMenu.cpp :
implementation file 


//


// Мы должны иметь доступ к
массиву цветов


 extern COLORREF
colors[]; 


// Определяем массив названий
цветов


 CString clrStringf]


{


"Черный",


"Белый",


"Темно-серый",


"Серый",


"Темно-красный",


"Красный",


"Темно-желтый",


"Желтый",


"Темно-зеленый",


"Зеленый",


"Темно-голубой",


"Голубой",


"Темно-синий",


"Синий",


"Темно-малиновый",


"Малиновый" 


};


// Resource.h


//


...


// Для каждого цвета
необходим свой идентификатор


#define
ID_CLRO             
32788


ttdefine
ID_CLR1            
32789


ttdefine
ID_CLR2            
32790


#define
ID_CLR3             
32791


ttdefine
ID_CLR4            
32792


ttdefine
ID_CLR5            
32793-


#define
ID_CLR6             
32794


#define
ID_CLR7             
32795


#define
ID_CLR8             
32796


#define
ID_CLR9             
32797


#define
ID_CLR10            
32798


#define
ID_CLR11            
32799


#define
ID_CLR12            
32800


#define
ID_CLR13            
32801


#define
ID_CLR14            
32802


#define
ID_CLR15            
32803


...


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



Создание собственных маркеров состояния


Последний пример, который мы рассмотрим в связи с меню, заключается в
реализации собственных маркеров, выводимых вместо "галочки".


Первое, что нам надо — это создать рисунки, которые будут показывать
отмеченное и неотмеченное состояния. Например, такие, как представленные на рис.
12.14. Я уже не буду объяснять, как это сделать.


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



class CMainFrame : public
CFrameWnd 


{


...


// Attributes


  public:


CBitmap m_bmpCheck; // для
отмеченного состояния


CBitmap m_bmpUnCheck; // для
неотмеченного состояния 


...


}



....................................................13

Рис. 12.13. Результат наших действий по рисованию элементов меню



...................................................14

Рис. 12.14. Так будет выглядеть "галочка" у отмеченного элемента меню


Затем добавляем код в обработчик события size=2>WM_CREATE:



int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct)


{


// Загружаем битовые массивы
с новыми маркерами


size=2>m_bmpCheck.LoadBitmap(IDB_MENU_DOWN);


size=2>m_bmpUnCheck.LoadBitmap(IDB_MENU_UP);


// Получаем системные размеры
маркера


int ex =
::GetSystemMetrics(SM_CXMENUCHECK);


int cy =
::GetSystemMetrics(SM_CYMENUCHECK);


// Приводим текущие размеры
битовых массивов к системным


size=2>m_bmpUnCheck.SetBitmapDimension(сх, су);


size=2>m_bmpCheck.SetBitmapDimension(ex, cy);


// Получаем доступ к главному
меню


CMenu *pViewMenu =
NULL;


CMenu *pTopMenu =
AfxGetMainWnd()->GetMenu();


int iPos;


// Ищем элемент меню "2 column"
по идентификатору IDjroOLS_2COLUMN



for (iPos =
pTopMenu->GetMenu!temCount0-1; iPos >= 0; iPos—)


{


// Поиск выполняем по позиции
элемента


CMenu* pMenu =
pTopMenu->GetSubMenu(iPos);


if (pMenu &&
pMenu->GetMenu!temID(0) == ID_TOOLS_2COLUMN)


1


// Если соответствующий
идентификатор найден,


// прекращаем поиск, запомнив
указатель на этот элемент


pViewMenu = pMenu;


break;



}


// Мы обязательно должны
найти элемент меню 


 ASSERT(pViewMenu !=
NULL);


// Устанавливаем новые
маркеры для отмеченного и


// неотмеченного
состояний,


// которые теперь будут
появляться вместо "галочек"


size=2>pViewMenu->SetMenuItemBitmaps(IDJTOOLS_2COLUMN, MF_BYCOMMAND,


&m_bmpUnCheck,
&m_bmpCheck);


size=2>pViewMenu->SetMenuItemBitmaps(ID_TOOLS_3COLUMN, MF_BYCOMMAND,


&m_bmpUnCheck,
&m_bmpCheck);


...


}


И вот что у нас получилось в результате (рис. 12.15).


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



..............................................15

Рис. 12.15. Так выглядят новые маркеры отмеченного и неотмеченного
состояний


По материаллам сайта: www.realcoding.net

16.02.2006

Телефон

+7 8636 237-836

Поиск

VSESMI.ru — новости в СМИ.
Один из больших по объему информации проектов, работающих под управлением HostCMS.

Tur-Hotel.ru — отзывы об отелях
На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.