Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка Windows - приложений в среде Borland. Учебное пособие
.pdf
В файле ресурсов появился новый ресурс – MENU, идентификатор нашего меню IDM_MENU1 имеет числовое значение 1, в правой панели отображаются соответствующие инструкции, добавленные в файл ресурсов. Дважды щелкните по строчке IDM_MENU1
(можно воспользоваться пунктом Edit… контекстного меню или основного меню Resource). Вы вернулись в режим редактирования меню. Выберите пункт Popup и в окне
дите в поле Text строку «Файлы». Знак & перед символом «Ф» позволяет вызвать данный пункт с помощью комбинации клавиш
«ALT + Ф». Выберите пункт «Файлы», вызовите контекстное меню
и в нем команду New Popup. В верхней (горизонтальной) строке создаваемого меню появится заготовка следующего выпадающего меню, назовите его «Редактирование», сюда мы поместим
добавления, удаления и редактирования записей. Можно поменять
относительное расположение пунктов меню, выделив нужный пункт
и передвинув его мышкой на нужное место. Добавьте еще одно выпадающее меню – «Операции», предназначенное для выбора различных вариантов поиска и сортировки.
Выберите первый пункт в создаваемом меню «Файлы» и добавьте еще две команды
меню пункт New Menuitem:
. Для этого нужно выбрать в контекстном
свойств (Property Inspector) вве-
команды
Затем выделите третий пункт и добавьте перед ним разделительную линию – New Separator. Сепаратор предназначен для визуального разграничения групп пунктов меню.
Выберите первый пункт меню и в окне Property Inspector в поле
Text введите название пункта – «Открыть». Для удобства дальней-
71

шей работы предлагаемое в поле Id название идентификатора, желательно, поменять на мнемоническое, например CM_OPEN. Флажок
Checked отмечает выбранный пункт галочкой, свойство Enable позволяет сделать пункт недоступным пользователю (Disabled), при
этом пункт меню отображается так же, как и доступный (Enabled).
Если вы хотите подчеркнуть, что данный пункт доступен только из
приложения, выберите для этого свойства
значение Grayed. Возможно, лучше не обращать внимания на свойство Enable при создании меню, а управлять доступом из приложения. Этот вопрос мы
рассмотрим ниже при обработке пункта меню «Сохранить как…».
Варианты выбора свойства Break также обсудим немного позже.
Можно задать для исполняемых пунктов меню (у которых дос-
тупно свойство идентификатор) комбинацию горячих клавиш
. Для
этого выберите в окне свойств Property Inspector вкладку Accelerator,
щелкнуть по кнопке Record и нажать на клавиатуре требуемую комбинацию клавиш, например, нажмите CTRL + O для пункта меню
«Открыть». Если вы воспользовались этой возможностью, стоит поместить подсказку пользователю – измените название пункта с «Открыть» на «Открыть \t Ctrl+O» (\t – табуляция). Аналогично отредактируйте две оставшиеся команды меню
– «Сохранить как…»
(CM_SAVEAS) и «Выход» (CM_EXIT). Идентификатор CM_EXIT
предопределен, выбор соответствующего пункта меню приведет к
завершению работы. Подберите длины названий пунктов так, чтобы
получилось следующее (просто добавьте пробелы):
Если запустить программу на исполнение, то никакого меню не
появится. Меню требуется подсоединить к окну. Откройте файл
workapp.cpp, найдите метод InitMainWindow и в конце функции,
перед инструкцией:
SetMainWindow(frame);
добавьте строчку:
frame->AssignMenu(IDM_MENU1);
72

