Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Разработка Windows - приложений в среде Borland. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
В файле ресурсов появился новый ресурс – 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 это указатель на главное окно приложения, а As­signMenu – метод, предназначенный для подключения ресурса ме-
ню. В качестве параметра указывается идентификатор нужного ре­сурса. Запустите проект на исполнение:
Доступным оказался только пункт «Выход». Объяснение весьма простое – идентификаторы наших пунктов меню требуется связать с соответствующими функциями обработчиками, аналогично тому, как это делалось для кнопок. Название же идентификатора пункта
«Выход» – 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_DE­LETE) и «Изменить» (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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]