Глава 12.
В глубине меню
Чем хуже готовят повара,
тем вежливей должны быть официанты.
Михаил Генин
Что такое меню, как оно создается и как с ним работать? Начальные сведения
уже были приведены в главе 4, здесь же мы постараемся осветить эти вопросы более
подробно. Однако, прежде чем переходить к изучению соответствующих средств
Windows и библиотеки MFC, необходимо рассмотреть стандартные типы меню, которые
можно создавать в приложениях Windows, и их основные характеристики.
Основные типы меню
Меню обычно располагается под полосой заголовка в верхней части
перекрывающегося или всплывающего окна и представляет собой словесные или
символические опции, дающие возможность манипулировать особенностями программы.
Такое меню называется Главным меню приложения (верхний уровень иерархии).
Отдельные элементы меню располагаются в полосе меню (menu bar) — рис. 12.1 и 12.2.
Элементы меню могут иметь различные типы и содержать ряд модификаторов (рис.
12.3):
- командная клавиша — вместо выбора пункта меню команду можно вызвать
с помощью этой клавиши;
- стрелка — показывает, что при выборе данного элемента появится
подменю;
- разделительная черта — отделяет одну группу элементов меню от
другой;
- маркер — означает, что данный параметр меню выбран;
- многоточие — сообщает пользователю о том, что при выборе данного
элемента на экран будет выведен блок диалога;
- полутоновая надпись — говорит, что в текущем состоянии данный
элемент меню недоступен;
- знак подчеркивания — означает, что данный элемент меню можно вызвать
с помощью командной клавиши (комбинации клавиш), куда входит эта буква. Такие
буквы называются мнемоническими.
Рис. 12.1. Заголовок и Главное меню приложения WordPad
Рис. 12.2. Раскрытое меню пункта "Вид"
Рис. 12.3. Команда, имеющая подменю
Каждое окно, имеющее заголовок, может иметь системное меню, которое
вызывается щелчком левой кнопки мыши на пиктограмме, расположенной в левой части
заголовка окна (рис. 12.4), и представляет собой набор стандартных команд. В
Windows 95 системное меню появляется, если щелкнуть правой кнопкой мыши в полосе
заголовка или на пиктограмме свернутого окна.
Рис. 12.4. Системное меню
Система Windows предоставляет специальный тип меню, которое можно создавать в
любом месте экрана. Такое меню называется контекстным или плавающим (floating popup menu) и не привязано к полосе
меню (рис. 12.5)'. Оно ассоциируется с некоторой областью окна или объектом,
например, пиктограммой. Контекстное меню в некоторых случаях удобнее других, т.
к. его содержимое зависит от того, для какого объекта оно было создано. Обычно
оно выводится на экран щелчком правой кнопки мыши.
Рис. 12.5. Контекстное меню рабочей области окна приложения WordPad
Теперь, перечислив основные типы используемых в Windows меню, рассмотрим те
возможности, которые предоставляет библиотека MFC для работы с ними.
Точно так же, как в Win32 API есть функции, специально предназначенные для
работы с меню, в библиотеке MFC есть для них адекватные альтернативы,
реализованные в виде класса СМenu (рис.
12.6), полностью вобравшего в себя функции API.
Рис. 12.6. Место класса СМenu в
иерархии библиотеки MFC
Класс СМenu инкапсулирует
объект Windows, определяемый дескриптором 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 ()
Удаляет меню перед завершением работы
приложения и возвращает системе занимаемые им ресурсы.
Для изменения состава меню, созданного функцией CreateMenu, в классе предусмотрена специальная группа
функций.
Добавление элементов в меню производится функциями 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 принимает одно из
следующих значений:
nFlags
IpszNewltem
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:
nFlags
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, то размеры и расположение этой прямоугольной
области будут совпадать с размерами контекстного меню.
В классе представлены две виртуальные функции, которые произвольным образом
позволяют изменять внешний вид меню: Measureltem и Drawltem. Но
они заслуживают отдельного разговора и мы к ним вернемся в разделе
"Самоотображение элементов меню" данной главы.
Теперь, после теоретического знакомства с возможностями, заложенными в классе
СМеnu, можно переходить к практическим
аспектам и тонкостям программирования при построении и работе с меню.
В системе Windows реализованы три способа создания меню:
1. На основе шаблона, задаваемого в файле ресурса.
2. Динамическое создание при помощи специальных функций.
3. На основе шаблона в оперативной памяти. Рассмотрим каждый из этих способов
более подробно.
Создание меню на основе шаблона
Этот подход мы уже рассматривали и поэтому не будем останавливаться на нем
более подробно, а приведем только описание атрибутов, определяющих внешний вид и
поведение строки меню (рис. 12.7, табл. 12.1).
Рис. 12.7. Атрибуты меню в окне свойств, открытом в режиме настройки
параметров меню
Таблица 12.1. Описание атрибутов, определяющих внешний вид меню
|
|
|
|
|
Элемент представляет собой горизонтальную
разделительную линию, служащую для разбиения элементов меню на
группы
|
|
|
При выводе на экран элемент меню отмечается
слева галочкой
|
|
|
Элемент определяет
подменю
|
|
|
Элемент отображается серым цветом и находится в
заблокированном состоянии; несовместим с атрибутом
Inactive
|
|
|
Элемент выравнивается по правому краю полосы
меню
|
|
|
Этот атрибут может принимать одно из трех
значений: None — обычный элемент меню; Column — для меню
верхнего уровня элемент выводится с новой строки, а для подменю — в новом
столбце; Ваг— дополнительный столбец подменю отделяется
вертикальной линией.
|
|
|
Идентификатор элемента
меню
|
|
|
|
|
|
Текст, определяющий имя элемента меню;
текстовая строка может содержать символы: & — буква, перед которой
стоит этот знак, будет подчеркнута; t — включает в строку символ
табуляции; а — выравнивает текст по правой границе
меню
|
|
|
Текст, который будет отображаться в строке
состояния и (после п) в окне всплывающей
подсказки
|
|
|
Элемент находится в заблокированном состоянии,
но отображается обычным цветом
|
После того как создан шаблон, необходимо подключить меню к окну приложения.
Этот вопрос также достаточно подробно рассматривался, поэтому только перечислим
те места, где это можно сделать.
- При регистрации класса окна — когда меню определяется для нескольких
окон одного класса:
wc.lpszMenuName =
"IDR_MAINFRAME";
- При создании окна — если для него необходимо меню, отличное от
указанного в классе окна:
if
(!pMainFrame->LoadFrame(IDR_MAINFFLAME))
return FALSE;
- При работе в рамках архитектуры "документ/представление" —
достаточно, чтобы идентификатор меню совпадал с идентификаторами фрейма,
документа и представления. Тогда вопросами подключения меню полностью
занимается библиотека классов MFC при создании шаблона документа:
pDocTemplate = new
CMultiDocTemplate(
IDR_NOTETYPE, //
идентификатор меню
RUNTIME_CLASS(CNoteDoc),
RUNTIME_CLASS(CNoteFrame),
RUNTIME_CLASS(CNoteView));
Примечание
Мы обязательно рассмотрим архитектуру
"документ/представление" более подробно.
При использовании любого из перечисленных способов подключения меню к окну
нет необходимости уничтожать меню перед завершением работы приложения, т. к.
библиотека MFC делает это сама.
Меню, созданное в системе Windows, где взаимодействие между объектами
осуществляется при помощи сообщений, также работает по этому принципу. Наиболее
важным из сообщений, которое передает меню, является WM_COMMAND, означающее, что выбран тот или иной элемент меню. В
табл. 12.2 приведены этапы работы с меню и возникающие при этом сообщения.
Таблица 12.2. Этапы работы с меню и возникающие при этом сообщения
|
|
|
|
|
|
|
|
|
Вывод всплывающего меню на
экран
|
|
|
|
Инициализация и вывод на экран всплывающего
меню
|
WM INITMENU и WM
INITMENUPOPUP
|
OnlnitMenu и
OnlnitMenuPopup
|
|
Нахождение нужного элемента
меню
|
|
|
|
|
|
|
|
Выбор элемента системного
меню
|
|
|
Рассмотрим кратко роль перечисленных сообщений.
Примечание
Действие приведенных обработчиков справедливы и для двух
других способов создания меню.
Когда пользователь активизирует элемент системного меню, сначала вызывается
обработчик сообщения WM_SYSCOMMAND:
void
CWnd::OnSysCommand(
UINT nID, LPARAM
IParam)
Первый параметр nID может содержать
идентификатор строки системного меню, а также некоторые другие
значения:
SC_CLOSE
Удаление окна
SC_HOTKEY
Активизация определенным приложением окна,
связанного с командной клавишей
SC_HSCROLL
Прокрутка по горизонтали
SC_KEYMENU
Выбор элемента меню при помощи командной
клавиши
SC_MAXIMIZE или 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:
nID
IParam
SC_HOTKEY
Содержит идентификатор
активизируемого окна
SC_MOUSEMOVE
Младшее слово содержит х-координату,
а старшее — у-координату
Перед отображением всплывающего меню (один раз, при активизации) система
Windows посылает сообщение WM_INITMENU с
тем, чтобы приложение могло изменить меню перед тем, как его увидит
пользователь. При этом библиотека MFC вызывает обработчик:
void CWnd:lOnlnitMenu
(CMenu *pMenu)
Параметр рМепи задает указатель на объект
СМеnu, определяющий активизируемое меню. Обработка может заключаться в
активизации или деактивизации строк меню, изменении их состояния и т.
п.
Когда пользователь выбирает элемент, имеющий подменю, система Windows, прежде
чем отобразить всплывающее меню, посылает владельцу сообщение WM_INITMENUPOPUP. Библиотека MFC вызывает
обработчик, который дает приложению возможность изменить всплывающее меню перед
его отображением:
void
CWnd::OnlnitMenuPopup (
CMenu pPopupMenu,
DINT nlndex,
BOOL bSubMenu)
Параметр рРорuрМеnu— указатель на объект
"всплывающее меню"; nID— индекс всплывающего меню в меню верхнего уровня;
bSubMenu— определяет, является ли всплывающее меню системным (TRUE) или
нет.
Каждый раз, когда пользователь перемещается от одного элемента меню к
другому, Windows посылает сообщение 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:
nFlags hSubMenu
MF_SYSMENU
Идентифицирует меню, ассоциированное с
сообщением
MF_POPUP
Идентифицирует дескриптор меню верхнего
уровня
Многие приложения используют сообщение WM_MENUSELECT для отображения дополнительной информации о
выбранном элементе меню в строке состояния.
Случай, когда пользователь выбирает элемент меню, a Windows посылает
сообщение WM_COMMAND, уже не раз
описывался в предыдущих примерах — необходимо либо воспользоваться одним из
предопределенных обработчиков стандартных команд меню, либо создать свой,
например, так, как было продемонстрировано в главе 11.
С учетом новых сведений покажем, как можно организовать "переключение"
количества столбцов панели инструментов рисования в нашем приложении Graph:
1. Добавить в уже имеющуюся полосу меню элемент Tools, а в качестве его
подпунктов — "2 column" и "3 column" (рис. 12.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,
OnUpdateToolsCo.lumn)
END_MESSAGE_MAP()
3. В описание класса CMainFrame
добавить определение обработчиков:
//{{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;
// Устанавливаем столбцы на
панели инструментов рисования
SetColumns(mJnPaneCol);
}
void
CMainFrame::OnUpdateToolsColumn(CCmdUI* pCmdUI)
{
// Определяем выбранный пункт
меню
UINT nCol = (pCmdUI->m_nID
== ID_TOOLS_2COLUMN) ? 2 : 3;
// Устанавливаем маркер
состояния — галочку у выбранного
// пункта меню и сбрасываем у
остальных
if(m_nPaneCol ==
nCol)
pCmdUI->SetCheck(TRUE);
else
pCmdUI->SetCheck(FALSE);
}
Как видите, все довольно просто и, на мой взгляд, нет надобности больше
задерживаться на этом вопросе: ведь при использовании шаблонов подавляющую часть
работы выполняет операционная система, а все действия, необходимые для работы с
меню, представляют относительную сложность только при построении самого первого
меню.
Поэтому мы переходим к практическим примерам. Как вы помните, мы еще не
закончили наше приложение Graph. Здесь мы добавим в него возможность выбора
цвета как для рисования линий, так и для заливки. Но сделаем это, как и
договаривались, не общепринятым способом, а таким, который позволит
проиллюстрировать максимум возможностей работы с меню.
Добавление элемента в системное меню
Итак, мы создаем контекстное меню, появляющееся при нажатии правой кнопки
мыши в клиентской области окна и позволяющее выбрать цвет заливки (да и текущий
цвет линий). Одновременно в системное меню добавим пункт, разрешающий работу с
этим контекстным меню (рис. 12.9).
int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct)
{
// Получаем доступ к
системному меню
CMenu* pSysMenu =
GetSystemMenu(FALSE);
// Определяем текст, которым
будет представлен
// созданный элемент
системного меню
CString
strColorMenu("Разрешить смену текущего цвета");
// Добавляем в него
разделитель и новый элемент
// "Разблокировать меню
Цвет"
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_BYCOMMAND I MF_STRING,
IDM_COLOR,
StrColorMenu);
...
}
Рис. 12.9. Добавляем новый пункт в системное меню
Давайте коротко разберем наши действия. Прежде всего необходимо получить
доступ к системному меню, что мы и делаем с помощью функции
CMenu*
CWnd::GetSystemMenu(BOOL bRevert)
Позволяет получить идентификатор копии
системного меню, так что с ним можно проводить любые допустимые манипуляции.
При добавлении своего элемента в системное меню необходимо учитывать, что его
идентификаторы имеют номера, начинающиеся с OxFOOO. Поэтому собственный
идентификатор должен иметь номер, меньший этого значения. Кроме того, в
качестве последней цифры шестнадцатеричного представления номера команды
рекомендуется использовать 0. Параметр bRevert задает режим работы функции:
если он равен FALSE, можно выполнять модификацию системного меню. Значение
TRUE "возвращает" системное меню в состояние, приписанное ему по
умолчанию.
Следующая строка кода
CString
strColorMenu("Разрешить смену текущего цвета");
задает текст, который должен появиться в системном меню. Хотя его длина не
оговаривается, все же не надо делать его слишком длинным. Но в любом случае
система Windows сама "подгонит" размеры этого меню под максимальный текст.
Нам осталось только добавить те элементы меню, которые нам необходимы. Это,
безусловно, разделитель и собственно текст нашего меню. Выполняется это очень
просто с помощью функции CMenu::AppendMenu.
Здесь необходимы некоторые пояснения. Как вы помните, при создании меню с
помощью редактора ресурсов мы обязательно должны были присвоить каждому его
элементу идентификатор, который является ничем иным, как некоторым целым числом.
Так вот, редактор ресурсов сам назначает числовой эквивалент введенному
идентификатору. Совсем по-другому обстоит дело в данном случае. При динамическом
добавлении элемента меню мы самостоятельно должны сопоставить идентификатор с
некоторым цифровым эквивалентом. Сделать это можно, непосредственно редактируя
файл <resource.h>, а можно с
помощью IDE. Именно такой подход мы
сейчас и рассмотрим:
1. Выберите пункт меню View | Resource
symbols и на экране появится одноименный диалог (рис. 12.10), в котором
вы найдете все имеющиеся идентификаторы.
Рис. 12.10. Здесь можно получить информацию по имеющимся
идентификаторам
2. Нажмите кнопку New, чтобы создать
новый идентификатор. На экране появится диалог New Symbol.
3. В поле Name введите обозначение
идентификатора, а в поле Value — его
цифровое значение (рис. 12.11).
4. Нажмите последовательно кнопки ОК и
Close в соответствующих диалогах — новый
идентификатор создан.
Рис. 12.11. Создаем новый идентификатор
Вот, собственно, и все. Теперь можно смело вставлять новый элемент в
системное меню и создавать для него соответствующий обработчик.
Примечание
Некоторые из представленных в блоке диалога Resource
Symbols идентификаторы не снабжены галочкой в столбце In Use. И в поле Used by
для них выводится загадочная надпись "not used". Естественно, это не означает,
что данный идентификатор не нужен. Просто ClassWizard о нем ничего не
знает.
Обработка команд системного меню несколько отличается от стандартной, и об
этом необходимо сказать несколько слов. Я уже отмечал, что когда пользователь
выбирает элемент системного меню, вызывается обработчик сообщения WM_SYSCOMMAND. Следовательно, наша задача —
реализовать этот обработчик. Очевидно, что делать это мы будем с помощью мастера
ClassWizard, который создаст необходимые
компоненты карты сообщений и заготовку обработчика:
class CMainFrame : public
CFrameWnd
{
...
public:
BOOL m__bEnable; // индикатор
включения контекстного меню
protected:
//{{AFX_MSG(CMainFrame)
afx_msg void
OnSysCommand(UINT nID, LPARAM IParam);
//}}AFX_MSG
...
};
...
BEGIN_MESSAGE_MAP(CMainFrame,
CFrameWnd)
/ / {{AFX_MSG_MAP
(CMainFrame)
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)
{
// сбрасываем маркер,
...
pSysMenu->CheckMenuItem(IDM_COLOR,
MF_BYCOMMAND |
MF_UNCHECKED);
// разрешаем работу с
контекстным меню mjDEnable = FALSE;
}
else //и
наоборот
{
pSysMenu->CheckMenu!tem(IDM_COLOR, MF_BYCOMMAND | MF_CHECKED);
// запрещаем работу с
контекстным меню m_bEnable = TRUE;
}
}
else
{
// Если команда "не наша",
необходимо передать ее
// библиотеке для корректной
обработки
CWnd::OnSysCommand(nID,
IParam);
}
}
В приведенном фрагменте обратите внимание на строчку
BOOL m_bEnable; // индикатор
включения контекстного меню
которая включена для того, чтобы как-то оправдать задачу создания новой
команды системного меню. Значение TRUE
этой переменной разрешит нам создать и работать с контекстным меню, a FALSE, соответственно, запретит.
Создание контекстного меню
Для решения поставленной задачи нам осталось выбрать команду, по которой
будет создаваться контекстное меню, и реализовать ее обработчик. Обычно
контекстные меню выводятся на экран при нажатии на правую кнопку мыши. Мы не
будем отступать от этого правила.
Создайте обработчик сообщения WM_RBUTTONDOWN. He забудьте, что помимо самой функции необходимо
определить компоненты карты сообщений.
void
CChildWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
// Создавать и отображать
контекстное меню будем только
//в том случае, если "получили
разрешение" от системного меню
if(((CMainFrame
*)GetParentFrame{))->m_bEnable)
{
// Поскольку мы создаем меню,
нам нужен соответствующий объект
CColorMenu menu;
// Создаем контекстное
меню
menu.CreatePopupMenu();
for(int i = 0; i < 16;
i++)
{
// Добавляем в него
элементы
menu.AppendMenu(MF_OWNERDRAW,
ID^CLRO + i, "");
}
// Для расположения
контекстного меню относительно экрана
// необходимо
преобразовать положение курсора мыши
// в экранные
координаты
ClientToScreen(Spoint);
// Выводим контекстное меню
на экран
menu.TrackPopupMenu(TPM__LEFTALIGN I TPM_RIGHTBUTTON,
point.x, point.у,
this);
}
// Как всегда, не мешаем
нормальной работе
CWnd::OnRButtonDown(nFlags, point);
}
Первое, что бросается в глаза — это появление нового класса CColorMenu, о котором мы ничего не говорили. Да
и не могли ничего сказать, потому что только приступаем к его созданию. До сих
пор мы пользовались объектами класса СМеnu, и этого было вполне достаточно. Зачем же нам понадобился
еще один класс? Все очень просто — мы хотим взять на себя задачу отрисовки
элементов меню. Этим мы займемся несколько позднее, а пока будем считать, что
работаем с объектом класса СМеnu.
Итак, после того как объект соответствующего класса создан, можно создавать
собственно контекстное меню:
menu.CreatePopupMenu();
Однако пока в нем нет ни одного элемента. Чтобы исправить такое положение,
добавим их :
for(int i = 0; i < 16;
i++)
menu.AppendMenu(MF_OWNERDRAW,
ID_CLRQ + i, "");
Следующим шагом является преобразование положения курсора мыши в экранные
координаты
ClientToScreen(Spoint);
чтобы место, где мы щелкнули правой кнопкой мыши, соответствовало левому
верхнему углу нашего контекстного меню. Если мы этого не сделаем, все будет
работать по-прежнему, но выглядеть не очень аккуратно.
Примечание
Эта точка может соответствовать и левому нижнему углу
контекстного меню, но такое перестроение автоматически выполняет сама система
Windows.
После того как мы все подготовили, осталось только вывести созданное меню на
экран. Что и делает, как вы помните, функция
menu.TrackPopupMenu(TPM_LEFTALIGN I TPM_RIGHTBUTTON,
point.x, point.у,
this);
Теперь выбор любого элемента нашего меню приведет к тому, что будет вызван
соответствующий обработчик, который, правда, мы еще не создали. Проделайте это
самостоятельно либо найдите его в полном тексте приложения Graph, который имеется на сопроводительной
дискете. А мы будем двигаться дальше.
Самоотображение элементов меню
Для того чтобы создать меню, внешний вид которого отличается от стандартного
(например, кроме текста мы хотим вывести там еще и пиктограмму), нам необходимо
перехватить и обработать два сообщения: WM_MEASUREITEM и WM_DRAWITEM. Библиотека MFC уже подготовила все для того, чтобы
этот процесс не занял много времени, и реализовала в классе СМеnu(и не только в нем) следующие две функции:
virtual void
CMenu::MaasureItem
(LPMEASUREITEMSTRUCT
IpMeasureltemStruct)
Вызывается библиотекой MFC при создании
меню, имеющего стиль самоотображения (owner-draw). В качестве параметра в
функцию передается указатель на структуру MEASUREITEMSTRUCT, с помощью которой
можно настроить размеры меню.
virtual void
CMenu::DrawItem (LPDRAWITEMSTRUCT IpDrawItemStruct)
Вызывается библиотекой MFC при создании
меню, имеющего стиль самоотображения. В качестве параметра в функцию
передается указатель на структуру DRAWITEMSTRUCT, которая содержит информацию
для рисования элемента меню.
При использовании стиля самоотображения обе функции должны быть обязательно
переопределены, т. к. их реализация в классе СМеnu не содержит кода.
Таким образом, прежде всего нам необходимо создать свой класс, производный от
СМеnu. Рассмотрим один из возможных вариантов:
1. Раскроем окно мастера ClassWizard и
выполним команду New раскрывающейся
кнопки Add Class.
2. В появившемся диалоге введем имя создаваемого класса в поле Name.
3. К сожалению, ClassWizard не
позволяет создавать класс на основе СМеnu. Поэтому поступаем следующим образом: в раскрывающемся
списке Base class выбираем какой-нибудь
класс, например, CButton (рис. 12.12).
Нажимаем кнопку ОК, затем еще раз ОК — новый класс создан. Теперь нам надо внести
некоторые изменения.
Рис. 12.12. Воспользоваться ClassWizard можно даже в том случае, если в списке базовых классов
нет того, который нужен
4. В окне Workspace на вкладке ClassView дважды щелкните на имени CColorMenu, что приведет к открытию файла, где
этот класс определен. Замените имя CButton на СМеnu — сделать
это надо в одном единственном месте.
5. Раскройте вкладку FileView в окне
Workspace и дважды щелкните по имени
файла <colormenu.cpp>. В строке
BEGIN_MESSAGE_MAP(CColorMenu,
CButton)
замените CButton на СМеnu. До этой строки можно "добраться" и
другим способом, я вам показываю только один из возможных вариантов.
6. Сохраните сделанные изменения. В результате мы получили собственный класс
CColorMenu, производный от СМеnu. Кстати, в данном конкретном случае
можете удалить из определения и реализации класса (обязательно в обоих местах)
все компоненты, кроме конструктора, — они нам не понадобятся.
После того как класс, базирующийся на СМеnu, создан, нам необходимо переопределить две функции, которые
отвечают за обработку сообщений WM_MEASUREITEM и WM_DRAWITEM. Воспользуйтесь для этого мастером ClassWizard. В результате вы должны получить
следующий код:
#if !defined(
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
//
///////////////////////////////////////////////////////////////////
CColorMenu window
class CColorMenu : public
CMenu
{
// Construction
public:
CColorMenu();
// Implementation
public:
virtual void
MeasureItem(LPMEASUREITEMSTRUCT IpMeasureltemStruct);
virtual void
Drawltern(LPDRAWITEMSTRUCT IpDrawItemStruct); };
#endif // !defined(
AFX_COLORMENU_H_OC5A82D5_B46D_11D3_BAB9_00600864785A__INCLUDED_)
//////////////////////////////////////////////////////////////
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
///////////////////////////////////////////////////////////////
CColorMenu
CColorMenu::CColorMenu()
{
}
/////////////////////////////////////////////////////////////////
CColorMenu message
handlers
void
CColorMenu::MeasureItem(
LPMEASUREITEMSTRUCT
IpMeasureltemStruct)
{
}
void
CColorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct)
{
}
//////////////////////////////////////////////////////////
CColorMenu message
handlers
Нам осталось только наполнить содержанием созданные для нас оболочки функций
Drawltem и Measurement. Однако сначала рассмотрим использованные в них
.структуры DRAWITEMSTRUCT и 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)
{
lpMeasureItemStruct->iteraWidth = 130;
lpMeasure!temStruct->itemHeight = 20;
}
Как вы понимаете, размеры вы можете задавать произвольные. Главное — сделать
это именно здесь.
Нам осталось только нарисовать в элементах меню то, ради чего, собственно,
все это и затевалось. Используем функцию:
void
CCoiorMenu::DrawItem(LPDRAWITEMSTRUCT IpDrawItemStruct)
{
// Определяем
прямоугольник,
// который будем заполнять
соответствующим цветом
CRect
rect(lpDrawItemStruct->rcItem.left,
lpDraw!temStruct->rcItem.top,
lpDrawItemStruct->rcItem.right — 110,
lpDrawItemStruct->rdtem.
bottom) ;
// При обработке сообщения
WM_DRAWITEM для нас уже создан
// контекст устройства,
и нам остается им воспользоваться
CDC *pDC =
CDC::FromHandle(lpDrawItemStruct->hDC);
// Дальнейшие действия
выполняем только в случае,
// если перерисовки требует
весь элемент меню
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);
// Выводимый текст не должен
создавать "пятна"
pDC->SetBkMode(TRANSPARENT);
// Выводим текстовое
обозначение цвета
pDC-XTextOut(rect.right + 10,
rect.top + 4,
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; // для
неотмеченного состояния
...
}
Рис. 12.13. Результат наших действий по рисованию элементов меню
Рис. 12.14. Так будет выглядеть "галочка" у отмеченного элемента меню
Затем добавляем код в обработчик события WM_CREATE:
int
CMainFrame::OnCreate(LPCREATESTRUCT IpCreateStruct)
{
// Загружаем битовые массивы
с новыми маркерами
m_bmpCheck.LoadBitmap(IDB_MENU_DOWN);
m_bmpUnCheck.LoadBitmap(IDB_MENU_UP);
// Получаем системные размеры
маркера
int ex =
::GetSystemMetrics(SM_CXMENUCHECK);
int cy =
::GetSystemMetrics(SM_CYMENUCHECK);
// Приводим текущие размеры
битовых массивов к системным
m_bmpUnCheck.SetBitmapDimension(сх, су);
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
// Если соответствующий
идентификатор найден,
// прекращаем поиск, з