Вы находитесь на странице: 1из 7

Лекция 10 Меню 1

ЛЕКЦИЯ 10
СОЗДАНИЕ МЕНЮ ______________________________________________________________________ 1
Основные типы меню____________________________________________________________________ 1
Создание меню с помощью редактора меню ________________________________________________ 1
Атрибуты элементов меню _______________________________________________________________ 2
Подключение меню к окну приложения _____________________________________________________ 3
Файл ресурсов _________________________________________________________________________ 3
Создание обработчика событий для элемента меню __________________________________________ 4
Создание акселераторов (клавиш быстрого вызова) __________________________________________ 5
Загрузка акселераторов _________________________________________________________________ 6

МЕНЮ
Ничто не ценится так дорого пользователем и не стоит так
дешево программисту, как меню.
Мигель Сервантес
Одной из основных проблем разработки пользовательского интерфейса любой программы является
создание средства быстрого и удобного доступа ко всем командам, поддерживаемым данным приложени-
ем. Пользователи уже успели привыкнуть к тому, что для запуска большинства команд нужно выполнить
несколько щелчков мышью. Кроме того, они хотят, чтобы все команды были сосредоточены в одном мес-
те.
Чтобы удовлетворить все эти требования, в Visual Studio предусмотрено специальное средство – ре-
дактор меню, с помощью которого можно быстро и удобно создать структуру меню разрабатываемой про-
граммы. Данное средство можно использовать и для создания строки меню для любой формы вашего
приложения, а также контекстных (или всплывающих) меню. Как правило, все команды сгруппированы по
логическим функциям (работа с файлами, редактирование, изменение режимов просмотра объектов и
др.).

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


Обычно меню располагается в верхней части окна под полосой заголовка, и представляет собой сло-
весные или символические элементы меню. Назначение элементов меню – выполнять определенные ко-
манды, путем посылки соответствующих сообщений в очередь приложения. Меню, расположенное под
полосой заголовка окна, называется главным меню приложения.
Элементы меню могут иметь различные типы и содержать ряд модификаторов:
Акселератор (клавиша вызова) сообщает пользователю, что вместо вызова меню можно
вызвать команду с помощью акселератора;
Стрелка показывает, что при выборе данного элемента, появится подменю;
Разделительная черта отделяет одну группу элементов от другой;
Маркер означает, что данный параметр меню выбран;
Многоточие сообщает пользователю о том, что при выборе данного элемента на экран
будет выведен блок диалога;
Полутоновая надпись говорит, что в текущем состоянии данный элемент меню недоступен;
Знак подчеркивания означает, что данный элемент меню можно вызвать с помощью горячей
клавиши, которая включает эту букву. Такие буквы называются
мнемоническими.
Каждое окно, имеющее заголовок, может иметь системное меню, которое вызывается щелчком ле-
вой кнопки мыши на пиктограмме, расположенной в левой части заголовка окна, и представляет собой ряд
стандартных команд. В Windows системное меню появляется, если щелкнуть правой кнопкой мыши в по-
лосе головка или на пиктограмме свернутого окна.
Windows предоставляет специальный тип меню, которое можно создавать в любом месте экрана. Та-
кое меню называется контекстным или плавающим.

Создание меню с помощью редактора меню


Когда вы создаете одно или многодокументное приложение с помощью Application Wizard, то он авто-
матически добавляет в файл ресурсов стандартное меню. Если же его там нет или вы хотите добавить
еще одно, то надо:
в главном меню Visual Studio выбрать меню Insert ► Resource... ;

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 2
в диалоговом окне Insert Resource выберите пункт Menu и нажмите кнопку New.

В файл ресурсов автоматически добавится пустое меню. Чтоб сконструировать нужный вид меню,
надо перейти на закладку ResourceView окна Workspace. Раскройте дерево ресурсов, а затем раскройте
папку Menu.
В контекстном меню идентификатора меню выберите пункт Properties и в диалоговом окне Menu
Properties:
выберите русский язык в качестве языка интерфейса;
измените идентификатор ресурса (в качестве префикса обычно используют IDR_ – IDentifier of Re-
source).