Здесь frame это указатель на главное окно приложения, а AssignMenu – метод, предназначенный для подключения ресурса ме-
ню. В качестве параметра указывается идентификатор нужного ресурса. Запустите проект на исполнение:
Доступным оказался только пункт «Выход». Объяснение весьма
простое – идентификаторы наших пунктов меню требуется связать с
соответствующими функциями обработчиками, аналогично тому,
как это делалось для кнопок. Название же идентификатора пункта
«Выход» – CM_EXIT было выбрано не случайно. Он уже связан
(разработчиками транслятора) с той же функцией, которая вызывается для кнопок “OK” и кнопки
закрытия окна (как, впрочем, и
идентификаторы кнопок OK и CANCEL). В частности в нашем случае будет вызвана функция CanClose и произведена проверка базы
данных с предложением сохранить, если требуется, результаты редактирования. Кроме того, пока не действуют «горячие клавиши»
для доступного пункта меню «Выход». Соответствующий ресурс
необходимо подключить дополнительно.
Снова откройте файл workapp.cpp,
найдите метод InitMain-
Window и в конце функции, перед инструкцией:
SetMainWindow(frame);
добавьте строчку, подключающую таблицу горячих клавиш (свойство AccelTable):
frame->GetFirstChild()->Attr.AccelTable=IDM_MENU1;
73

Здесь есть одна тонкость: если подключить данный ресурс к
главному окну приложения, то программа не будет реагировать на
наши акселераторы, так как активным является окно клиент (наш
диалог), и сообщения о нажатых клавишах получит именно клиентское окно. Поэтому с помощью метода GetFirstChild мы получаем
указатель на окно диалога (первое дочернее
таблицу «горячих клавиш» именно к нему. Добавлю, что Attr – это
имя структуры, хранящей стили окна, его размеры и положение на
экране, и, в частности, имя нужного нам ресурса – AccelTable. Если
теперь запустить наше приложение, комбинация клавиш «Ctrl+E»
будет вызывать пункт меню «Выход». Нужно обращать внимание,
была ли задана клавиша «е
ные комбинации акселераторов.
В окне ClassExpert выберите в панели «Classes» наш класс диалогового окна TWorkDlgClient, затем в левой панели «Events» найдите идентификатор CM_OPEN пункта меню «Открыть», раскройте
список событий и выберите пункт Command:
» или «Е», то есть «SHIFT + E». Это раз-
окно) и присоединяем
Вызовите для этого пункта контекстное меню и задайте имя
функции обработчика этой команды – CmOpen. При выборе имени
функции используется стандартное соглашение – имена функции и
соответствующего идентификатора образованы теми же символами,
но имя идентификатора записывается в верхнем регистре. Такой
подход упрощает запоминание нужных имен. Вообще говоря, имена
могут быть произвольными, но, естественно
панели окна ClassExpert, как и ранее, появится заготовка требуемого
метода. Сюда нужно поместить код, аналогичный коду функции
BNClickedOpen, вызываемой по нажатию кнопки «Открыть». Можно, конечно, просто скопировать этот код, но, если вы не собирае-
74
, различными. В нижней

тесь удалять кнопку «Открыть», достаточно просто вызвать уже
имеющийся в проекте метод BNClickedOpen:
void TWorkDlgClient::CmOpen()
{
// INSERT>> Your code here.
BNClickedOpen();
}
Запустите проект на исполнение. Пункт меню «Открыть» доступен, выбор этого пункта приводит к запросу имени файла и попытке его чтения. Работает и комбинация клавиш «Ctrl + O». Аналогично создайте функцию обработчик CmSaveAs для пункта меню
«Сохранить как
(или скопируйте нужные инструкции).
Обычно в программах доступность этого пункта зависит от наличия данных, которые можно сохранить. Сделаем так, чтобы, если
в базе данных нет записей (вектор crew пустой), пункт меню «Сохранить как…» был недоступен. Снова найдите в окне ClassExpert
идентификатор CM_OPEN пункта меню «Открыть
пункт Command Enable:
…» и в теле функции вызовите метод BNClickedSave
» и выберите
В контекстном меню выберите пункт «Add handler» задайте имя
функции обработчика команды – CeSaveAs. Будет создана заготовка
нужного метода:
void TWorkDlgClient::CeSaveAsEnable(TCommandEnabler &tce)
{
// INSERT>> Your code here.
}
Класс TCommandEnabler это абстрактный базовый класс, позволяющий управлять доступностью команд. Для команд меню сущест-
75