Дважды щелкните по строке идентификатора меню. Автоматически запустится редактор меню


Menu.rc:

Дважды щелкните на пустом прямоугольнике – появится окно атрибутов меню:

Изменить последовательность команд в меню можно путём перетаскивания строк в окне редактора
меню.

Атрибуты элементов меню


Атрибуты, определяющие внешний вид и поведение строки меню, приведены ниже.
Separator горизонтальная разделительная линия для разбиения пунктов меню на группы;
Checked при выводе на экран элемент меню отмечается слева галочкой;
Pop-up элемент определяет подменю;
Grayed элемент отображается серым цветом и находится в заблокированном состоянии; несо-
вместим с атрибутом Inactive;

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 3
Help элемент выравнивается по правому краю меню
Break этот атрибут может принимать одно из трех значений:
None обычный элемент меню;
Column для меню верхнего уровня элемент выводится с новой строки,
для подменю – в новом столбце;
Ваг дополнительный столбец подменю отделяется вертикальной линией
ID идентификатор элемента меню;
Caption текст, определяющий имя элемента меню; текстовая строка может содержать символы:
& буква, перед которой стоит этот символ, будет подчеркнута;
\t включает в строку символ табуляции;
\а выравнивает текст по правой границе меню;
Prompt текст, который будет отображаться в строке состояния и после знака \n в окне всплы-
вающей подсказки;
Inactive Элемент находится в заблокированном состоянии, но отображается обычным цветом
Пункты меню верхнего уровня предназначены только для отображения соответствующих подменю и,
как правило, не инициируют какие-либо события. Поэтому для них нет необходимости резервировать
идентификатор. Кроме того, меню верхнего уровня не могут содержать горизонтальную разделительную
линию (Separator).

Подключение меню к окну приложения


Для подключения меню к окну приложения нужно:
открыть диалоговое окно свойств диалогового окна Dialog Properties, к которому вы хотите подклю-
чить меню;
в поле со списком Menu выберите одно из меню созданное вами ранее.

Файл ресурсов
Все ваши действия при создании меню записываются в файле ресурсов (файл с расширением *.гс).
Файл ресурсов недоступен в среде Visual Studio для просмотра и редактирования, но его можно открыть с
помощью блокнота. Вот как выглядит текст в файле ресурсов, описывающий меню:
// Menu
IDR_MAIN_MENU MENU DISCARDABLE
BEGIN
POPUP "Файл"
BEGIN
MENUITEM "Открыть", IDM_OPEN
MENUITEM "Выход", IDM_EXIT
END
END
Первая строка содержит параметр, который определяет способ хранения меню в оперативной памя-
ти. Этот параметр может принимать следующие значения:
DISCARDABLE меню может быть удалено из памяти, если больше не используется;
FIXED меню постоянно находится в памяти;
LOADONCALL меню загружается при обращении к нему;
MOVEABLE меню может перемещаться в другую область памяти;
PRELOAD меню загружается при запуске программы.
Вот текст, описывающий строку в поле Prompt, элемента меню (он также находится в файле ресур-
сов):

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 4
// String Table
STRINGTABLE DISCARDABLE
BEGIN
IDM_OPEN "Открыть файл \nОткрыть файл"
IDM_SAVE "Сохранить файл \nСохранить файл"
IDM_EXIT "Закрыть программу \nЗакрыть программу"
END
Просмотреть и отредактировать значение атрибута меню Prompt можно также в редакторе строковой
таблицы:

Определение идентификаторов IDR_MAIN, IDM_OPEN и IDM_EXIT, находится в файле


resource.h и выглядит так:
#define IDR_MAIN_MENU 130
#define IDM_OPEN 32771
#define IDM_SAVE 32772
#define IDM_EXIT 32773

Создание обработчика событий для элемента меню


Чтобы создать обработчик для элемента меню Open, воспользуемся ClassWizard. Для этого нажмите
Ctrl+W или выберите пункт меню View ► ClassWizard в Visual Studio.
Существует два способа создания обработчика событий: в классе меню и в классе диалогового окна.
Если обработчик создаётся в классе меню, то необходимо создать новый класс. Для этого нужно в
диалоговом окне Adding a Class выбрать Create a new class. В этом случае возникает возможность под-
ключать меню к разным диалоговым окнам с помощью соответствующих команд.
Если в вашей программе предполагается использовать меню только в одном диалоговом окне, то
проще выбрать существующий класс диалогового окна. Для этого нужно в диалоговом окне Adding a
Class выбрать Select an existing class, а в диалоговом окне Select Class – класс диалогового окна к кото-
рому вы хотите подключить меню.

В диалоговом окне MFC ClassWizard откройте вкладку Message Maps.


Далее следует проделать следующие шаги:
из выпадающего списка Class name выберите имя класса диалогового окна, к которому вы хотите
подключить меню, например CMenuDemoDlg;
из списка Object IDs выберите идентификатор пункта меню, для которого вы хотите создать обра-
ботчик, например IDM_FILE;
из списка Messages выберите COMMAND, дважды щелкнув на этой строчке, или, один раз щелкнув
на строке COMMAND, нажмите кнопку Add Function... ;
в появившемся диалоге задайте имя обработчика, если вас не устраивает предложенное по умол-
чанию;
в списке Member functions дважды щелкните на появившейся строчке или один раз щелкните на
ней и нажмите кнопку Edit Code;

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 5
наберите в предложенном месте программы нужный код.

Создание акселераторов (клавиш быстрого вызова)


Для ускорения доступа к элементам меню при помощи клавиатуры, а также для вызова нужных функ-
ций, не связанных с меню, в Windows используется таблица акселераторов. Она находится в ресурсах
приложения и определяет соответствие между акселератором и значением параметра WM_COMMAND,
передающегося в функцию окна при нажатии комбинации клавиш.
Чтобы комбинация клавиш стала работать как акселератор, она должна быть описана в таблице ак-
селераторов и, кроме того, приложение должно загрузить таблицу акселераторов из ресурсов.
Для того, чтобы добавить таблицу акселераторов в проект проделать следующие шаги:
в главном меню Visual Studio выбрать меню Insert ► Resource... ;
в диалоговом окне Insert Resource выберите пункт Accelerator и нажмите кнопку New.

Для ввода нового акселератора дважды щёлкните на пустой строке в таблице акселераторов, для
редактирования – на существующей. Диалоговое окно Accel Properties позволяет сформировать
таблицу акселераторов.
Таблица акселераторов определяется в файле описания ресурсов приложения в следующем виде:
<ID_ACCEL> ACCELERATORS
BEGIN
Key , ID , [Type] , [Modifiers]
END
ID_ACCEL идентификатор таблицы акселераторов;
Key определяет клавишу, которая будет использована для создания акселератора;
ID задает идентификатор команды WM_COMMAND, которая передается в функцию окна
при использовании акселератора;
Туре принимает значение ASCII или VIRTKEY и определяет клавишу с использованием ко-
да ASCII или с использованием кода виртуальной клавиши;
Modifiers если в поле Туре стоит VIRTKEY, то здесь можно указывать параметры Ctrl, Alt или
Shift.

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 6
Создадим акселератор для пункта меню Open. Пусть это будет комбинация клавиш Ctrl+O.

Описание этого акселератора в файле ресурсов выглядит так:


IDR_ACCELTABLE ACCELERATORS DISCARDABLE
BEGIN
"O" , IDM_OPEN , VIRTKEY , CONTROL , NOINVERT
END