вует дочерний класс TMenuItemEnabler. Параметр функции tce – экземпляр этого класса, дает ссылку на наш пункт меню. Метод En-
able имеет один параметр типа bool. Вызов метода c параметром
false сделает пункт меню недоступным. Вызов со значением true –
доступным. Добавьте после комментария // INSERT>> Your code
here строчку:
tce.Enable ( ! crew.empty());
и запустите программу. Пункт меню «Сохранить как…» теперь будет
доступен только в том случае, если в контейнере crew имеются
записи, и недоступен, если контейнер пуст.
Выпадающее меню «Редактирование» должно содержать пункты «Добавить» (идентификатор CM_ADD), «Удалить» (CM_DELETE) и «Изменить» (CM_CHANGE). В функциях обработчиках
этих команд также можно просто вызвать уже имеющиеся методы
для соответствующих кнопок.
Меню «Операции» сделаем более сложным. Откройте его в
дакторе ресурсов, откройте меню «Операции», выберите первый
пункт и в контекстном меню команду New Popup. Появится заготовка выпадающего меню. Добавьте еще одно выпадающее меню:
ре-
Попробуем выбрать свойство Break. Если задать его для головного меню равным Bar Break или Menu Bar Break, то меню «Операции» будет отображаться в строке ниже меню «Файлы» и «Редактирование». Если задать это свойство, равным Bar Break, для пункта
Item из меню второго уровня Popup, слева от названия пункта меню
появится вертикальный сепаратор. Если для первого пункта Item
76

задать свойство Bar Break, а для остальных оставить значение No
Break, меню второго уровня будет вертикальным:
если же для всех пунктов Item задать значение Bar Break, меню будет горизонтальным:
Теперь понятно, что задание свойства Break, равным значению
Bar Break, позволяет создать новую колонку в меню. Итак, выберем
вариант вертикального меню. Первое меню второго уровня назовите
«Поиск», второе – «Сортировка». Оба меню должны содержать команды «по имени», «по должности» и «по окладу». В меню «Поиск»
дополнительно включите пункт «по шаблону». Очевидно, что
«Операции» снова дублирует уже реализованные с помощью командных кнопок и переключателей функции. Это делается для того,
чтобы не отвлекаться на вопросы, связанные с реализацией алгоритмов, чтобы сосредоточиться непосредственно на процессе создания многоуровневого меню:
меню
Создайте функции, обработчики новых команд меню. Для команды меню, осуществляющей поиск по шаблону, достаточно вызвать реализованный ранее метод BNClickedPatternSearch. Для остальных функций, если вы повторяли все рассмотренные в предыдущей главе действия, нет полностью подходящих методов. Впрочем, необходимый код несложно получить, скопировав нужные ин-
77