Загрузка акселераторов
К счастью, чтобы использовать в программе акселераторы, не требуется почти никаких затрат, по-
скольку поддержка акселераторов встроена в операционную систему. Но, как обычно, не может быть все
столь хорошо. Дело в том, что сначала таблицу акселераторов необходимо загрузить из ресурсов вашей
программы, после чего вызывать функцию TranslateAccelerator в цикле обработки сообщений главного
окна программы. Вот здесь-то и кроется засада, поскольку в модальном диалоге цикл обработки сообще-
ний скрыт где-то в недрах операционной системы и в явном виде недоступен.
Конечно, можно было бы создать немодальный диалог, но это потребует дополнительных усилий про-
граммиста – например, в случае вложенных немодальных диалогов придется изрядно попотеть, стараясь
не допустить переключения между диалогом-родителем и диалогом-потомком. К счастью, программисты
из одной чрезвычайно крупной компании при разработке MFC поступили весьма интересно, поскольку в
MFC нет модальных диалогов в чистом виде – при использовании функции DoModal в наследнике CDia-
log создается типичный немодальный диалог с циклом выборки сообщений, а "модальность" такого диало-
га эмулируется программно. Благодаря такому решению у нас есть доступ к циклу сообщений, а для реа-
лизации акселераторов большего нам и не надо.
Перед использованием акселераторов они должны быть загружены в память, для чего используется
функция LoadAccelerators, которая возвращает указатель на загруженную таблицу акселераторов. Указа-
тель необходимо сохранить, поскольку он используется в качестве параметра функции TranslateAccelera-
tor, поэтому в Visual Stidio перейдем на закладку "ClassView", в контекстом меню класса основного диало-
га выбираем пункт меню Add member variable... (добавить переменную-член класса).

Здесь выбран тип доступа Protected в соответствии с правилами хорошего тона C++: не надо давать
доступ к членам класса больше, чем это действительно необходимо, поскольку помогает избавиться от
необоснованной модификации членов класса за его пределами.
Теперь в конструкторе диалога добавляем код загрузки таблицы акселераторов:
CMenuDemoDlg :: CMenuDemoDlg ( CWnd* pParent /*=NULL*/ )
: CDialog ( CMenuDemoDlg :: IDD , pParent )
{
m_hIcon = AfxGetApp ( ) -> LoadIcon ( IDR_MAINFRAME) ;
// загрузки таблицы акселераторов
hAccel = LoadAccelerators ( AfxGetResourceHandle ( ) ,
MAKEINTRESOURCE( IDR_ACCELTABLE ) ) ;
}
Здесь был использован весьма полезный макрос MAKEINTRESOURCE ( ). Дело в том, что боль-
шинство функций Win32, работающих с ресурсами, для ссылок на ресурсы используют их текстовые име-

Выжол Ю.А. Программирование на Visual C++


Лекция 10 Меню 7
на. Однако в обычной практике программирования для ресурсов используют числовые идентификаторы,
поскольку это уменьшает размер программы. Макрос MAKEINTRESOURCE ( ) как раз и позволяет вы-
полнить преобразование числового идентификатора ресурса к символьному указателю.
А теперь нам осталось нанести тот самый завершающий штрих, после которого картина оживет... До-
бавляем в класс диалога виртуальную функцию PreTranslateMessage, которая замечательна тем, что вы-
зывается при поступлении очередных (т.е. из очереди) сообщений, но еще до того, как вызвать стандарт-
ные обработчики.
Процесс добавления функцию PreTranslateMessage показан на рисунке.

Осталось слегка модифицировать тело PreTranslateMessage:


BOOL CMenuDemoDlg :: PreTranslateMessage ( MSG* pMsg )
{
if ( TranslateAccelerator ( m_hWnd , hAccel , pMsg ) ) return TRUE ; // добавлено
return CDialog :: PreTranslateMessage ( pMsg ) ;
}

Выжол Ю.А. Программирование на Visual C++

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