струкции из имеющихся методов. Например, у нас есть функция
BNClickedSearch для поиска по полю, определяемому выбором в
группе переключателей, и содержащая инструкции:
char buff[40];
if (crew.empty()){
MessageBox ("База пуста", "Поиск", MB_OK |
MB_ICONERROR); return;
}
buff[0]='\0';
staff st;
vector <staff>::iterator k;
if ( rbnPay->GetCheck() == BF_CHECKED){ //--
TInputDialog (this, "Поиск", "Введите искомый оклад", //p
buff,sizeof(buff)).Execute(); //p
st = staff (atoi(buff),"",""); //p
k = find ( crew.begin(), crew.end(), st); //p
wsprintf (buff, «Оклад %i\n не найден», st. GetPay()); //p
} //-If ( rbnJob->GetCheck() == BF_CHECKED){ //--
TInputDialog (this, "Поиск", "Введите искомую должность", //j
buff, sizeof(buff)).Execute(); //j
st = staff (0,"",buff); //j
k = find_if (crew.begin(), crew.end(), bind2nd (ptr_fun
(SearchJob), st) ); //j
wsprintf (buff, "Должность \"%s\"\n не найдена", st. job); //j
} //-if( rbnName->GetCheck() == BF_CHECKED){//--
TInputDialog (this, "Поиск", "Введите искомое
buff, sizeof(buff)). Execute(); //n
st = staff(0,buff,""); //n
k = find_if (crew.begin(), crew.end(), bind2nd (ptr_fun
(SearchName), st) ); //n
wsprintf (buff,"Клиент \"%s\"\n не найден", st.name); //n
} //--
if( k == crew.end()){
MessageBox(buff,"Поиск",MB_OK | MB_ICONERROR); return;
78
имя", //n

}
iCur = k - crew.begin();
ShowRecord();
Для функции обработки команды меню поиска «по имени» достаточно скопировать все не помеченные инструкции и инструкции,
отмеченные комментарием «//n», для поиска «по должности» – все
непомеченные и отмеченные «//j», для поиска «по окладу» – все непомеченные и отмеченные «//p». Инструкции, отмеченные комментарием «//--», не понадобятся вовсе.
Можно поступить иначе.
Search осуществит нужный вариант поиска, если выбран нужный
переключатель. Это можно осуществить программно. Правда, здесь
нас ожидает неприятный сюрприз: если просто «выбрать» нужный
переключатель с помощью методов Check или SetCheck, состояние
остальных переключателей не изменится. Для того, чтобы группа
переключателей правильно реагировала на эти методы, необходимо
при их
третий параметр был не нулем, а указателем на объект класса
TGroupBox. Кроме того, нужно задать соответствующие стили этому объекту и самим переключателям. Однако нетрудно обойти эти
проблемы, симулировав сообщение Windows, возникающее при
щелчке по переключателю с помощью метода SendDlgItemMessage
класса TWindow. Этот метод посылает сообщение, указанное в каче
стве второго параметра элементу управления, чей идентификатор
задается в первом параметре. Например, IDC_RADIOBUTTON1 –
это идентификатор переключателя «по имени». Следовательно, инструкция:
пошлет сообщение BM_CLICK, имитирующее щелчок мышкой по
данному переключателю. При этом остальные переключатели группы станут «непомеченными». Соответственно функция, обрабатывающая команду меню сортировки по имени, может содержать сле
дующий код:
создании в конструкторе диалога:
rbnName = new TRadioButton(this, IDC_RADIOBUTTON1, 0);
SendDlgItemMessage ( IDC_RADIOBUTTON1, BM_CLICK);
Очевидно, что функция BNClicked-
-
-
79

SendDlgItemMessage( IDC_RADIOBUTTON1, BM_CLICK);
BNClickedSearch();
Анологично могут выглядеть и функции, вызываемые для команд меню поиска по должностям или окладам сотрудников.
Замечание: В разделах третьей главы «Разработка модального
диалога. Вывод статистических данных. Шрифты. Диаграммы» и
четвертой главы «Флажки и переключатели (радиокнопки)» рассматривается альтернативный способ программной установки состояния группы переключателей с помощью метода CheckRadio-
Button
му соответствующие инструкции приводятся полностью (сравните с
методом BNClickedSort). Сортировка по имени:
if(crew.empty()){
sort(crew.begin(),crew.end(),CompName);
iCur=0; ShowRecord();
Сортировка по должности:
if(crew.empty()){
sort(crew.begin(),crew.end(),CompJob);
iCur=0; ShowRecord();
Сортировка по окладу:
if(crew.empty()){
sort(crew.begin(),crew.end());
iCur=0; ShowRecord();
ранее методом BNClickedSort и программно установить нужный пе-
класса TDialog.
Функции обработки команд меню сортировки невелики, поэто-
MessageBox ("База пуста", "Сортировка", MB_OK |
MB_ICONERROR); return;}
MessageBox ("База пуста", "Сортировка", MB_OK |
MB_ICONERROR); return;}
MessageBox ("База пуста", "Сортировка", MB_OK |
MB_ICONERROR); return;}
Вполне возможно вместо этого воспользоваться реализованным
80
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
